//extraction of the ESD from the track GF_Err GetESD(GF_MovieBox *moov, u32 trackID, u32 StreamDescIndex, GF_ESD **outESD) { GF_Err e; GF_ESD *esd; u32 track_num = 0; GF_SampleTableBox *stbl; GF_TrackBox *trak, *OCRTrack; GF_TrackReferenceTypeBox *dpnd; GF_SLConfig *slc; GF_MPEGSampleEntryBox *entry; track_num = gf_isom_get_tracknum_from_id(moov, trackID); dpnd = NULL; *outESD = NULL; trak = gf_isom_get_track(moov, track_num); if (!trak) return GF_ISOM_INVALID_FILE; e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0); if (e) return e; e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL); if (e) return e; //set the ID esd->ESID = trackID; //find stream dependencies e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_DPND , &dpnd); if (e) return e; if (dpnd) { //ONLY ONE STREAM DEPENDENCY IS ALLOWED if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; //fix the spec: where is the index located ?? esd->dependsOnESID = dpnd->trackIDs[0]; } else { esd->dependsOnESID = 0; } if (trak->udta) { GF_UserDataMap *map; u32 i = 0; while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) { if (map->boxType == GF_4CC('A','U','X','V')) { GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA); gf_list_add(esd->extensionDescriptors, d); break; } } } //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference dpnd = NULL; OCRTrack = NULL; //find OCR dependencies e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd); if (e) return e; if (dpnd) { if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; esd->OCRESID = dpnd->trackIDs[0]; OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); while (OCRTrack) { /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/ e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd); if (e || !dpnd || !dpnd->trackIDCount) { OCRTrack = NULL; goto default_sync; } /*this is explicit desync*/ if (dpnd && ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))) break; /*loop in OCRs, break it*/ if (esd->ESID == OCRTrack->Header->trackID) { OCRTrack = NULL; goto default_sync; } /*check next*/ OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); } if (!OCRTrack) goto default_sync; } else { default_sync: /*all tracks are sync'ed by default*/ if (trak->moov->mov->es_id_default_sync<0) { if (esd->OCRESID) trak->moov->mov->es_id_default_sync = esd->OCRESID; else trak->moov->mov->es_id_default_sync = esd->ESID; } if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync; /*cf ESD writer*/ if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; } //update the IPI stuff if needed if (esd->ipiPtr != NULL) { dpnd = NULL; e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd); if (e) return e; if (dpnd) { if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE; //OK, retrieve the ID: the IPI_ES_Id is currently the ref track esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1]; //and change the tag esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG; } else { return GF_ISOM_INVALID_FILE; } } if ((trak->Media->mediaHeader->packedLanguage[0] != 'u') || (trak->Media->mediaHeader->packedLanguage[1] != 'n') || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) { if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG); esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0]; esd->langDesc->langCode <<= 8; esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1]; esd->langDesc->langCode <<= 8; esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2]; } { u16 rvc_predefined; char *rvc_cfg_data; const char *mime_type; u32 rvc_cfg_size; e = gf_isom_get_rvc_config(moov->mov, track_num, 1, &rvc_predefined, &rvc_cfg_data, &rvc_cfg_size, &mime_type); if (e==GF_OK) { if (rvc_predefined) { esd->decoderConfig->predefined_rvc_config = rvc_predefined; } else { esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) { #if !defined(GPAC_DISABLE_CORE_TOOLS) && !defined(GPAC_DISABLE_ZLIB) gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength); gf_free(rvc_cfg_data); #endif } else { esd->decoderConfig->rvc_config->data = rvc_cfg_data; esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size; } } } } /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips) so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/ if (esd->URLString != NULL) { *outESD = esd; return GF_OK; } //if we are in publishing mode and we have an SLConfig specified, use it as is switch (entry->type) { case GF_ISOM_BOX_TYPE_MP4V: slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; break; case GF_ISOM_BOX_TYPE_MP4A: slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; break; case GF_ISOM_BOX_TYPE_MP4S: slc = entry->slc; break; default: slc = NULL; break; } if (slc) { gf_odf_desc_del((GF_Descriptor *)esd->slConfig); gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig); *outESD = esd; return GF_OK; } //otherwise use the regular mapping //this is a desc for a media in the file, let's rewrite some param esd->slConfig->timestampLength = 32; esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale; //NO OCR from MP4File streams (eg, constant OC Res one) esd->slConfig->OCRLength = 0; esd->slConfig->OCRResolution = 0; // if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale; stbl = trak->Media->information->sampleTable; // a little optimization here: if all our samples are sync, //set the RAPOnly to true... for external users... if (! stbl->SyncSample) { esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; esd->slConfig->useRandomAccessPointFlag = 0; } else { esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0; //signal we are NOT using sync points if no info is present in the table esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0; } //do we have DegradationPriority ? if (stbl->DegradationPriority) { esd->slConfig->degradationPriorityLength = 15; } else { esd->slConfig->degradationPriorityLength = 0; } //paddingBits if (stbl->PaddingBits) { esd->slConfig->usePaddingFlag = 1; } //change to support reflecting OD streams esd->slConfig->useAccessUnitEndFlag = 1; esd->slConfig->useAccessUnitStartFlag = 1; //signal we do have padding flag (since we only use logical SL packet //the user can decide whether to use the info or not esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0; //same with degradation priority esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0; //this new SL will be OUT OF THE FILE. Let's set its predefined to 0 esd->slConfig->predefined = 0; *outESD = esd; return GF_OK; }
RTPStream *RP_NewStream(RTPClient *rtp, GF_SDPMedia *media, GF_SDPInfo *sdp, RTPStream *input_stream) { GF_RTSPRange *range; RTPStream *tmp; GF_RTPMap *map; u32 i, ESID, ODID, ssrc, rtp_seq, rtp_time; Bool force_bcast = 0; Double Start, End; Float CurrentTime; u16 rvc_predef = 0; char *rvc_config_att = NULL; u32 s_port_first, s_port_last; GF_X_Attribute *att; Bool is_migration = 0; char *ctrl; GF_SDPConnection *conn; GF_RTSPTransport trans; u32 mid, prev_stream, base_stream; //extract all relevant info from the GF_SDPMedia Start = 0.0; End = -1.0; CurrentTime = 0.0f; ODID = 0; ESID = 0; ctrl = NULL; range = NULL; s_port_first = s_port_last = 0; ssrc = rtp_seq = rtp_time = 0; mid = prev_stream = base_stream = 0; i=0; while ((att = (GF_X_Attribute*)gf_list_enum(media->Attributes, &i))) { if (!stricmp(att->Name, "control")) ctrl = att->Value; else if (!stricmp(att->Name, "gpac-broadcast")) force_bcast = 1; else if (!stricmp(att->Name, "mpeg4-esid") && att->Value) ESID = atoi(att->Value); else if (!stricmp(att->Name, "mpeg4-odid") && att->Value) ODID = atoi(att->Value); else if (!stricmp(att->Name, "range") && !range) range = gf_rtsp_range_parse(att->Value); else if (!stricmp(att->Name, "x-stream-state") ) { sscanf(att->Value, "server-port=%u-%u;ssrc=%X;npt=%g;seq=%u;rtptime=%u", &s_port_first, &s_port_last, &ssrc, &CurrentTime, &rtp_seq, &rtp_time); is_migration = 1; } else if (!stricmp(att->Name, "x-server-port") ) { sscanf(att->Value, "%u-%u", &s_port_first, &s_port_last); } else if (!stricmp(att->Name, "rvc-config-predef")) { rvc_predef = atoi(att->Value); } else if (!stricmp(att->Name, "rvc-config")) { rvc_config_att = att->Value; } else if (!stricmp(att->Name, "mid")) { sscanf(att->Value, "L%d", &mid); } else if (!stricmp(att->Name, "depend")) { char buf[3000]; memset(buf, 0, 3000); sscanf(att->Value, "%*d lay L%d %*s %s", &base_stream, buf); if (!strlen(buf)) sscanf(att->Value, "%*d lay %s", buf); sscanf(buf, "L%d", &prev_stream); } } if (range) { Start = range->start; End = range->end; gf_rtsp_range_del(range); } /*check connection*/ conn = sdp->c_connection; if (conn && (!conn->host || !strcmp(conn->host, "0.0.0.0"))) conn = NULL; if (!conn) conn = (GF_SDPConnection*)gf_list_get(media->Connections, 0); if (conn && (!conn->host || !strcmp(conn->host, "0.0.0.0"))) conn = NULL; if (!conn) { /*RTSP RFC recommends an empty "c= " line but some server don't send it. Use session info (o=)*/ if (!sdp->o_net_type || !sdp->o_add_type || strcmp(sdp->o_net_type, "IN")) return NULL; if (strcmp(sdp->o_add_type, "IP4") && strcmp(sdp->o_add_type, "IP6")) return NULL; } else { if (strcmp(conn->net_type, "IN")) return NULL; if (strcmp(conn->add_type, "IP4") && strcmp(conn->add_type, "IP6")) return NULL; } /*do we support transport*/ if (strcmp(media->Profile, "RTP/AVP") && strcmp(media->Profile, "RTP/AVP/TCP") && strcmp(media->Profile, "RTP/SAVP") && strcmp(media->Profile, "RTP/SAVP/TCP") ) return NULL; /*check RTP map. For now we only support 1 RTPMap*/ if (media->fmt_list || (gf_list_count(media->RTPMaps) > 1)) return NULL; /*check payload type*/ map = (GF_RTPMap*)gf_list_get(media->RTPMaps, 0); /*this is an ESD-URL setup, we likely have namespace conflicts so overwrite given ES_ID by the app one (client side), but keep control (server side) if provided*/ if (input_stream) { ESID = input_stream->ES_ID; if (!ctrl) ctrl = input_stream->control; tmp = input_stream; } else { tmp = RP_FindChannel(rtp, NULL, ESID, NULL, 0); if (tmp) return NULL; GF_SAFEALLOC(tmp, RTPStream); tmp->owner = rtp; } /*create an RTP channel*/ tmp->rtp_ch = gf_rtp_new(); if (ctrl) tmp->control = gf_strdup(ctrl); tmp->ES_ID = ESID; tmp->OD_ID = ODID; tmp->mid = mid; tmp->prev_stream = prev_stream; tmp->base_stream = base_stream; memset(&trans, 0, sizeof(GF_RTSPTransport)); trans.Profile = media->Profile; trans.source = conn ? conn->host : sdp->o_address; trans.IsUnicast = gf_sk_is_multicast_address(trans.source) ? 0 : 1; if (!trans.IsUnicast) { trans.port_first = media->PortNumber; trans.port_last = media->PortNumber + 1; trans.TTL = conn ? conn->TTL : 0; } else { trans.client_port_first = media->PortNumber; trans.client_port_last = media->PortNumber + 1; trans.port_first = s_port_first ? s_port_first : trans.client_port_first; trans.port_last = s_port_last ? s_port_last : trans.client_port_last; } if (gf_rtp_setup_transport(tmp->rtp_ch, &trans, NULL) != GF_OK) { RP_DeleteStream(tmp); return NULL; } /*setup depacketizer*/ tmp->depacketizer = gf_rtp_depacketizer_new(media, rtp_sl_packet_cbk, tmp); if (!tmp->depacketizer) { RP_DeleteStream(tmp); return NULL; } /*setup channel*/ gf_rtp_setup_payload(tmp->rtp_ch, map); // tmp->status = NM_Disconnected; ctrl = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Streaming", "DisableRTCP"); if (!ctrl || stricmp(ctrl, "yes")) tmp->flags |= RTP_ENABLE_RTCP; /*setup NAT keep-alive*/ ctrl = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Streaming", "NATKeepAlive"); if (ctrl) gf_rtp_enable_nat_keepalive(tmp->rtp_ch, atoi(ctrl)); tmp->range_start = Start; tmp->range_end = End; if (End != -1.0) tmp->flags |= RTP_HAS_RANGE; if (force_bcast) tmp->flags |= RTP_FORCE_BROADCAST; if (is_migration) { tmp->current_start = (Double) CurrentTime; tmp->check_rtp_time = RTP_SET_TIME_RTP; gf_rtp_set_info_rtp(tmp->rtp_ch, rtp_seq, rtp_time, ssrc); tmp->status = RTP_SessionResume; } if (rvc_predef) { tmp->depacketizer->sl_map.rvc_predef = rvc_predef ; } else if (rvc_config_att) { char *rvc_data=NULL; u32 rvc_size; Bool is_gz = 0; if (!strncmp(rvc_config_att, "data:application/rvc-config+xml", 32) && strstr(rvc_config_att, "base64") ) { char *data = strchr(rvc_config_att, ','); if (data) { rvc_size = (u32) strlen(data) * 3 / 4 + 1; rvc_data = gf_malloc(sizeof(char) * rvc_size); rvc_size = gf_base64_decode(data, (u32) strlen(data), rvc_data, rvc_size); rvc_data[rvc_size] = 0; } if (!strncmp(rvc_config_att, "data:application/rvc-config+xml+gz", 35)) is_gz = 1; } else if (!strnicmp(rvc_config_att, "http://", 7) || !strnicmp(rvc_config_att, "https://", 8) ) { char *mime; if (gf_dm_get_file_memory(rvc_config_att, &rvc_data, &rvc_size, &mime) == GF_OK) { if (mime && strstr(mime, "+gz")) is_gz = 1; if (mime) gf_free(mime); } } if (rvc_data) { if (is_gz) { #ifdef GPAC_DISABLE_ZLIB fprintf(stderr, "Error: no zlib support - RVC not supported in RTP\n"); return NULL; #endif gf_gz_decompress_payload(rvc_data, rvc_size, &tmp->depacketizer->sl_map.rvc_config, &tmp->depacketizer->sl_map.rvc_config_size); gf_free(rvc_data); } else { tmp->depacketizer->sl_map.rvc_config = rvc_data; tmp->depacketizer->sl_map.rvc_config_size = rvc_size; } } } return tmp; }