GF_Err adobe_gen_multirate_manifest(AdobeMultirate* am, char *bootstrap, size_t bootstrap_size) { GF_Err e; u32 i; #ifdef ADOBE_INLINED_BOOTSTRAP char bootstrap64[GF_MAX_PATH]; u32 bootstrap64_len; #endif fprintf(am->f, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); fprintf(am->f, "<manifest xmlns=\"http://ns.adobe.com/f4m/2.0\">\n"); fprintf(am->f, "<id>%s</id>\n", am->id); fprintf(am->f, "<baseURL>%s</baseURL>\n", am->base_url); fprintf(am->f, "<streamType>live</streamType>\n"); assert(am->streams); for (i=0; i<gf_list_count(am->streams); i++) { AdobeStream *as = gf_list_get(am->streams, i); assert(as); #ifdef ADOBE_INLINED_BOOTSTRAP fprintf(am->f, "<bootstrapInfo profile=\"named\" id=\"boot_%s_%d\">\n", as->id, as->bitrate); bootstrap64_len = gf_base64_encode(bootstrap, bootstrap_size, bootstrap64, GF_MAX_PATH); fwrite(bootstrap64, bootstrap64_len, 1, am->f); if (bootstrap64_len >= GF_MAX_PATH) { fprintf(stderr, "Bootstrap may have been truncated for stream %s_%d.\n", as->id, as->bitrate); assert(0); } fprintf(am->f, "\n</bootstrapInfo>\n"); #else { char filename[GF_MAX_PATH]; FILE *bstfile; sprintf(filename, "%s_%d.bootstrap", as->id, as->bitrate); bstfile = fopen(filename, "wb"); gf_fwrite(bootstrap, bootstrap_size, 1, bstfile); fclose(bstfile); } #endif e = adobe_gen_stream_manifest(as); if (!e) { if (!am->base_url && !as->base_url) fprintf(stderr, "Warning: no base_url specified\n"); fprintf(am->f, "<media href=\"%s_%s_%d.f4m\" bitrate=\"%d\"/>\n", am->id, as->id, as->bitrate, as->bitrate); } } fprintf(am->f, "</manifest>\n"); return GF_OK; }
void gf_media_format_ttxt_sdp(GP_RTPPacketizer *builder, char *payload_name, char *sdpLine, GF_ISOFile *file, u32 track) { char buffer[2000]; u32 w, h, i, m_w, m_h; s32 tx, ty; s16 l; sprintf(sdpLine, "a=fmtp:%d sver=60; ", builder->PayloadType); gf_isom_get_track_layout_info(file, track, &w, &h, &tx, &ty, &l); sprintf(buffer, "width=%d; height=%d; tx=%d; ty=%d; layer=%d; ", w, h, tx, ty, l); strcat(sdpLine, buffer); m_w = w; m_h = h; for (i=0; i<gf_isom_get_track_count(file); i++) { switch (gf_isom_get_media_type(file, i+1)) { case GF_ISOM_MEDIA_SCENE: case GF_ISOM_MEDIA_VISUAL: gf_isom_get_track_layout_info(file, i+1, &w, &h, &tx, &ty, &l); if (w>m_w) m_w = w; if (h>m_h) m_h = h; break; default: break; } } sprintf(buffer, "max-w=%d; max-h=%d", m_w, m_h); strcat(sdpLine, buffer); strcat(sdpLine, "; tx3g="); for (i=0; i<gf_isom_get_sample_description_count(file, track); i++) { char *tx3g; u32 tx3g_len, len; gf_isom_text_get_encoded_tx3g(file, track, i+1, GF_RTP_TX3G_SIDX_OFFSET, &tx3g, &tx3g_len); len = gf_base64_encode(tx3g, tx3g_len, buffer, 2000); gf_free(tx3g); buffer[len] = 0; if (i) strcat(sdpLine, ", "); strcat(sdpLine, buffer); } }
GF_EXPORT Bool gf_hinter_can_embbed_data(char *data, u32 data_size, u32 streamType) { char data64[5000]; u32 size64; size64 = gf_base64_encode(data, data_size, data64, 5000); if (!size64) return 0; switch (streamType) { case GF_STREAM_OD: size64 += strlen("data:application/mpeg4-od-au;base64,"); break; case GF_STREAM_SCENE: size64 += strlen("data:application/mpeg4-bifs-au;base64,"); break; default: /*NOT NORMATIVE*/ size64 += strlen("data:application/mpeg4-es-au;base64,"); break; } if (size64>=255) return 0; return 1; }
GF_EXPORT char *gf_seng_get_base64_iod(GF_SceneEngine *seng) { u32 size, size64; char *buffer, *buf64; u32 i=0; GF_StreamContext*sc = NULL; if (!seng->ctx->root_od) return NULL; while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) { if ((sc->streamType == GF_STREAM_SCENE) && (sc->objectType != GPAC_OTI_SCENE_DIMS)) break; } if (!sc) return NULL; size = 0; gf_odf_desc_write((GF_Descriptor *) seng->ctx->root_od, &buffer, &size); buf64 = gf_malloc(size*2); size64 = gf_base64_encode( buffer, size, buf64, size*2); buf64[size64] = 0; gf_free(buffer); return buf64; }
GF_EXPORT GF_Err gf_rtp_streamer_append_sdp_extended(GF_RTPStreamer *rtp, u16 ESID, char *dsi, u32 dsi_len, GF_ISOFile *isofile, u32 isotrack, char *KMS_URI, u32 width, u32 height, char **out_sdp_buffer) { u32 size; u16 port; char mediaName[30], payloadName[30]; char sdp[20000], sdpLine[10000]; if (!out_sdp_buffer) return GF_BAD_PARAM; gf_rtp_builder_get_payload_name(rtp->packetizer, payloadName, mediaName); gf_rtp_get_ports(rtp->channel, &port, NULL); sprintf(sdp, "m=%s %d RTP/%s %d\n", mediaName, port, rtp->packetizer->slMap.IV_length ? "SAVP" : "AVP", rtp->packetizer->PayloadType); sprintf(sdpLine, "a=rtpmap:%d %s/%d\n", rtp->packetizer->PayloadType, payloadName, rtp->packetizer->sl_config.timestampResolution); strcat(sdp, sdpLine); if (ESID && (rtp->packetizer->rtp_payt != GF_RTP_PAYT_3GPP_DIMS)) { sprintf(sdpLine, "a=mpeg4-esid:%d\n", ESID); strcat(sdp, sdpLine); } if (width && height) { if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_H263) { sprintf(sdpLine, "a=cliprect:0,0,%d,%d\n", height, width); strcat(sdp, sdpLine); } /*extensions for some mobile phones*/ sprintf(sdpLine, "a=framesize:%d %d-%d\n", rtp->packetizer->PayloadType, width, height); strcat(sdp, sdpLine); } strcpy(sdpLine, ""); /*AMR*/ if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_AMR) || (rtp->packetizer->rtp_payt == GF_RTP_PAYT_AMR_WB)) { sprintf(sdpLine, "a=fmtp:%d octet-align=1\n", rtp->packetizer->PayloadType); } /*Text*/ else if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_3GPP_TEXT) { gf_media_format_ttxt_sdp(rtp->packetizer, payloadName, sdpLine, isofile, isotrack); strcat(sdpLine, "\n"); } /*EVRC/SMV in non header-free mode*/ else if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_EVRC_SMV) && (rtp->packetizer->auh_size>1)) { sprintf(sdpLine, "a=fmtp:%d maxptime=%d\n", rtp->packetizer->PayloadType, rtp->packetizer->auh_size*20); } /*H264/AVC*/ else if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_H264_AVC) || (rtp->packetizer->rtp_payt == GF_RTP_PAYT_H264_SVC)) { GF_AVCConfig *avcc = dsi ? gf_odf_avc_cfg_read(dsi, dsi_len) : NULL; if (avcc) { sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", rtp->packetizer->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication); if (gf_list_count(avcc->pictureParameterSets) || gf_list_count(avcc->sequenceParameterSets)) { u32 i, count, b64s; char b64[200]; strcat(sdpLine, "; sprop-parameter-sets="); count = gf_list_count(avcc->sequenceParameterSets); for (i=0; i<count; i++) { GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(avcc->sequenceParameterSets, i); b64s = gf_base64_encode(sl->data, sl->size, b64, 200); b64[b64s]=0; strcat(sdpLine, b64); if (i+1<count) strcat(sdpLine, ","); } if (i) strcat(sdpLine, ","); count = gf_list_count(avcc->pictureParameterSets); for (i=0; i<count; i++) { GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(avcc->pictureParameterSets, i); b64s = gf_base64_encode(sl->data, sl->size, b64, 200); b64[b64s]=0; strcat(sdpLine, b64); if (i+1<count) strcat(sdpLine, ","); } } gf_odf_avc_cfg_del(avcc); strcat(sdpLine, "\n"); } } else if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_HEVC) { #ifndef GPAC_DISABLE_HEVC GF_HEVCConfig *hevcc = dsi ? gf_odf_hevc_cfg_read(dsi, dsi_len, 0) : NULL; if (hevcc) { u32 count, i, j, b64s; char b64[200]; sprintf(sdpLine, "a=fmtp:%d", rtp->packetizer->PayloadType); count = gf_list_count(hevcc->param_array); for (i = 0; i < count; i++) { GF_HEVCParamArray *ar = (GF_HEVCParamArray *)gf_list_get(hevcc->param_array, i); if (ar->type==GF_HEVC_NALU_SEQ_PARAM) { strcat(sdpLine, "; sprop-sps="); } else if (ar->type==GF_HEVC_NALU_PIC_PARAM) { strcat(sdpLine, "; sprop-pps="); } else if (ar->type==GF_HEVC_NALU_VID_PARAM) { strcat(sdpLine, "; sprop-vps="); } for (j = 0; j < gf_list_count(ar->nalus); j++) { GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(ar->nalus, j); b64s = gf_base64_encode(sl->data, sl->size, b64, 200); b64[b64s]=0; if (j) strcat(sdpLine, ", "); strcat(sdpLine, b64); } } gf_odf_hevc_cfg_del(hevcc); strcat(sdpLine, "\n"); } #endif } /*MPEG-4 decoder config*/ else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_MPEG4) { gf_rtp_builder_format_sdp(rtp->packetizer, payloadName, sdpLine, dsi, dsi_len); strcat(sdpLine, "\n"); if (rtp->packetizer->slMap.IV_length && KMS_URI) { if (!strnicmp(KMS_URI, "(key)", 5) || !strnicmp(KMS_URI, "(ipmp)", 6) || !strnicmp(KMS_URI, "(uri)", 5)) { strcat(sdpLine, "; ISMACrypKey="); } else { strcat(sdpLine, "; ISMACrypKey=(uri)"); } strcat(sdpLine, KMS_URI); strcat(sdpLine, "\n"); } } /*DIMS decoder config*/ else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_3GPP_DIMS) { sprintf(sdpLine, "a=fmtp:%d Version-profile=%d", rtp->packetizer->PayloadType, 10); if (rtp->packetizer->flags & GP_RTP_DIMS_COMPRESSED) { strcat(sdpLine, ";content-coding=deflate"); } strcat(sdpLine, "\n"); } /*MPEG-4 Audio LATM*/ else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_LATM) { GF_BitStream *bs; char *config_bytes; u32 config_size; /* form config string */ bs = gf_bs_new(NULL, 32, GF_BITSTREAM_WRITE); gf_bs_write_int(bs, 0, 1); /* AudioMuxVersion */ gf_bs_write_int(bs, 1, 1); /* all streams same time */ gf_bs_write_int(bs, 0, 6); /* numSubFrames */ gf_bs_write_int(bs, 0, 4); /* numPrograms */ gf_bs_write_int(bs, 0, 3); /* numLayer */ /* audio-specific config - PacketVideo patch: don't signal SBR and PS stuff, not allowed in LATM with audioMuxVersion=0*/ if (dsi) gf_bs_write_data(bs, dsi, MIN(dsi_len, 2) ); /* other data */ gf_bs_write_int(bs, 0, 3); /* frameLengthType */ gf_bs_write_int(bs, 0xff, 8); /* latmBufferFullness */ gf_bs_write_int(bs, 0, 1); /* otherDataPresent */ gf_bs_write_int(bs, 0, 1); /* crcCheckPresent */ gf_bs_get_content(bs, &config_bytes, &config_size); gf_bs_del(bs); gf_rtp_builder_format_sdp(rtp->packetizer, payloadName, sdpLine, config_bytes, config_size); gf_free(config_bytes); strcat(sdpLine, "\n"); } strcat(sdp, sdpLine); size = (u32) strlen(sdp) + (*out_sdp_buffer ? (u32) strlen(*out_sdp_buffer) : 0) + 1; if ( !*out_sdp_buffer) { *out_sdp_buffer = gf_malloc(sizeof(char)*size); if (! *out_sdp_buffer) return GF_OUT_OF_MEM; strcpy(*out_sdp_buffer, sdp); } else { *out_sdp_buffer = gf_realloc(*out_sdp_buffer, sizeof(char)*size); if (! *out_sdp_buffer) return GF_OUT_OF_MEM; strcat(*out_sdp_buffer, sdp); } return GF_OK; }
int sdp_generator(PNC_CallbackData *data, char *ip_dest, char *sdp_fmt) { GF_SceneEngine *codec; GF_ESD *esd = NULL; u32 size,size64; char *buffer; char buf64[5000]; FILE *fp; int ret; char temp[5000]; u16 port; u32 socket_type; gf_sk_get_local_info(data->chan->rtp, &port, &socket_type); fp = fopen("broadcaster.sdp", "w+"); if(fp == NULL) { fprintf(stderr, "Cannot open SDP file broadcaster.sdp\n"); exit(1); } ret = fwrite("v=0\n", 1, 4, fp); sprintf(temp, "o=GpacBroadcaster 3326096807 1117107880000 IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); ret = fwrite(temp, 1, strlen(temp), fp); ret = fwrite("s=MPEG4Broadcaster\n", 1, 19, fp); sprintf(temp, "c=IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); ret = fwrite(temp, 1, strlen(temp), fp); ret = fwrite("t=0 0\n", 1, 6, fp); codec = (GF_SceneEngine *) data->codec; if (codec) { buffer = NULL; size = 0; gf_odf_desc_write((GF_Descriptor *) codec->ctx->root_od, &buffer, &size); esd = gf_list_get(codec->ctx->root_od->ESDescriptors, 0); size64 = gf_base64_encode((unsigned char *) buffer, size, (unsigned char *) buf64, 2000); buf64[size64] = 0; free(buffer); sprintf(temp, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\n", buf64); ret = fwrite(temp, 1, strlen(temp), fp); } sprintf(temp, "m=application %d RTP/AVP 96\n", port); ret = fwrite(temp, 1, strlen(temp), fp); ret = fwrite("a=rtpmap:96 mpeg4-generic/1000\n", 1, 31, fp); if (esd) { sprintf(temp, "a=mpeg4-esid:%d\n", esd->ESID); ret = fwrite(temp, 1, strlen(temp), fp); } sprintf(temp, "%s\n", sdp_fmt); ret = fwrite(temp, 1, strlen(temp), fp); fflush(fp); fclose(fp); dprintf(DEBUG_sdp_generator, "SDP file generated in broadcaster.sdp\n"); return GF_OK; }
GF_EXPORT GF_Err gf_hinter_finalize(GF_ISOFile *file, u32 IOD_Profile, u32 bandwidth) { u32 i, sceneT, odT, descIndex, size, size64; GF_InitialObjectDescriptor *iod; GF_SLConfig slc; GF_ESD *esd; GF_ISOSample *samp; Bool remove_ocr; char *buffer; char buf64[5000], sdpLine[2300]; gf_isom_sdp_clean(file); if (bandwidth) { sprintf(buf64, "b=AS:%d", bandwidth); gf_isom_sdp_add_line(file, buf64); } //xtended attribute for copyright sprintf(buf64, "a=x-copyright: %s", "MP4/3GP File hinted with GPAC " GPAC_FULL_VERSION " (C)2000-2005 - http://gpac.sourceforge.net"); gf_isom_sdp_add_line(file, buf64); if (IOD_Profile == GF_SDP_IOD_NONE) return GF_OK; odT = sceneT = 0; for (i=0; i<gf_isom_get_track_count(file); i++) { if (!gf_isom_is_track_in_root_od(file, i+1)) continue; switch (gf_isom_get_media_type(file,i+1)) { case GF_ISOM_MEDIA_OD: odT = i+1; break; case GF_ISOM_MEDIA_SCENE: sceneT = i+1; break; } } remove_ocr = 0; if (IOD_Profile == GF_SDP_IOD_ISMA_STRICT) { IOD_Profile = GF_SDP_IOD_ISMA; remove_ocr = 1; } /*if we want ISMA like iods, we need at least BIFS */ if ( (IOD_Profile == GF_SDP_IOD_ISMA) && !sceneT ) return GF_BAD_PARAM; /*do NOT change PLs, we assume they are correct*/ iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(file); if (!iod) return GF_NOT_SUPPORTED; /*rewrite an IOD with good SL config - embbed data if possible*/ if (IOD_Profile == GF_SDP_IOD_ISMA) { Bool is_ok = 1; while (gf_list_count(iod->ESDescriptors)) { esd = (GF_ESD*)gf_list_get(iod->ESDescriptors, 0); gf_odf_desc_del((GF_Descriptor *) esd); gf_list_rem(iod->ESDescriptors, 0); } /*get OD esd, and embbed stream data if possible*/ if (odT) { esd = gf_isom_get_esd(file, odT, 1); if (gf_isom_get_sample_count(file, odT)==1) { samp = gf_isom_get_sample(file, odT, 1, &descIndex); if (gf_hinter_can_embbed_data(samp->data, samp->dataLength, GF_STREAM_OD)) { InitSL_NULL(&slc); slc.predefined = 0; slc.hasRandomAccessUnitsOnlyFlag = 1; slc.timeScale = slc.timestampResolution = gf_isom_get_media_timescale(file, odT); slc.OCRResolution = 1000; slc.startCTS = samp->DTS+samp->CTS_Offset; slc.startDTS = samp->DTS; //set the SL for future extraction gf_isom_set_extraction_slc(file, odT, 1, &slc); size64 = gf_base64_encode(samp->data, samp->dataLength, buf64, 2000); buf64[size64] = 0; sprintf(sdpLine, "data:application/mpeg4-od-au;base64,%s", buf64); esd->decoderConfig->avgBitrate = 0; esd->decoderConfig->bufferSizeDB = samp->dataLength; esd->decoderConfig->maxBitrate = 0; size64 = strlen(sdpLine)+1; esd->URLString = (char*)gf_malloc(sizeof(char) * size64); strcpy(esd->URLString, sdpLine); } else { GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[rtp hinter] OD sample too large to be embedded in IOD - ISMA disabled\n")); is_ok = 0; } gf_isom_sample_del(&samp); } if (remove_ocr) esd->OCRESID = 0; else if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; //OK, add this to our IOD gf_list_add(iod->ESDescriptors, esd); } esd = gf_isom_get_esd(file, sceneT, 1); if (gf_isom_get_sample_count(file, sceneT)==1) { samp = gf_isom_get_sample(file, sceneT, 1, &descIndex); if (gf_hinter_can_embbed_data(samp->data, samp->dataLength, GF_STREAM_SCENE)) { slc.timeScale = slc.timestampResolution = gf_isom_get_media_timescale(file, sceneT); slc.OCRResolution = 1000; slc.startCTS = samp->DTS+samp->CTS_Offset; slc.startDTS = samp->DTS; //set the SL for future extraction gf_isom_set_extraction_slc(file, sceneT, 1, &slc); //encode in Base64 the sample size64 = gf_base64_encode(samp->data, samp->dataLength, buf64, 2000); buf64[size64] = 0; sprintf(sdpLine, "data:application/mpeg4-bifs-au;base64,%s", buf64); esd->decoderConfig->avgBitrate = 0; esd->decoderConfig->bufferSizeDB = samp->dataLength; esd->decoderConfig->maxBitrate = 0; esd->URLString = (char*)gf_malloc(sizeof(char) * (strlen(sdpLine)+1)); strcpy(esd->URLString, sdpLine); } else { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Scene description sample too large to be embedded in IOD - ISMA disabled\n")); is_ok = 0; } gf_isom_sample_del(&samp); } if (remove_ocr) esd->OCRESID = 0; else if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; gf_list_add(iod->ESDescriptors, esd); if (is_ok) { u32 has_a, has_v, has_i_a, has_i_v; has_a = has_v = has_i_a = has_i_v = 0; for (i=0; i<gf_isom_get_track_count(file); i++) { esd = gf_isom_get_esd(file, i+1, 1); if (!esd) continue; if (esd->decoderConfig->streamType==GF_STREAM_VISUAL) { if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_MPEG4_PART2) has_i_v ++; else has_v++; } else if (esd->decoderConfig->streamType==GF_STREAM_AUDIO) { if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_AUDIO_AAC_MPEG4) has_i_a ++; else has_a++; } gf_odf_desc_del((GF_Descriptor *)esd); } /*only 1 MPEG-4 visual max and 1 MPEG-4 audio max for ISMA compliancy*/ if (!has_v && !has_a && (has_i_v<=1) && (has_i_a<=1)) { sprintf(sdpLine, "a=isma-compliance:1,1.0,1"); gf_isom_sdp_add_line(file, sdpLine); } } } //encode the IOD buffer = NULL; size = 0; gf_odf_desc_write((GF_Descriptor *) iod, &buffer, &size); gf_odf_desc_del((GF_Descriptor *)iod); //encode in Base64 the iod size64 = gf_base64_encode(buffer, size, buf64, 2000); buf64[size64] = 0; gf_free(buffer); sprintf(sdpLine, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"", buf64); gf_isom_sdp_add_line(file, sdpLine); return GF_OK; }
GF_EXPORT GF_Err gf_hinter_track_finalize(GF_RTPHinter *tkHint, Bool AddSystemInfo) { u32 Width, Height; GF_ESD *esd; char sdpLine[20000]; char mediaName[30], payloadName[30]; Width = Height = 0; gf_isom_sdp_clean_track(tkHint->file, tkHint->TrackNum); if (gf_isom_get_media_type(tkHint->file, tkHint->TrackNum) == GF_ISOM_MEDIA_VISUAL) gf_isom_get_visual_info(tkHint->file, tkHint->TrackNum, 1, &Width, &Height); gf_rtp_builder_get_payload_name(tkHint->rtp_p, payloadName, mediaName); /*TODO- extract out of rtp_p for future live tools*/ sprintf(sdpLine, "m=%s 0 RTP/%s %d", mediaName, tkHint->rtp_p->slMap.IV_length ? "SAVP" : "AVP", tkHint->rtp_p->PayloadType); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); if (tkHint->bandwidth) { sprintf(sdpLine, "b=AS:%d", tkHint->bandwidth); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } if (tkHint->nb_chan) { sprintf(sdpLine, "a=rtpmap:%d %s/%d/%d", tkHint->rtp_p->PayloadType, payloadName, tkHint->rtp_p->sl_config.timestampResolution, tkHint->nb_chan); } else { sprintf(sdpLine, "a=rtpmap:%d %s/%d", tkHint->rtp_p->PayloadType, payloadName, tkHint->rtp_p->sl_config.timestampResolution); } gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); /*control for MPEG-4*/ if (AddSystemInfo) { sprintf(sdpLine, "a=mpeg4-esid:%d", gf_isom_get_track_id(tkHint->file, tkHint->TrackNum)); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } /*control for QTSS/DSS*/ sprintf(sdpLine, "a=control:trackID=%d", gf_isom_get_track_id(tkHint->file, tkHint->HintTrack)); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); /*H263 extensions*/ if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H263) { sprintf(sdpLine, "a=cliprect:0,0,%d,%d", Height, Width); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } /*AMR*/ else if ((tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_AMR) || (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_AMR_WB)) { sprintf(sdpLine, "a=fmtp:%d octet-align=1", tkHint->rtp_p->PayloadType); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } /*Text*/ else if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_3GPP_TEXT) { gf_media_format_ttxt_sdp(tkHint->rtp_p, payloadName, sdpLine, tkHint->file, tkHint->TrackNum); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } /*EVRC/SMV in non header-free mode*/ else if ((tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_EVRC_SMV) && (tkHint->rtp_p->auh_size>1)) { sprintf(sdpLine, "a=fmtp:%d maxptime=%d", tkHint->rtp_p->PayloadType, tkHint->rtp_p->auh_size*20); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } /*H264/AVC*/ else if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H264_AVC) { GF_AVCConfig *avcc = gf_isom_avc_config_get(tkHint->file, tkHint->TrackNum, 1); sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", tkHint->rtp_p->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication); if (gf_list_count(avcc->pictureParameterSets) || gf_list_count(avcc->sequenceParameterSets)) { u32 i, count, b64s; char b64[200]; strcat(sdpLine, "; sprop-parameter-sets="); count = gf_list_count(avcc->sequenceParameterSets); for (i=0; i<count; i++) { GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(avcc->sequenceParameterSets, i); b64s = gf_base64_encode(sl->data, sl->size, b64, 200); b64[b64s]=0; strcat(sdpLine, b64); if (i+1<count) strcat(sdpLine, ","); } if (i) strcat(sdpLine, ","); count = gf_list_count(avcc->pictureParameterSets); for (i=0; i<count; i++) { GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(avcc->pictureParameterSets, i); b64s = gf_base64_encode(sl->data, sl->size, b64, 200); b64[b64s]=0; strcat(sdpLine, b64); if (i+1<count) strcat(sdpLine, ","); } } gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); gf_odf_avc_cfg_del(avcc); } /*MPEG-4 decoder config*/ else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_MPEG4) { esd = gf_isom_get_esd(tkHint->file, tkHint->TrackNum, 1); if (esd && esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); } else { gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, NULL, 0); } if (esd) gf_odf_desc_del((GF_Descriptor *)esd); if (tkHint->rtp_p->slMap.IV_length) { const char *kms; gf_isom_get_ismacryp_info(tkHint->file, tkHint->TrackNum, 1, NULL, NULL, NULL, NULL, &kms, NULL, NULL, NULL); if (!strnicmp(kms, "(key)", 5) || !strnicmp(kms, "(ipmp)", 6) || !strnicmp(kms, "(uri)", 5)) { strcat(sdpLine, "; ISMACrypKey="); } else { strcat(sdpLine, "; ISMACrypKey=(uri)"); } strcat(sdpLine, kms); } gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } /*MPEG-4 Audio LATM*/ else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_LATM) { GF_BitStream *bs; char *config_bytes; u32 config_size; /* form config string */ bs = gf_bs_new(NULL, 32, GF_BITSTREAM_WRITE); gf_bs_write_int(bs, 0, 1); /* AudioMuxVersion */ gf_bs_write_int(bs, 1, 1); /* all streams same time */ gf_bs_write_int(bs, 0, 6); /* numSubFrames */ gf_bs_write_int(bs, 0, 4); /* numPrograms */ gf_bs_write_int(bs, 0, 3); /* numLayer */ /* audio-specific config */ esd = gf_isom_get_esd(tkHint->file, tkHint->TrackNum, 1); if (esd && esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo) { /*PacketVideo patch: don't signal SBR and PS stuff, not allowed in LATM with audioMuxVersion=0*/ gf_bs_write_data(bs, esd->decoderConfig->decoderSpecificInfo->data, MIN(esd->decoderConfig->decoderSpecificInfo->dataLength, 2) ); } if (esd) gf_odf_desc_del((GF_Descriptor *)esd); /* other data */ gf_bs_write_int(bs, 0, 3); /* frameLengthType */ gf_bs_write_int(bs, 0xff, 8); /* latmBufferFullness */ gf_bs_write_int(bs, 0, 1); /* otherDataPresent */ gf_bs_write_int(bs, 0, 1); /* crcCheckPresent */ gf_bs_get_content(bs, &config_bytes, &config_size); gf_bs_del(bs); gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, config_bytes, config_size); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); gf_free(config_bytes); } /*3GPP DIMS*/ else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_3GPP_DIMS) { GF_DIMSDescription dims; char fmt[200]; gf_isom_get_visual_info(tkHint->file, tkHint->TrackNum, 1, &Width, &Height); gf_isom_get_dims_description(tkHint->file, tkHint->TrackNum, 1, &dims); sprintf(sdpLine, "a=fmtp:%d Version-profile=%d", tkHint->rtp_p->PayloadType, dims.profile); if (! dims.fullRequestHost) { strcat(sdpLine, ";useFullRequestHost=0"); sprintf(fmt, ";pathComponents=%d", dims.pathComponents); strcat(sdpLine, fmt); } if (!dims.streamType) strcat(sdpLine, ";stream-type=secondary"); if (dims.containsRedundant == 1) strcat(sdpLine, ";contains-redundant=main"); else if (dims.containsRedundant == 2) strcat(sdpLine, ";contains-redundant=redundant"); if (dims.textEncoding && strlen(dims.textEncoding)) { strcat(sdpLine, ";text-encoding="); strcat(sdpLine, dims.textEncoding); } if (dims.contentEncoding && strlen(dims.contentEncoding)) { strcat(sdpLine, ";content-coding="); strcat(sdpLine, dims.contentEncoding); } if (dims.content_script_types && strlen(dims.content_script_types) ) { strcat(sdpLine, ";content-script-types="); strcat(sdpLine, dims.contentEncoding); } gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } /*extensions for some mobile phones*/ if (Width && Height) { sprintf(sdpLine, "a=framesize:%d %d-%d", tkHint->rtp_p->PayloadType, Width, Height); gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } esd = gf_isom_get_esd(tkHint->file, tkHint->TrackNum, 1); if (esd && esd->decoderConfig && (esd->decoderConfig->rvc_config || esd->decoderConfig->predefined_rvc_config)) { if (esd->decoderConfig->predefined_rvc_config) { sprintf(sdpLine, "a=rvc-config-predef:%d", esd->decoderConfig->predefined_rvc_config); } else { /*temporary ...*/ if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_AVC) { sprintf(sdpLine, "a=rvc-config:%s", "http://download.tsi.telecom-paristech.fr/gpac/RVC/rvc_config_avc.xml"); } else { sprintf(sdpLine, "a=rvc-config:%s", "http://download.tsi.telecom-paristech.fr/gpac/RVC/rvc_config_sp.xml"); } } gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); } if (esd) gf_odf_desc_del((GF_Descriptor *)esd); gf_isom_set_track_enabled(tkHint->file, tkHint->HintTrack, 1); return GF_OK; }
int sdp_generator(PNC_CallbackData * data, char *ip_dest, char *sdp_fmt) { GF_BifsEngine *codec; GF_ESD *esd; u32 size,size64; char *buffer; char buf64[5000]; FILE *fp; int ret; char temp[5000]; u16 port; u32 socket_type; // fonctions necessaires pour recuperer les informations necessaires a la construction du fichier gf_sk_get_local_info(data->chan->rtp, &port, &socket_type); // fprintf(stdout, "%s --------- %d\n", ip_adresse, port); fp = fopen("broadcaster.sdp", "w+"); if(fp == NULL) { fprintf(stdout, "[broadcaster] : erreur, probleme a ouvrir file temp pour scene initiale\n"); exit(1); } // ecriture du fichier SDP ret = fwrite("v=0\n", 1, 4, fp); sprintf(temp, "o=GpacBroadcaster 3326096807 1117107880000 IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); ret = fwrite(temp, 1, strlen(temp), fp); ret = fwrite("s=MPEG4Broadcaster\n", 1, 19, fp); sprintf(temp, "c=IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); ret = fwrite(temp, 1, strlen(temp), fp); ret = fwrite("t=0 0\n", 1, 6, fp); // GF_BIFSEngine codec = (GF_BifsEngine *) data->codec; buffer = NULL; size = 0; gf_odf_desc_write((GF_Descriptor *) codec->ctx->root_od, &buffer, &size); esd = gf_list_get(codec->ctx->root_od->ESDescriptors, 0); //encode in Base64 the iod size64 = gf_base64_encode((unsigned char *) buffer, size, (unsigned char *) buf64, 2000); // size64 = gf_base64_encode(buffer, size, buf64, 2000); buf64[size64] = 0; free(buffer); // fprintf(stdout, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\n", buf64); sprintf(temp, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\n", buf64); ret = fwrite(temp, 1, strlen(temp), fp); sprintf(temp, "m=application %d RTP/AVP 96\n", port); ret = fwrite(temp, 1, strlen(temp), fp); ret = fwrite("a=rtpmap:96 mpeg4-generic/1000\n", 1, 31, fp); sprintf(temp, "a=mpeg4-esid:%d\n", esd->ESID); ret = fwrite(temp, 1, strlen(temp), fp); // fprintf(stdout, "%s\n", sdp_fmt); sprintf(temp, "%s\n", sdp_fmt); ret = fwrite(temp, 1, strlen(temp), fp); fclose(fp); fprintf(stdout, "[sdp generator] : fichier SDP generater in broadcaster.sdp\n"); return GF_OK; }
int main(int argc, char **argv) { /********************/ /* declarations */ /********************/ char *input, *output, tmpstr[GF_MAX_PATH]; GF_ISOFile *isom_file_in; GF_MediaImporter import; AdobeHDSCtx ctx; GF_Err e; u32 i; /*****************/ /* gpac init */ /*****************/ gf_sys_init(GF_MemTrackerNone); gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_WARNING); /***********************/ /* initialisations */ /***********************/ input = NULL; output = NULL; isom_file_in = NULL; memset(&import, 0, sizeof(GF_MediaImporter)); e = GF_OK; memset(&ctx, 0, sizeof(ctx)); ctx.curr_time = 0; ctx.segnum = 1; /*********************************************/ /* parse arguments and build HDS context */ /*********************************************/ if (GF_OK != parse_args(argc, argv, &input, &output, &ctx.curr_time, &ctx.segnum)) { usage(argv[0]); goto exit; } ctx.multirate_manifest = adobe_alloc_multirate_manifest(output); #if 0 /*'moov' conversion tests*/ { char metamoov64[GF_MAX_PATH]; u32 metamoov64_len; unsigned char metamoov[GF_MAX_PATH]; u32 metamoov_len=GF_MAX_PATH; FILE *f = gf_fopen("metamoov64"/*input*/, "rt"); gf_fseek(f, 0, SEEK_END); metamoov64_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(metamoov64, metamoov64_len, 1, f); metamoov_len = gf_base64_decode(metamoov64, metamoov64_len, metamoov, metamoov_len); gf_fclose(f); f = gf_fopen("metamoov", "wb"); fwrite(metamoov, metamoov_len, 1, f); gf_fclose(f); return 0; } #endif #if 0 /*'abst'conversion tests*/ { char bootstrap64[GF_MAX_PATH]; u32 bootstrap64_len; unsigned char bootstrap[GF_MAX_PATH]; u32 bootstrap_len=GF_MAX_PATH; GF_AdobeBootstrapInfoBox *abst = (GF_AdobeBootstrapInfoBox *)abst_New(); GF_BitStream *bs; #if 1 //64 FILE *f = gf_fopen("bootstrap64"/*input*/, "rt"); gf_fseek(f, 0, SEEK_END); bootstrap64_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(bootstrap64, bootstrap64_len, 1, f); bootstrap_len = gf_base64_decode(bootstrap64, bootstrap64_len, bootstrap, bootstrap_len); #else //binary bootstrap FILE *f = gf_fopen("bootstrap.bin"/*input*/, "rb"); gf_fseek(f, 0, SEEK_END); bootstrap_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(bootstrap, bootstrap_len, 1, f); #endif bs = gf_bs_new(bootstrap+8, bootstrap_len-8, GF_BITSTREAM_READ); abst->size = bootstrap[2]*256+bootstrap[3]; assert(abst->size<GF_MAX_PATH); abst_Read((GF_Box*)abst, bs); gf_bs_del(bs); //then rewrite with just one 'afrt' memset(bootstrap, 0, bootstrap_len); bs = gf_bs_new(bootstrap, bootstrap_len, GF_BITSTREAM_WRITE); abst_Write((GF_Box*)abst, bs); bootstrap_len = (u32)gf_bs_get_position(bs); gf_bs_del(bs); gf_fclose(f); f = gf_fopen("bootstrap", "wt"); bootstrap64_len = gf_base64_encode(bootstrap, bootstrap_len, bootstrap64, GF_MAX_PATH); fwrite(bootstrap64, bootstrap64_len, 1, f); fprintf(f, "\n\n"); abst_dump((GF_Box*)abst, f); gf_fclose(f); abst_del((GF_Box*)abst); return 0; } #endif /*****************/ /* main loop */ /*****************/ import.trackID = 0; import.in_name = input; import.flags = GF_IMPORT_PROBE_ONLY; //create output or open when recovering from a saved state sprintf(tmpstr, "%s_import.mp4", input); isom_file_in = gf_isom_open(tmpstr, GF_ISOM_WRITE_EDIT, NULL); if (!isom_file_in) { fprintf(stderr, "Error opening output file %s: %s\n", tmpstr, gf_error_to_string(e)); assert(0); goto exit; } import.dest = isom_file_in; //probe input e = gf_media_import(&import); if (e) { fprintf(stderr, "Error while importing input file %s: %s\n", input, gf_error_to_string(e)); assert(0); goto exit; } //import input data import.flags = 0; for (i=0; i<import.nb_tracks; i++) { import.trackID = import.tk_info[i].track_num; e = gf_media_import(&import); if (e) { fprintf(stderr, "Error while importing track number %u, input file %s: %s\n", import.trackID, input, gf_error_to_string(e)); assert(0); goto exit; } } //Adobe specific stuff e = adobize_segment(isom_file_in, &ctx); if (e) { fprintf(stderr, "Couldn't turn the ISOM fragmented file into an Adobe f4v segment: %s\n", gf_error_to_string(e)); assert(0); goto exit; } //interleave data and remove imported file //FIXME: set multiple fragments: sprintf(tmpstr, "%s_HD_100_Seg%u-Frag1", output, ctx.segnum); //FIXME: "HD", "100" and fragnum: pass as arg //e = gf_media_fragment_file(isom_file_in, tmpstr, 1.0); e = gf_media_fragment_file(isom_file_in, tmpstr, 1.0+gf_isom_get_duration(isom_file_in)/gf_isom_get_timescale(isom_file_in)); if (e) { fprintf(stderr, "Error while fragmenting file to output %s: %s\n", output, gf_error_to_string(e)); assert(0); goto exit; } gf_isom_delete(isom_file_in); isom_file_in = NULL; e = adobe_gen_multirate_manifest(ctx.multirate_manifest, ctx.bootstrap, ctx.bootstrap_size); if (e) { fprintf(stderr, "Couldn't generate Adobe f4m manifest: %s\n", gf_error_to_string(e)); assert(0); goto exit; } exit: //delete intermediate mp4 file if (isom_file_in) gf_isom_delete(isom_file_in); if (ctx.multirate_manifest) adobe_free_multirate_manifest(ctx.multirate_manifest); if (ctx.bootstrap) { gf_free(ctx.bootstrap); //ctx.bootstrap = NULL; //ctx.bootstrap_size = 0; } gf_sys_close(); return !e ? 0 : 1; }
void http_do_requests(GF_DownloadSession *sess) { GF_Err e; Bool is_ice; GF_NETIO_Parameter par; char sHTTP[GF_DOWNLOAD_BUFFER_SIZE]; char buf[1024]; char comp[400]; char *new_location; char *hdr, *hdr_val; u32 bytesRead, res; s32 LinePos, Pos; u32 rsp_code, ContentLength, first_byte, last_byte, total_size, range, no_range; s32 BodyStart; /*sent HTTP request*/ if (sess->status==GF_NETIO_CONNECTED) { char range_buf[1024]; char pass_buf[1024]; const char *user_agent; u32 size; Bool has_accept, has_connection, has_range, has_agent; /*setup authentification*/ strcpy(pass_buf, ""); if (sess->user) { if (!sess->passwd) { char szUSR[50], szPASS[50]; strcpy(szUSR, sess->user); strcpy(szPASS, ""); /*failed getting pass*/ if (!sess->dm->GetUserPassword || !sess->dm->GetUserPassword(sess->dm->usr_cbk, sess->server_name, szUSR, szPASS)) { sess->status = GF_NETIO_STATE_ERROR; return; } sess->passwd = strdup(szPASS); } sprintf(pass_buf, "%s:%s", sess->user, sess->passwd); size = gf_base64_encode(pass_buf, strlen(pass_buf), range_buf, 1024); range_buf[size] = 0; sprintf(pass_buf, "Authorization: Basic %s", range_buf); } /*MIX2005 KMS project*/ #if 0 if (strstr(sess->remote_path, "getKey.php?")) { char *sLogin, *sPass; sLogin = gf_cfg_get_key(sess->dm->cfg, "General", "KMS_User"); sPass = gf_cfg_get_key(sess->dm->cfg, "General", "KMS_Password"); if (!sLogin) sLogin = "******"; if (!sPass) sPass = "******"; sprintf(https_get_buffer, "%s&login=%s&password=%s", sess->remote_path, sLogin, sPass); } #endif user_agent = gf_cfg_get_key(sess->dm->cfg, "Downloader", "UserAgent"); if (!user_agent) user_agent = GF_DOWNLOAD_AGENT_NAME; par.error = 0; par.msg_type = GF_NETIO_GET_METHOD; par.name = NULL; gf_dm_sess_user_io(sess, &par); if (par.name) { if (!strcmp(par.name, "GET")) sess->http_read_type = 0; else if (!strcmp(par.name, "HEAD")) sess->http_read_type = 1; else sess->http_read_type = 2; } else { sess->http_read_type = 0; } sprintf(sHTTP, "%s %s HTTP/1.0\r\nHost: %s\r\n" , par.name ? par.name : "GET", sess->remote_path, sess->server_name); /*signal we support title streaming*/ if (!strcmp(sess->remote_path, "/")) strcat(sHTTP, "icy-metadata:1\r\n"); /*get all headers*/ has_agent = has_accept = has_connection = has_range = 0; while (1) { par.msg_type = GF_NETIO_GET_HEADER; par.name = NULL; par.value = NULL; gf_dm_sess_user_io(sess, &par); if (!par.name) break; strcat(sHTTP, par.name); strcat(sHTTP, ": "); strcat(sHTTP, par.value); strcat(sHTTP, "\r\n"); if (!strcmp(par.name, "Accept")) has_accept = 1; else if (!strcmp(par.name, "Connection")) has_connection = 1; else if (!strcmp(par.name, "Range")) has_range = 1; else if (!strcmp(par.name, "User-Agent")) has_agent = 1; } if (!has_agent) { strcat(sHTTP, "User-Agent: "); strcat(sHTTP, user_agent); strcat(sHTTP, "\r\n"); } if (!has_accept) strcat(sHTTP, "Accept: */*\r\n"); if (!has_connection) strcat(sHTTP, "Connection: Keep-Alive\r\n"); if (!has_range && sess->cache_start_size) { sprintf(range_buf, "Range: bytes=%d-\r\n", sess->cache_start_size); strcat(sHTTP, range_buf); } if (strlen(pass_buf)) { strcat(sHTTP, pass_buf); strcat(sHTTP, "\r\n"); } if (sess->flags & GF_DOWNLOAD_IS_ICY) strcat(sHTTP, "Icy-Metadata: 1\r\n"); par.msg_type = GF_NETIO_GET_CONTENT; par.data = NULL; par.size = 0; gf_dm_sess_user_io(sess, &par); if (par.data && par.size) { sprintf(range_buf, "Content-Length: %d\r\n", par.size); strcat(sHTTP, range_buf); } strcat(sHTTP, "\r\n"); #ifdef GPAC_HAS_SSL if (sess->ssl) { e = GF_IP_NETWORK_FAILURE; if (!SSL_write(sess->ssl, sHTTP, strlen(sHTTP))) e = GF_OK; } else #endif e = gf_sk_send(sess->sock, sHTTP, strlen(sHTTP)); GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s\n\n", sHTTP)); if (e) { sess->status = GF_NETIO_STATE_ERROR; sess->last_error = e; gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e); return; } if (par.size && par.data) { u32 done = 0; while (done<par.size) { #ifdef GPAC_HAS_SSL if (sess->ssl) { e = GF_IP_NETWORK_FAILURE; if (!SSL_write(sess->ssl, par.data+done, par.size-done)) e = GF_OK; } else #endif e = gf_sk_send(sess->sock, par.data+done, par.size-done); if (e) { sess->status = GF_NETIO_STATE_ERROR; sess->last_error = e; gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e); return; } } } sess->status = GF_NETIO_WAIT_FOR_REPLY; gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK); return; } /*process HTTP request*/ if (sess->status == GF_NETIO_WAIT_FOR_REPLY) { bytesRead = res = 0; new_location = NULL; while (1) { e = gf_dm_read_data(sess, sHTTP + bytesRead, GF_DOWNLOAD_BUFFER_SIZE - bytesRead, &res); switch (e) { case GF_IP_NETWORK_EMPTY: if (!bytesRead) return; continue; /*socket has been closed while configuring, retry (not sure if the server got the GET)*/ case GF_IP_CONNECTION_CLOSED: gf_dm_disconnect(sess); if (sess->num_retry) sess->status = GF_NETIO_SETUP; else { sess->last_error = e; sess->status = GF_NETIO_STATE_ERROR; } return; case GF_OK: if (!res) return; break; default: goto exit; } bytesRead += res; /*locate body start*/ BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\r\n\r\n"); if (BodyStart <= 0) { BodyStart=0; continue; } BodyStart += 4; break; } if (bytesRead < 0) { e = GF_REMOTE_SERVICE_ERROR; goto exit; } if (!BodyStart) BodyStart = bytesRead; sHTTP[BodyStart-1] = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s\n\n", sHTTP)); LinePos = gf_token_get_line(sHTTP, 0, bytesRead, buf, 1024); Pos = gf_token_get(buf, 0, " \t\r\n", comp, 400); if (sess->mime_type) free(sess->mime_type); sess->mime_type = NULL; is_ice = 0; if (!strncmp("ICY", comp, 4)) { is_ice = 1; /*be prepared not to recieve any mime type from ShoutCast servers*/ sess->mime_type = strdup("audio/mpeg"); } else if ((strncmp("HTTP", comp, 4) != 0)) { e = GF_REMOTE_SERVICE_ERROR; goto exit; } Pos = gf_token_get(buf, Pos, " ", comp, 400); if (Pos <= 0) { e = GF_REMOTE_SERVICE_ERROR; goto exit; } rsp_code = (u32) atoi(comp); Pos = gf_token_get(buf, Pos, " \r\n", comp, 400); no_range = range = ContentLength = first_byte = last_byte = total_size = 0; //parse header while (1) { char *sep, *hdr_sep; if ( (u32) LinePos + 4 > BodyStart) break; LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024); if (LinePos < 0) break; hdr_sep = NULL; hdr_val = NULL; hdr = buf; sep = strchr(buf, ':'); if (sep) { sep[0]=0; hdr_val = sep+1; while (hdr_val[0]==' ') hdr_val++; hdr_sep = strrchr(hdr_val, '\r'); if (hdr_sep) hdr_sep[0] = 0; } par.error = 0; par.msg_type = GF_NETIO_PARSE_HEADER; par.name = hdr; par.value = hdr_val; gf_dm_sess_user_io(sess, &par); if (!stricmp(hdr, "Content-Length") ) ContentLength = (u32) atoi(hdr_val); else if (!stricmp(hdr, "Content-Type")) { if (sess->mime_type) free(sess->mime_type); sess->mime_type = strdup(hdr_val); while (1) { u32 len = strlen(sess->mime_type); char c = len ? sess->mime_type[len-1] : 0; if ((c=='\r') || (c=='\n')) { sess->mime_type[len-1] = 0; } else { break; } } hdr = strchr(sess->mime_type, ';'); if (hdr) hdr[0] = 0; } else if (!stricmp(hdr, "Content-Range")) { range = 1; if (!strncmp(hdr_val, "bytes", 5)) { hdr_val += 5; if (hdr_val[0] == ':') hdr_val += 1; hdr_val += http_skip_space(hdr_val); if (hdr_val[0] == '*') { sscanf(hdr_val, "*/%d", &total_size); } else { sscanf(hdr_val, "%d-%d/%d", &first_byte, &last_byte, &total_size); } } } else if (!stricmp(hdr, "Accept-Ranges")) { if (strstr(hdr_val, "none")) no_range = 1; } else if (!stricmp(hdr, "Location")) new_location = strdup(hdr_val); else if (!stricmp(hdr, "icy-metaint")) sess->icy_metaint = atoi(hdr_val); else if (!stricmp(hdr, "ice") || !stricmp(hdr, "icy") ) is_ice = 1; if (sep) sep[0]=':'; if (hdr_sep) hdr_sep[0] = '\r'; } if (no_range) first_byte = 0; if (sess->cache_start_size) { if (total_size && (sess->cache_start_size >= total_size) ) { rsp_code = 200; ContentLength = total_size; } if (ContentLength && (sess->cache_start_size == ContentLength) ) rsp_code = 200; } par.msg_type = GF_NETIO_PARSE_REPLY; par.error = GF_OK; par.reply = rsp_code; par.value = comp; switch (rsp_code) { case 200: case 201: case 202: case 206: gf_dm_sess_user_io(sess, &par); e = GF_OK; break; /*redirection: extract the new location*/ case 301: case 302: if (!new_location || !strlen(new_location) ) { gf_dm_sess_user_io(sess, &par); e = GF_URL_ERROR; goto exit; } while ( (new_location[strlen(new_location)-1] == '\n') || (new_location[strlen(new_location)-1] == '\r') ) new_location[strlen(new_location)-1] = 0; /*reset and reconnect*/ gf_dm_disconnect(sess); sess->status = GF_NETIO_SETUP; e = gf_dm_setup_from_url(sess, new_location); if (e) { sess->status = GF_NETIO_STATE_ERROR; sess->last_error = e; gf_dm_sess_notify_state(sess, sess->status, e); return; } return; case 404: case 416: /*try without cache (some servers screw up when content-length is specified)*/ if (sess->cache_start_size) { gf_dm_disconnect(sess); sess->status = GF_NETIO_SETUP; return; } else if (is_ice && !(sess->flags & GF_DOWNLOAD_IS_ICY)) { gf_dm_disconnect(sess); sess->status = GF_NETIO_SETUP; sess->flags |= GF_DOWNLOAD_IS_ICY; return; } gf_dm_sess_user_io(sess, &par); e = GF_URL_ERROR; goto exit; case 503: default: gf_dm_sess_user_io(sess, &par); e = GF_REMOTE_SERVICE_ERROR; goto exit; } /*head*/ if (sess->http_read_type==1) { gf_dm_disconnect(sess); gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); sess->status = GF_NETIO_DISCONNECTED; sess->http_read_type = 0; return; } if (!ContentLength && sess->mime_type && strstr(sess->mime_type, "ogg")) is_ice = 1; /*some servers may reply without content length, but we MUST have it*/ // if (!is_ice && !ContentLength) e = GF_REMOTE_SERVICE_ERROR; if (e) goto exit; /*force disabling cache (no content length)*/ if (is_ice) { sess->flags |= GF_NETIO_SESSION_NOT_CACHED; if (sess->mime_type && !stricmp(sess->mime_type, "video/nsv")) { free(sess->mime_type); sess->mime_type = strdup("audio/aac"); } } /*done*/ if (sess->cache_start_size && ( (total_size && sess->cache_start_size >= total_size) || (sess->cache_start_size == ContentLength)) ) { sess->total_size = sess->bytes_done = sess->cache_start_size; /*disconnect*/ gf_dm_disconnect(sess); BodyStart = bytesRead; gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); } else if (sess->flags & GF_DOWNLOAD_IS_ICY) { sess->icy_bytes = 0; sess->status = GF_NETIO_DATA_EXCHANGE; } /*we don't expect anything*/ else if (!ContentLength && sess->http_read_type) { gf_dm_disconnect(sess); gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); sess->status = GF_NETIO_DISCONNECTED; sess->http_read_type = 0; } /*no range header, Accep-Ranges deny or dumb server : restart*/ else if (!range || !first_byte || (first_byte != sess->cache_start_size) ) { sess->cache_start_size = sess->bytes_done = 0; sess->total_size = ContentLength; if (! (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ) { sess->cache = fopen(sess->cache_name, "wb"); if (!sess->cache) { e = GF_IO_ERR; goto exit; } } sess->status = GF_NETIO_DATA_EXCHANGE; } /*resume*/ else { sess->total_size = ContentLength + sess->cache_start_size; if (! (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ) { sess->cache = fopen(sess->cache_name, "ab"); if (!sess->cache) { e = GF_IO_ERR; goto exit; } } sess->status = GF_NETIO_DATA_EXCHANGE; sess->bytes_done = sess->cache_start_size; } sess->window_start = sess->start_time = gf_sys_clock(); sess->bytes_in_wnd = 0; //we may have existing data in this buffer ... if (!e && (BodyStart < (u32) bytesRead)) { gf_dm_data_recieved(sess, sHTTP + BodyStart, bytesRead - BodyStart); /*store data if no callbacks or cache*/ if (sess->flags & GF_NETIO_SESSION_NOT_CACHED) { if (sess->init_data) free(sess->init_data); sess->init_data_size = bytesRead - BodyStart; sess->init_data = (char *) malloc(sizeof(char) * sess->init_data_size); memcpy(sess->init_data, sHTTP+BodyStart, sess->init_data_size); } } exit: if (e) { gf_dm_disconnect(sess); sess->status = GF_NETIO_STATE_ERROR; sess->last_error = e; gf_dm_sess_notify_state(sess, sess->status, e); } return; } /*fetch data*/ while (1) { u32 size; #if 1 if (sess->limit_data_rate && sess->bytes_per_sec) { if (sess->bytes_per_sec>sess->limit_data_rate) { /*update state*/ u32 runtime = gf_sys_clock() - sess->window_start; sess->bytes_per_sec = (1000 * (sess->bytes_in_wnd)) / runtime; if (sess->bytes_per_sec > sess->limit_data_rate) return; } } #endif e = gf_dm_read_data(sess, sHTTP, GF_DOWNLOAD_BUFFER_SIZE, &size); if (!size || e == GF_IP_NETWORK_EMPTY) { if (!sess->total_size && (gf_sys_clock() - sess->window_start > 1000)) { sess->total_size = sess->bytes_done; gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); } return; } if (e) { gf_dm_disconnect(sess); sess->last_error = e; gf_dm_sess_notify_state(sess, sess->status, e); return; } gf_dm_data_recieved(sess, sHTTP, size); /*socket empty*/ if (size < GF_DOWNLOAD_BUFFER_SIZE) return; } }