This source file includes following definitions.
- RP_SetupSDP
- RP_SDPLoadIOD
- get_stream_type_from_hint
- RP_GetChannelESD
- RP_GetChannelOD
- RP_EmulateIOD
- RP_SetupObjects
- RP_LoadSDP
- MigrateSDP_NetIO
- RP_SaveSessionState
#include "rtp_in.h"
#include <gpac/internal/ietf_dev.h>
#ifndef GPAC_DISABLE_STREAMING
GF_Err RP_SetupSDP(RTPClient *rtp, GF_SDPInfo *sdp, RTPStream *stream)
{
GF_Err e;
GF_SDPMedia *media;
Double Start, End;
u32 i;
char *sess_ctrl, *session_id, *url;
GF_X_Attribute *att;
GF_RTSPRange *range;
RTPStream *ch;
RTSPSession *migrate_sess = NULL;
Start = 0.0;
End = -1.0;
sess_ctrl = NULL;
range = NULL;
session_id = url = NULL;
i=0;
while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) {
if (!strcmp(att->Name, "control") && att->Value) sess_ctrl = att->Value;
else if (!strcmp(att->Name, "range") && !range) range = gf_rtsp_range_parse(att->Value);
else if (!strcmp(att->Name, "x-session-name")) url = att->Value;
else if (!strcmp(att->Name, "x-session-id")) session_id = att->Value;
else if (!strcmp(att->Name, "group") && !strncmp(att->Value, "DDP", 3)) rtp->is_scalable = GF_TRUE;
}
if (range) {
Start = range->start;
End = range->end;
gf_rtsp_range_del(range);
}
if (url) {
migrate_sess = RP_NewSession(rtp, url);
if (migrate_sess && session_id) {
migrate_sess->session_id = gf_strdup(session_id);
}
sess_ctrl = url;
}
i=0;
while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) {
ch = RP_NewStream(rtp, media, sdp, stream);
if (!ch) continue;
e = RP_AddStream(rtp, ch, sess_ctrl);
if (e) {
RP_DeleteStream(ch);
return e;
}
if (!(ch->flags & RTP_HAS_RANGE)) {
ch->range_start = Start;
ch->range_end = End;
if (End > 0) ch->flags |= RTP_HAS_RANGE;
}
if (ch->rtsp) {
switch (ch->depacketizer->sl_map.StreamType) {
case GF_STREAM_VISUAL:
case GF_STREAM_AUDIO:
if ((rtp->transport_mode==1) && ! (ch->rtsp->flags & RTSP_FORCE_INTER) ) {
gf_rtsp_set_buffer_size(ch->rtsp->session, RTSP_TCP_BUFFER_SIZE);
ch->rtsp->flags |= RTSP_FORCE_INTER;
}
break;
default:
if (rtp->transport_mode && ! (ch->rtsp->flags & RTSP_FORCE_INTER) ) {
gf_rtsp_set_buffer_size(ch->rtsp->session, RTSP_TCP_BUFFER_SIZE);
ch->rtsp->flags |= RTSP_FORCE_INTER;
}
break;
}
}
}
return GF_OK;
}
GF_Err RP_SDPLoadIOD(RTPClient *rtp, char *iod_str)
{
char buf[2000];
u32 size;
if (rtp->session_desc) return GF_SERVICE_ERROR;
iod_str += 1;
if (!strnicmp(iod_str, "data:application/mpeg4-iod;base64", strlen("data:application/mpeg4-iod;base64"))) {
char *buf64;
u32 size64;
buf64 = strstr(iod_str, ",");
if (!buf64) return GF_URL_ERROR;
buf64 += 1;
size64 = (u32) strlen(buf64) - 1;
size = gf_base64_decode(buf64, size64, buf, 2000);
if (!size) return GF_SERVICE_ERROR;
} else if (!strnicmp(iod_str, "data:application/mpeg4-iod;base16", strlen("data:application/mpeg4-iod;base16"))) {
char *buf16;
u32 size16;
buf16 = strstr(iod_str, ",");
if (!buf16) return GF_URL_ERROR;
buf16 += 1;
size16 = (u32) strlen(buf16) - 1;
size = gf_base16_decode(buf16, size16, buf, 2000);
if (!size) return GF_SERVICE_ERROR;
} else {
return GF_NOT_SUPPORTED;
}
gf_odf_desc_read(buf, size, &rtp->session_desc);
return GF_OK;
}
static u32 get_stream_type_from_hint(u32 ht)
{
switch (ht) {
case GF_MEDIA_OBJECT_VIDEO:
return GF_STREAM_VISUAL;
case GF_MEDIA_OBJECT_AUDIO:
return GF_STREAM_AUDIO;
case GF_MEDIA_OBJECT_TEXT:
return GF_STREAM_TEXT;
default:
return 0;
}
}
static GF_ESD *RP_GetChannelESD(RTPStream *ch, u32 ch_idx)
{
GF_ESD *esd;
if (!ch->ES_ID) ch->ES_ID = ch_idx + 1;
if (!ch->depacketizer) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("RTP Stream channel %u has no depacketizer\n", ch_idx));
return NULL;
}
esd = gf_odf_desc_esd_new(0);
esd->slConfig->timestampResolution = gf_rtp_get_clockrate(ch->rtp_ch);
esd->slConfig->useRandomAccessPointFlag = 1;
esd->slConfig->useTimestampsFlag = 1;
esd->slConfig->no_dts_signaling = ch->depacketizer->sl_map.DTSDeltaLength ? GF_FALSE : GF_TRUE;
esd->ESID = ch->ES_ID;
esd->OCRESID = 0;
if (ch->mid)
esd->has_scalable_layers = GF_TRUE;
esd->decoderConfig->streamType = ch->depacketizer->sl_map.StreamType;
esd->decoderConfig->objectTypeIndication = ch->depacketizer->sl_map.ObjectTypeIndication;
if (ch->depacketizer->sl_map.config) {
esd->decoderConfig->decoderSpecificInfo->data = (char*)gf_malloc(sizeof(char) * ch->depacketizer->sl_map.configSize);
memcpy(esd->decoderConfig->decoderSpecificInfo->data, ch->depacketizer->sl_map.config, sizeof(char) * ch->depacketizer->sl_map.configSize);
esd->decoderConfig->decoderSpecificInfo->dataLength = ch->depacketizer->sl_map.configSize;
}
if (ch->depacketizer->sl_map.rvc_predef) {
esd->decoderConfig->predefined_rvc_config = ch->depacketizer->sl_map.rvc_predef;
} else if (ch->depacketizer->sl_map.rvc_config) {
esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
esd->decoderConfig->rvc_config->data = ch->depacketizer->sl_map.rvc_config;
esd->decoderConfig->rvc_config->dataLength = ch->depacketizer->sl_map.rvc_config_size;
ch->depacketizer->sl_map.rvc_config = NULL;
ch->depacketizer->sl_map.rvc_config_size = 0;
}
return esd;
}
static GF_ObjectDescriptor *RP_GetChannelOD(RTPStream *ch, u32 ch_idx)
{
GF_ESD *esd;
GF_ObjectDescriptor *od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
esd = RP_GetChannelESD(ch, ch_idx);
od->objectDescriptorID = ch->OD_ID ? ch->OD_ID : ch->ES_ID;
gf_list_add(od->ESDescriptors, esd);
if (ch->owner->is_scalable) {
u32 i, count;
count = gf_list_count(ch->owner->channels);
for (i = 0; i < count; i++) {
RTPStream *the_channel;
GF_ESD *the_esd;
the_channel = (RTPStream *)gf_list_get(ch->owner->channels, i);
if (the_channel->base_stream == ch->mid) {
the_esd = RP_GetChannelESD(the_channel, i);
the_esd->dependsOnESID = the_channel->prev_stream;
gf_list_add(od->ESDescriptors, the_esd);
}
}
}
return od;
}
GF_Descriptor *RP_EmulateIOD(RTPClient *rtp, const char *sub_url)
{
GF_ObjectDescriptor *the_od;
RTPStream *a_str, *ch;
u32 i;
if (rtp->media_type==GF_MEDIA_OBJECT_INTERACT) return NULL;
if (rtp->media_type==GF_MEDIA_OBJECT_UPDATES) return NULL;
a_str = NULL;
if (sub_url || ((rtp->media_type != GF_MEDIA_OBJECT_SCENE) && (rtp->media_type != GF_MEDIA_OBJECT_UNDEF)) ) {
i=0;
while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) {
if (ch->depacketizer->sl_map.StreamType != get_stream_type_from_hint(rtp->media_type)) continue;
if (!sub_url || (ch->control && strstr(sub_url, ch->control)) ) {
the_od = RP_GetChannelOD(ch, i-1);
if (!the_od) continue;
return (GF_Descriptor *) the_od;
}
if (!a_str) a_str = ch;
}
if (a_str) {
the_od = RP_GetChannelOD(a_str, gf_list_find(rtp->channels, a_str) );
return (GF_Descriptor *) the_od;
}
return NULL;
}
return NULL;
}
void RP_SetupObjects(RTPClient *rtp)
{
GF_ObjectDescriptor *od;
RTPStream *ch;
u32 i;
i=0;
while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) {
if (ch->control && !strnicmp(ch->control, "data:", 5)) continue;
if (ch->prev_stream) continue;
if (!rtp->media_type) {
od = RP_GetChannelOD(ch, i);
if (!od) continue;
gf_service_declare_media(rtp->service, (GF_Descriptor*)od, GF_TRUE);
} else if (rtp->media_type==ch->depacketizer->sl_map.StreamType) {
od = RP_GetChannelOD(ch, i);
if (!od) continue;
gf_service_declare_media(rtp->service, (GF_Descriptor*)od, GF_TRUE);
rtp->media_type = 0;
break;
}
}
gf_service_declare_media(rtp->service, NULL, GF_FALSE);
}
void RP_LoadSDP(RTPClient *rtp, char *sdp_text, u32 sdp_len, RTPStream *stream)
{
GF_Err e;
u32 i;
GF_SDPInfo *sdp;
Bool is_isma_1, has_iod;
char *iod_str;
GF_X_Attribute *att;
is_isma_1 = GF_FALSE;
iod_str = NULL;
sdp = gf_sdp_info_new();
e = gf_sdp_info_parse(sdp, sdp_text, sdp_len);
if (e == GF_OK) e = RP_SetupSDP(rtp, sdp, stream);
if (!stream) {
if (e==GF_OK) {
i=0;
while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) {
if (!iod_str && !strcmp(att->Name, "mpeg4-iod") ) iod_str = att->Value;
if (!is_isma_1 && !strcmp(att->Name, "isma-compliance") ) {
if (!stricmp(att->Value, "1,1.0,1")) is_isma_1 = GF_TRUE;
}
}
if (is_isma_1) iod_str = NULL;
if (iod_str) {
RTPStream *ch;
i=0;
while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) {
if ((ch->depacketizer->payt==GF_RTP_PAYT_AMR) || (ch->depacketizer->payt==GF_RTP_PAYT_AMR_WB) ) {
iod_str = NULL;
break;
}
}
}
if (!iod_str) {
RTPStream *ch;
Bool needs_iod = GF_FALSE;
i=0;
while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) {
if (ch->depacketizer && (ch->depacketizer->payt==GF_RTP_PAYT_MPEG4) && (ch->depacketizer->sl_map.StreamType==GF_STREAM_SCENE)
) {
needs_iod = GF_TRUE;
break;
}
}
if (needs_iod) {
rtp->session_desc = (GF_Descriptor *)RP_GetChannelOD(ch, 0);
}
}
if (iod_str) e = RP_SDPLoadIOD(rtp, iod_str);
}
has_iod = rtp->session_desc ? GF_TRUE : GF_FALSE;
gf_service_connect_ack(rtp->service, NULL, e);
if (!e && !has_iod && !rtp->media_type) RP_SetupObjects(rtp);
rtp->media_type = 0;
}
else {
if (e) {
gf_service_connect_ack(rtp->service, stream->channel, e);
stream->status = RTP_Unavailable;
} else {
RP_SetupChannel(stream, NULL);
}
}
if (sdp) {
char *buf=NULL;
gf_sdp_info_write(sdp, &buf);
if (buf) {
rtp->session_state_data = (char*)gf_malloc(sizeof(char) * (strlen("data:application/sdp,") + strlen(buf) + 1) );
strcpy(rtp->session_state_data, "data:application/sdp,");
strcat(rtp->session_state_data, buf);
gf_free(buf);
}
gf_sdp_info_del(sdp);
}
}
void MigrateSDP_NetIO(void *cbk, GF_NETIO_Parameter *param)
{
RTPClient *rtp = (RTPClient *)cbk;
switch (param->msg_type) {
case GF_NETIO_GET_METHOD:
param->name = "POST";
return;
case GF_NETIO_GET_HEADER:
if (!strcmp(param->name, "POST")) {
param->name = "Content-Type";
param->value = "application/sdp";
}
return;
case GF_NETIO_GET_CONTENT:
param->data = rtp->session_state_data + strlen("data:application/sdp,");
param->size = (u32) strlen(param->data);
return;
default:
return;
}
}
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 = (RTPStream*)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;
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);
if (!att) {
GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Failed to save stream state for channel\n"));
continue;
}
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;
}
}
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);
if (!att) {
GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Failed to save session ID\n"));
} else {
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);
if (!att) {
GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Failed to save session name\n"));
} else {
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 = (char*)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_service_get_interface(rtp->service), "Streaming", "SessionMigrationServer");
if (opt) {
if (rtp->dnload) gf_service_download_del(rtp->dnload);
rtp->dnload = NULL;
if (strnicmp(opt, "http://", 7)) {
rtp->dnload = gf_service_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_service_download_del(rtp->dnload);
rtp->dnload = NULL;
} else {
FILE *f = gf_fopen(opt, "wt");
if (f) {
sdp_buf = rtp->session_state_data + strlen("data:application/sdp,");
gf_fwrite(sdp_buf, 1, strlen(sdp_buf), f);
gf_fclose(f);
} else {
e = GF_IO_ERR;
}
}
if (sess && sess->owner && (e<0)) {
RP_SendMessage(sess->owner->service, e, "Error saving session state");
}
}
}
#endif