GF_EXPORT Bool gf_mo_get_visual_info(GF_MediaObject *mo, u32 *width, u32 *height, u32 *stride, u32 *pixel_ar, u32 *pixelFormat) { GF_CodecCapability cap; if ((mo->type != GF_MEDIA_OBJECT_VIDEO) && (mo->type!=GF_MEDIA_OBJECT_TEXT)) return 0; if (width) { cap.CapCode = GF_CODEC_WIDTH; gf_codec_get_capability(mo->odm->codec, &cap); *width = cap.cap.valueInt; } if (height) { cap.CapCode = GF_CODEC_HEIGHT; gf_codec_get_capability(mo->odm->codec, &cap); *height = cap.cap.valueInt; } if (mo->type==GF_MEDIA_OBJECT_TEXT) return 1; if (stride) { cap.CapCode = GF_CODEC_STRIDE; gf_codec_get_capability(mo->odm->codec, &cap); *stride = cap.cap.valueInt; } if (pixelFormat) { cap.CapCode = GF_CODEC_PIXEL_FORMAT; gf_codec_get_capability(mo->odm->codec, &cap); *pixelFormat = cap.cap.valueInt; } /*get PAR settings*/ if (pixel_ar) { cap.CapCode = GF_CODEC_PAR; gf_codec_get_capability(mo->odm->codec, &cap); *pixel_ar = cap.cap.valueInt; if (! (*pixel_ar & 0x0000FFFF)) *pixel_ar = 0; if (! (*pixel_ar & 0xFFFF0000)) *pixel_ar = 0; /**/ if (! *pixel_ar) { GF_Channel *ch; GF_NetworkCommand com; com.base.command_type = GF_NET_CHAN_GET_PIXEL_AR; ch = gf_list_get(mo->odm->channels, 0); if (!ch) return 0; com.base.on_channel = ch; com.par.hSpacing = com.par.vSpacing = 0; if (gf_term_service_command(ch->service, &com) == GF_OK) { if ((com.par.hSpacing>65535) || (com.par.vSpacing>65535)) { com.par.hSpacing>>=16; com.par.vSpacing>>=16; } if (com.par.hSpacing|| com.par.vSpacing) *pixel_ar = (com.par.hSpacing<<16) | com.par.vSpacing; } }
GF_Err gf_term_get_service_info(GF_Terminal *term, GF_ObjectManager *odm, NetInfoCommand *netinfo) { GF_Err e; GF_NetworkCommand com; if (!term || !odm || !netinfo || !gf_term_check_odm(term, odm)) return GF_BAD_PARAM; memset(&com, 0, sizeof(GF_NetworkCommand)); com.command_type = GF_NET_SERVICE_INFO; e = gf_term_service_command(odm->net_service, &com); memcpy(netinfo, &com.info, sizeof(NetInfoCommand)); return e; }
Bool gf_term_get_channel_net_info(GF_Terminal *term, GF_ObjectManager *odm, u32 *d_enum, u32 *chid, NetStatCommand *netcom, GF_Err *ret_code) { GF_Channel *ch; GF_NetworkCommand com; if (!term || !odm || !gf_term_check_odm(term, odm)) return 0; if (*d_enum >= gf_list_count(odm->channels)) return 0; ch = (GF_Channel*)gf_list_get(odm->channels, *d_enum); if (!ch) return 0; (*d_enum) ++; if (ch->is_pulling) { (*ret_code) = GF_NOT_SUPPORTED; return 1; } (*chid) = ch->esd->ESID; memset(&com, 0, sizeof(GF_NetworkCommand)); com.base.on_channel = ch; com.command_type = GF_NET_GET_STATS; (*ret_code) = gf_term_service_command(ch->service, &com); memcpy(netcom, &com.net_stats, sizeof(NetStatCommand)); return 1; }
GF_EXPORT Bool gf_mo_get_visual_info(GF_MediaObject *mo, u32 *width, u32 *height, u32 *stride, u32 *pixel_ar, u32 *pixelFormat, Bool *is_flipped) { GF_CodecCapability cap; if ((mo->type != GF_MEDIA_OBJECT_VIDEO) && (mo->type!=GF_MEDIA_OBJECT_TEXT)) return GF_FALSE; if (width) { cap.CapCode = GF_CODEC_WIDTH; gf_codec_get_capability(mo->odm->codec, &cap); *width = cap.cap.valueInt; } if (height) { cap.CapCode = GF_CODEC_HEIGHT; gf_codec_get_capability(mo->odm->codec, &cap); *height = cap.cap.valueInt; } if (mo->type==GF_MEDIA_OBJECT_TEXT) return GF_TRUE; if (is_flipped) { cap.CapCode = GF_CODEC_FLIP; cap.cap.valueInt = 0; gf_codec_get_capability(mo->odm->codec, &cap); *is_flipped = cap.cap.valueInt ? GF_TRUE : GF_FALSE; } if (stride) { cap.CapCode = GF_CODEC_STRIDE; gf_codec_get_capability(mo->odm->codec, &cap); *stride = cap.cap.valueInt; } if (pixelFormat) { cap.CapCode = GF_CODEC_PIXEL_FORMAT; gf_codec_get_capability(mo->odm->codec, &cap); *pixelFormat = cap.cap.valueInt; if (mo->odm && mo->odm->parentscene->is_dynamic_scene) { #ifndef GPAC_DISABLE_VRML const char *name = gf_node_get_name(gf_event_target_get_node(gf_mo_event_target_get(mo, 0))); if (name && !strcmp(name, "DYN_VIDEO")) { const char *opt; u32 r, g, b, a; M_Background2D *back = (M_Background2D *) gf_sg_find_node_by_name(mo->odm->parentscene->graph, "DYN_BACK"); if (back) { switch (cap.cap.valueInt) { case GF_PIXEL_ARGB: case GF_PIXEL_RGBA: case GF_PIXEL_YUVA: opt = gf_cfg_get_key(mo->odm->term->user->config, "Compositor", "BackColor"); if (!opt) { gf_cfg_set_key(mo->odm->term->user->config, "Compositor", "BackColor", "FF999999"); opt = "FF999999"; } sscanf(opt, "%02X%02X%02X%02X", &a, &r, &g, &b); back->backColor.red = INT2FIX(r)/255; back->backColor.green = INT2FIX(g)/255; back->backColor.blue = INT2FIX(b)/255; break; default: back->backColor.red = back->backColor.green = back->backColor.blue = 0; break; } gf_node_dirty_set((GF_Node *)back, 0, GF_TRUE); } } #endif } } /*get PAR settings*/ if (pixel_ar) { cap.CapCode = GF_CODEC_PAR; gf_codec_get_capability(mo->odm->codec, &cap); *pixel_ar = cap.cap.valueInt; if (! (*pixel_ar & 0x0000FFFF)) *pixel_ar = 0; if (! (*pixel_ar & 0xFFFF0000)) *pixel_ar = 0; /**/ if (! *pixel_ar) { GF_Channel *ch; GF_NetworkCommand com; com.base.command_type = GF_NET_CHAN_GET_PIXEL_AR; ch = (GF_Channel *)gf_list_get(mo->odm->channels, 0); if (!ch) return GF_FALSE; com.base.on_channel = ch; com.par.hSpacing = com.par.vSpacing = 0; if (gf_term_service_command(ch->service, &com) == GF_OK) { if ((com.par.hSpacing>65535) || (com.par.vSpacing>65535)) { com.par.hSpacing>>=16; com.par.vSpacing>>=16; } if (com.par.hSpacing|| com.par.vSpacing) *pixel_ar = (com.par.hSpacing<<16) | com.par.vSpacing; } }
/*performs final setup upon connection confirm*/ void gf_es_on_connect(GF_Channel *ch) { Bool can_buffer; GF_NetworkCommand com; /*check whether we can work in pull mode or not*/ can_buffer = 1; /*if local interaction streams no buffer nor pull*/ if ((ch->esd->decoderConfig->streamType == GF_STREAM_INTERACT) && !ch->esd->URLString) can_buffer = 0; com.base.on_channel = ch; ch->is_pulling = 0; if (can_buffer) { /*request padding*/ com.command_type = GF_NET_CHAN_SET_PADDING; com.pad.padding_bytes = ch->media_padding_bytes; if (!com.pad.padding_bytes || (gf_term_service_command(ch->service, &com) == GF_OK)) { /*request pull if possible*/ if (ch->service->ifce->ChannelGetSLP && ch->service->ifce->ChannelReleaseSLP) { com.command_type = GF_NET_CHAN_SET_PULL; if (gf_term_service_command(ch->service, &com) == GF_OK) { ch->is_pulling = 1; can_buffer = 0; } } } } /*checks whether the stream is interactive or not*/ com.command_type = GF_NET_CHAN_INTERACTIVE; if (gf_term_service_command(ch->service, &com)!=GF_OK) { ch->clock->no_time_ctrl = 1; ch->odm->flags |= GF_ODM_NO_TIME_CTRL; refresh_non_interactive_clocks(ch->odm); } /*signal channel state*/ if (ch->es_state == GF_ESM_ES_WAIT_FOR_ACK) ch->es_state = GF_ESM_ES_CONNECTED; /*signal only once connected to prevent PLAY trigger on connection callback*/ ch->odm->pending_channels--; /*remember channels connected on service*/ if (ch->esd->URLString) ch->service->nb_ch_users++; /*turn off buffering for JPEG and PNG*/ switch (ch->esd->decoderConfig->objectTypeIndication) { case GPAC_OTI_IMAGE_JPEG: case GPAC_OTI_IMAGE_PNG: can_buffer = 0; break; } /*buffer setup*/ ch->MinBuffer = ch->MaxBuffer = 0; if (can_buffer) { const char *sOpt; com.command_type = GF_NET_CHAN_BUFFER; com.base.on_channel = ch; /*set default values*/ com.buffer.max = 1000; sOpt = gf_cfg_get_key(ch->odm->term->user->config, "Network", "BufferLength"); if (sOpt) com.buffer.max = atoi(sOpt); com.buffer.min = 0; sOpt = gf_cfg_get_key(ch->odm->term->user->config, "Network", "RebufferLength"); if (sOpt) com.buffer.min = atoi(sOpt); if (gf_term_service_command(ch->service, &com) == GF_OK) { ch->MinBuffer = com.buffer.min; ch->MaxBuffer = com.buffer.max; } } if (ch->esd->decoderConfig->streamType == GF_STREAM_PRIVATE_SCENE && ch->esd->decoderConfig->objectTypeIndication == GPAC_OTI_PRIVATE_SCENE_EPG) { ch->bypass_sl_and_db = 1; } if (ch->clock->no_time_ctrl) { switch (ch->esd->decoderConfig->streamType) { case GF_STREAM_AUDIO: case GF_STREAM_VISUAL: break; default: ch->dispatch_after_db = 1; break; } } /*get duration*/ com.command_type = GF_NET_CHAN_DURATION; com.base.on_channel = ch; if (gf_term_service_command(ch->service, &com) == GF_OK) { if (com.duration.duration>=0) gf_odm_set_duration(ch->odm, ch, (u64) (1000*com.duration.duration)); } }
GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch) { GF_Err e; GF_NetworkCommand com; GF_Channel *a_ch; char *dsi; u32 dsiSize, CUsize, i; GF_CodecCapability cap; u32 min, max; /*only for valid codecs (eg not OCR)*/ if (codec->decio) { com.get_dsi.dsi = NULL; dsi = NULL; dsiSize = 0; if (ch->esd->decoderConfig->upstream) codec->flags |= GF_ESM_CODEC_HAS_UPSTREAM; if (ch->esd->decoderConfig->decoderSpecificInfo) { dsi = ch->esd->decoderConfig->decoderSpecificInfo->data; dsiSize = ch->esd->decoderConfig->decoderSpecificInfo->dataLength; } /*For objects declared in OD stream, override with network DSI if any*/ if (ch->service && !(ch->odm->flags & GF_ODM_NOT_IN_OD_STREAM) ) { com.command_type = GF_NET_CHAN_GET_DSI; com.base.on_channel = ch; e = gf_term_service_command(ch->service, &com); if (!e && com.get_dsi.dsi) { dsi = com.get_dsi.dsi; dsiSize = com.get_dsi.dsi_len; if (ch->esd->decoderConfig->decoderSpecificInfo->data) gf_free(ch->esd->decoderConfig->decoderSpecificInfo->data); ch->esd->decoderConfig->decoderSpecificInfo->data = com.get_dsi.dsi; ch->esd->decoderConfig->decoderSpecificInfo->dataLength = com.get_dsi.dsi_len; } } GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[Codec] Attaching stream %d to codec %s\n", ch->esd->ESID, codec->decio->module_name)); /*lock the channel before setup in case we are using direct_decode */ gf_mx_p(ch->mx); e = codec->decio->AttachStream(codec->decio, ch->esd); gf_mx_v(ch->mx); if (ch->esd->decoderConfig && ch->esd->decoderConfig->rvc_config) { gf_odf_desc_del((GF_Descriptor *)ch->esd->decoderConfig->rvc_config); ch->esd->decoderConfig->rvc_config = NULL; } if (e) { GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[Codec] Attach Stream failed %s\n", gf_error_to_string(e) )); return e; } /*ask codec for desired output capacity - note this may be 0 if stream is not yet configured*/ cap.CapCode = GF_CODEC_OUTPUT_SIZE; gf_codec_get_capability(codec, &cap); if (codec->CB && (cap.cap.valueInt != codec->CB->UnitSize)) { gf_cm_del(codec->CB); codec->CB = NULL; } CUsize = cap.cap.valueInt; /*get desired amount of units and minimal fullness (used for scheduling)*/ switch(codec->type) { case GF_STREAM_VISUAL: case GF_STREAM_AUDIO: cap.CapCode = GF_CODEC_BUFFER_MIN; gf_codec_get_capability(codec, &cap); min = cap.cap.valueInt; cap.CapCode = GF_CODEC_BUFFER_MAX; gf_codec_get_capability(codec, &cap); max = cap.cap.valueInt; break; case GF_STREAM_ND_SUBPIC: max = 1; min = 0; break; default: min = max = 0; } if ((codec->type==GF_STREAM_AUDIO) && (max<2)) max = 2; /*setup CB*/ if (!codec->CB && max) { if (codec->flags & GF_ESM_CODEC_IS_RAW_MEDIA) { max = 1; /*create a semaphore in non-notified stage*/ codec->odm->raw_frame_sema = gf_sema_new(1, 0); } GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[ODM] Creating composition buffer for codec %s - %d units %d bytes each\n", codec->decio->module_name, max, CUsize)); codec->CB = gf_cm_new(CUsize, max, (codec->flags & GF_ESM_CODEC_IS_RAW_MEDIA) ? 1 : 0); codec->CB->Min = min; codec->CB->odm = codec->odm; } if (codec->CB) { /*check re-ordering - set by default on all codecs*/ codec->is_reordering = 1; cap.CapCode = GF_CODEC_REORDER; if (gf_codec_get_capability(codec, &cap) == GF_OK) codec->is_reordering = cap.cap.valueInt; } if (codec->flags & GF_ESM_CODEC_IS_RAW_MEDIA) { ch->is_raw_channel = 1; } /*setup net channel config*/ if (ch->service) { memset(&com, 0, sizeof(GF_NetworkCommand)); com.command_type = GF_NET_CHAN_CONFIG; com.base.on_channel = ch; com.cfg.priority = ch->esd->streamPriority; com.cfg.sync_id = ch->clock->clockID; memcpy(&com.cfg.sl_config, ch->esd->slConfig, sizeof(GF_SLConfig)); /*get the frame duration if audio (used by some network stack)*/ if (ch->odm->codec && (ch->odm->codec->type==GF_STREAM_AUDIO) ) { cap.CapCode = GF_CODEC_SAMPLERATE; gf_codec_get_capability(ch->odm->codec, &cap); com.cfg.sample_rate = cap.cap.valueInt; cap.CapCode = GF_CODEC_CU_DURATION; gf_codec_get_capability(ch->odm->codec, &cap); com.cfg.frame_duration = cap.cap.valueInt; } gf_term_service_command(ch->service, &com); ch->carousel_type = GF_ESM_CAROUSEL_NONE; if (com.cfg.use_m2ts_sections) { ch->carousel_type = GF_ESM_CAROUSEL_MPEG2; } else { switch (ch->esd->decoderConfig->streamType) { case GF_STREAM_OD: case GF_STREAM_SCENE: ch->carousel_type = ch->esd->slConfig->AUSeqNumLength ? GF_ESM_CAROUSEL_MPEG4 : GF_ESM_CAROUSEL_NONE; break; } } } } /*assign the first base layer as the codec clock by default, or current channel clock if no clock set Also assign codec priority here*/ if (!ch->esd->dependsOnESID || !codec->ck) { codec->ck = ch->clock; codec->Priority = ch->esd->streamPriority; /*insert base layer first - note we are sure this is a stream of the same type as the codec (other streams - OCI, MPEG7, MPEGJ - are not added that way)*/ return gf_list_insert(codec->inChannels, ch, 0); } else { /*make sure all channels are in order*/ i=0; while ((a_ch = (GF_Channel*)gf_list_enum(codec->inChannels, &i))) { if (ch->esd->dependsOnESID == a_ch->esd->ESID) { return gf_list_insert(codec->inChannels, ch, i); } if (a_ch->esd->dependsOnESID == ch->esd->ESID) { return gf_list_insert(codec->inChannels, ch, i-1); } } /*by default append*/ return gf_list_add(codec->inChannels, ch); } }