void RP_SaveSessionState(RTPClient *rtp) { GF_Err e; char *sdp_buf; const char *opt; GF_X_Attribute*att; u32 i, j; GF_SDPInfo *sdp; RTSPSession *sess = NULL; if (!rtp->session_state_data) return; sdp_buf = rtp->session_state_data + strlen("data:application/sdp,"); sdp = gf_sdp_info_new(); e = gf_sdp_info_parse(sdp, sdp_buf, (u32) strlen(sdp_buf) ); for (i=0; i<gf_list_count(rtp->channels); i++) { GF_SDPMedia *media = NULL; RTPStream *ch = gf_list_get(rtp->channels, i); if (!ch->control) continue; for (j=0; j<gf_list_count(sdp->media_desc); j++) { u32 k; GF_SDPMedia *med = (GF_SDPMedia*)gf_list_get(sdp->media_desc, j); for (k=0; k<gf_list_count(med->Attributes); k++) { att = (GF_X_Attribute*)gf_list_get(med->Attributes, k); if (!stricmp(att->Name, "control") && (strstr(att->Value, ch->control)!=NULL) ) { media = med; break; } } if (media) break; } if (!media) continue; if (ch->rtp_ch->net_info.IsUnicast) { char szPorts[4096]; u16 porta, portb; media->PortNumber = ch->rtp_ch->net_info.client_port_first; /*remove x-server-port extension*/ for (j=0; j<gf_list_count(media->Attributes); j++) { att = (GF_X_Attribute*)gf_list_get(media->Attributes, j); if (!stricmp(att->Name, "x-stream-state") ) { gf_free(att->Name); gf_free(att->Value); gf_free(att); gf_list_rem(media->Attributes, j); } } ch->current_start += gf_rtp_get_current_time(ch->rtp_ch); GF_SAFEALLOC(att, GF_X_Attribute); att->Name = gf_strdup("x-stream-state"); porta = ch->rtp_ch->net_info.port_first ? ch->rtp_ch->net_info.port_first : ch->rtp_ch->net_info.client_port_first; portb = ch->rtp_ch->net_info.port_last ? ch->rtp_ch->net_info.port_last : ch->rtp_ch->net_info.client_port_last; sprintf(szPorts, "server-port=%d-%d;ssrc=%X;npt=%g;seq=%d;rtptime=%d", porta, portb, ch->rtp_ch->SenderSSRC, ch->current_start, ch->rtp_ch->rtp_first_SN, ch->rtp_ch->rtp_time ); att->Value = gf_strdup(szPorts); gf_list_add(media->Attributes, att); if (ch->rtsp) sess = ch->rtsp; } else { media->PortNumber = ch->rtp_ch->net_info.port_first; } } /*remove x-server-port/x-session-id extension*/ for (j=0; j<gf_list_count(sdp->Attributes); j++) { att = (GF_X_Attribute*)gf_list_get(sdp->Attributes, j); if (!stricmp(att->Name, "x-session-id") || !stricmp(att->Name, "x-session-name") ) { gf_free(att->Name); gf_free(att->Value); gf_free(att); gf_list_rem(sdp->Attributes, j); } } if (sess) { char szURL[4096]; if (sess->session_id) { GF_SAFEALLOC(att, GF_X_Attribute); att->Name = gf_strdup("x-session-id"); att->Value = gf_strdup(sess->session_id); gf_list_add(sdp->Attributes, att); } GF_SAFEALLOC(att, GF_X_Attribute); att->Name = gf_strdup("x-session-name"); sprintf(szURL, "rtsp://%s:%d/%s", sess->session->Server, sess->session->Port, sess->session->Service); att->Value = gf_strdup(szURL); gf_list_add(sdp->Attributes, att); } gf_free(rtp->session_state_data); sdp_buf = NULL; gf_sdp_info_write(sdp, &sdp_buf); if (sdp_buf) { rtp->session_state_data = gf_malloc(sizeof(char) * (strlen("data:application/sdp,") + strlen(sdp_buf) + 1) ); strcpy(rtp->session_state_data, "data:application/sdp,"); strcat(rtp->session_state_data, sdp_buf); gf_free(sdp_buf); } gf_sdp_info_del(sdp); opt = (char *) gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(rtp->service), "Streaming", "SessionMigrationServer"); if (opt) { if (rtp->dnload) gf_term_download_del(rtp->dnload); rtp->dnload = NULL; if (strnicmp(opt, "http://", 7)) { rtp->dnload = gf_term_download_new(rtp->service, opt, GF_NETIO_SESSION_NOT_THREADED, MigrateSDP_NetIO, rtp); while (1) { char buffer[100]; u32 read; e = gf_dm_sess_fetch_data(rtp->dnload, buffer, 100, &read); if (e && (e!=GF_IP_NETWORK_EMPTY)) break; } gf_term_download_del(rtp->dnload); rtp->dnload = NULL; } else { FILE *f = gf_f64_open(opt, "wt"); if (f) { sdp_buf = rtp->session_state_data + strlen("data:application/sdp,"); gf_fwrite(sdp_buf, 1, strlen(sdp_buf), f); fclose(f); } else { e = GF_IO_ERR; } } if (e<0) { gf_term_on_message(sess->owner->service, e, "Error saving session state"); } } }
void RP_ProcessRTP(RTPStream *ch, char *pck, u32 size) { GF_NetworkCommand com; GF_Err e; GF_RTPHeader hdr; u32 PayloadStart; ch->rtp_bytes += size; /*first decode RTP*/ e = gf_rtp_decode_rtp(ch->rtp_ch, pck, size, &hdr, &PayloadStart); /*corrupted or NULL data*/ if (e || (PayloadStart >= size)) { //gf_term_on_sl_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_CORRUPTED_DATA); return; } /*if we must notify some timing, do it now. If the channel has no range, this should NEVER be called*/ if (ch->check_rtp_time /*&& gf_rtp_is_active(ch->rtp_ch)*/) { Double ch_time; /*it may happen that we still receive packets from a previous "play" request. If this is the case, filter until we reach the indicated rtptime*/ if (ch->rtp_ch->rtp_time && (ch->rtp_ch->rtp_first_SN > hdr.SequenceNumber) && (ch->rtp_ch->rtp_time < hdr.TimeStamp) ) { GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Rejecting too early packet (TS %d vs signaled rtp time %d - diff %d ms)\n", hdr.TimeStamp, ch->rtp_ch->rtp_time, ((hdr.TimeStamp - ch->rtp_ch->rtp_time)*1000) / ch->rtp_ch->TimeScale)); return; } ch_time = gf_rtp_get_current_time(ch->rtp_ch); /*this is the first packet on the channel (no PAUSE)*/ if (ch->check_rtp_time == RTP_SET_TIME_RTP) { /*Note: in a SEEK with RTSP, the rtp-info time given by the server is the rtp time of the desired range. But the server may (and should) send from the previous I frame on video, so the time of the first rtp packet after a SEEK can actually be less than CurrentStart. We don't drop these packets in order to see the maximum video. We could drop it, this would mean wait for next RAP...*/ memset(&com, 0, sizeof(com)); com.command_type = GF_NET_CHAN_MAP_TIME; com.base.on_channel = ch->channel; if (ch->rtsp) { com.map_time.media_time = ch->current_start + ch_time; } else { com.map_time.media_time = 0; } com.map_time.timestamp = hdr.TimeStamp; com.map_time.reset_buffers = 0; gf_term_on_command(ch->owner->service, &com, GF_OK); GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Mapping RTP Time seq %d TS %d Media Time %g - rtp info seq %d TS %d\n", hdr.SequenceNumber, hdr.TimeStamp, com.map_time.media_time, ch->rtp_ch->rtp_first_SN, ch->rtp_ch->rtp_time )); /*skip RTCP clock init when RTSP is used*/ if (ch->rtsp) ch->rtcp_init = 1; // if (ch->depacketizer->payt==GF_RTP_PAYT_H264_AVC) ch->depacketizer->flags |= GF_RTP_AVC_WAIT_RAP; } /*this is RESUME on channel, filter packet based on time (darwin seems to send couple of packet before) do not fetch if we're below 10 ms or <0, because this means we already have this packet - as the PAUSE is issued with the RTP currentTime*/ else if (ch_time <= 0.021) { return; } ch->check_rtp_time = RTP_SET_TIME_NONE; } gf_rtp_depacketizer_process(ch->depacketizer, &hdr, pck + PayloadStart, size - PayloadStart); /*last check: signal EOS if we're close to end range in case the server do not send RTCP BYE*/ if ((ch->flags & RTP_HAS_RANGE) && !(ch->flags & RTP_EOS) ) { /*also check last CTS*/ Double ts = (Double) ((u32) ch->depacketizer->sl_hdr.compositionTimeStamp - hdr.TimeStamp); ts /= gf_rtp_get_clockrate(ch->rtp_ch); if (ABSDIFF(ch->range_end, (ts + ch->current_start + gf_rtp_get_current_time(ch->rtp_ch)) ) < 0.2) { ch->flags |= RTP_EOS; ch->stat_stop_time = gf_sys_clock(); gf_term_on_sl_packet(ch->owner->service, ch->channel, NULL, 0, NULL, GF_EOS); } } }