This source file includes following definitions.
- RT_LoadPrefs
- RP_cleanup
- RP_Thread
- RP_RegisterMimeTypes
- RP_CanHandleURL
- Satip_GetServerIP
- RP_ConnectServiceEx
- RP_ConnectService
- RP_FlushCommands
- RP_CloseService
- RP_GetServiceDesc
- RP_ConnectChannel
- RP_DisconnectChannel
- gf_rtp_switch_quality
- RP_ServiceCommand
- RP_ChannelGetSLP
- RP_ChannelReleaseSLP
- RP_CanHandleURLInService
- RTP_Load
- RTP_Delete
- QueryInterfaces
- LoadInterface
- ShutdownInterface
#include "rtp_in.h"
#ifndef GPAC_DISABLE_STREAMING
#define RTP_DEFAULT_TIMEOUT 10000
#define RTSP_DEFAULT_TIMEOUT 2000
static void RT_LoadPrefs(GF_InputService *plug, RTPClient *rtp)
{
const char *sOpt;
sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "DefaultPort");
if (sOpt) {
rtp->default_port = atoi(sOpt);
} else {
rtp->default_port = 554;
}
if ((rtp->default_port == 80) || (rtp->default_port == 8080))
gf_modules_set_option((GF_BaseInterface *)plug, "Streaming", "RTPoverRTSP", "yes");
sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "RTPoverRTSP");
if (sOpt && !stricmp(sOpt, "yes")) {
rtp->transport_mode = 1;
} else if (sOpt && !stricmp(sOpt, "OnlyCritical")) {
rtp->transport_mode = 2;
} else {
rtp->transport_mode = 0;
}
sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "UDPNotAvailable");
if (sOpt && !stricmp(sOpt, "yes")) {
if (!rtp->transport_mode) rtp->transport_mode = 1;
gf_modules_set_option((GF_BaseInterface *)plug, "Network", "UDPNotAvailable", "no");
}
if (!rtp->transport_mode) {
sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "UDPTimeout");
if (sOpt ) {
rtp->udp_time_out = atoi(sOpt);
} else {
rtp->udp_time_out = RTP_DEFAULT_TIMEOUT;
}
}
sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "RTSPTimeout");
if (sOpt ) {
rtp->time_out = atoi(sOpt);
} else {
rtp->time_out = RTSP_DEFAULT_TIMEOUT;
}
sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "FirstPacketDrop");
if (sOpt) {
rtp->first_packet_drop = atoi(sOpt);
} else {
rtp->first_packet_drop = 0;
}
sOpt = gf_modules_get_option((GF_BaseInterface *)plug, "Streaming", "PacketDropFrequency");
if (sOpt) {
rtp->frequency_drop = atoi(sOpt);
} else {
rtp->frequency_drop = 0;
}
}
static void RP_cleanup(RTPClient *rtp)
{
RTSPSession *sess;
while (gf_list_count(rtp->channels)) {
RTPStream *ch = (RTPStream *)gf_list_get(rtp->channels, 0);
gf_list_rem(rtp->channels, 0);
RP_DeleteStream(ch);
}
while ( (sess = (RTSPSession *)gf_list_last(rtp->sessions)) ) {
gf_list_rem_last(rtp->sessions);
RP_DelSession(sess);
}
if (rtp->session_desc) gf_odf_desc_del(rtp->session_desc);
rtp->session_desc = NULL;
if (rtp->sdp_temp) {
gf_free(rtp->sdp_temp->remote_url);
gf_free(rtp->sdp_temp);
}
rtp->sdp_temp = NULL;
}
u32 RP_Thread(void *param)
{
u32 i;
GF_NetworkCommand com;
RTSPSession *sess;
RTPStream *ch;
RTPClient *rtp = (RTPClient *)param;
rtp->th_state = 1;
com.command_type = GF_NET_CHAN_BUFFER_QUERY;
while (rtp->th_state) {
gf_mx_p(rtp->mx);
i=0;
while ((ch = (RTPStream *)gf_list_enum(rtp->channels, &i))) {
if ((ch->flags & RTP_EOS) || (ch->status!=RTP_Running) ) continue;
if (ch->flags & RTP_INTERLEAVED) {
com.base.on_channel = ch->channel;
gf_service_command(rtp->service, &com, GF_OK);
if (!com.buffer.max) com.buffer.max = 3000;
if (com.buffer.occupancy <= com.buffer.max) ch->rtsp->flags |= RTSP_TCP_FLUSH;
} else {
RP_ReadStream(ch);
}
}
i=0;
while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) {
RP_ProcessCommands(sess);
if (sess->connect_error) {
gf_service_connect_ack(sess->owner->service, NULL, sess->connect_error);
sess->connect_error = GF_OK;
}
}
gf_mx_v(rtp->mx);
gf_sleep(1);
}
if (rtp->dnload) gf_service_download_del(rtp->dnload);
rtp->dnload = NULL;
rtp->th_state = 2;
return 0;
}
static const char * sdp_mime = "application/sdp";
static const char * sdp_exts = "sdp";
static const char * sdp_desc = "OnDemand Media/Multicast Session";
static u32 RP_RegisterMimeTypes(const GF_InputService *plug) {
if (!plug)
return 0;
gf_service_register_mime(plug, sdp_mime, sdp_exts, sdp_desc);
return 1;
}
static Bool RP_CanHandleURL(GF_InputService *plug, const char *url)
{
char *sExt;
if (!plug || !url)
return GF_FALSE;
sExt = strrchr(url, '.');
if (sExt && gf_service_check_mime_register(plug, sdp_mime, sdp_exts, sdp_desc, sExt)) return GF_TRUE;
if (strstr(url, "data:application/sdp")) return GF_TRUE;
if (strstr(url, "data:application/mpeg4-od-au;base64") ||
strstr(url, "data:application/mpeg4-bifs-au;base64") ||
strstr(url, "data:application/mpeg4-es-au;base64")) return GF_TRUE;
if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8) || !strnicmp(url, "rtp://", 6) || !strnicmp(url, "satip://", 6))
return GF_TRUE;
return GF_FALSE;
}
static void Satip_GetServerIP(const char *sURL, char *Server)
{
char schema[10], *test, text[1024], *retest;
u32 i, len;
Bool is_ipv6;
strcpy(Server, "");
i = 0;
while (i <= strlen(sURL)) {
if (sURL[i] == ':')
goto found;
schema[i] = sURL[i];
i += 1;
}
return;
found:
schema[i] = 0;
if (stricmp(schema, "satip")) {
GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP] Wrong SATIP schema %s - not setting up\n", schema));
return;
}
test = strstr(sURL, "://");
test += 3;
retest = strstr(test, "/");
retest = strrchr(test, ':');
if (retest && strchr(retest, ']')) retest = NULL;
if (retest && strstr(retest, "/")) {
retest += 1;
i = 0;
while (i<strlen(retest)) {
if (retest[i] == '/') break;
text[i] = retest[i];
i += 1;
}
text[i] = 0;
}
is_ipv6 = GF_FALSE;
len = (u32)strlen(test);
i = 0;
while (i<len) {
if (test[i] == '[') is_ipv6 = GF_TRUE;
else if (test[i] == ']') is_ipv6 = GF_FALSE;
if ((test[i] == '/') || (!is_ipv6 && (test[i] == ':'))) break;
text[i] = test[i];
i += 1;
}
text[i] = 0;
strcpy(Server, text);
}
GF_Err RP_ConnectServiceEx(GF_InputService *plug, GF_ClientService *serv, const char *url, Bool skip_migration)
{
char *session_cache;
RTSPSession *sess;
RTPClient *priv = (RTPClient *)plug->priv;
priv->service = serv;
if (priv->dnload) gf_service_download_del(priv->dnload);
priv->dnload = NULL;
GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Opening service %s\n", url));
RT_LoadPrefs(plug, priv);
gf_th_run(priv->th, RP_Thread, priv);
if (!skip_migration) {
session_cache = (char *) gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationFile");
if (session_cache && session_cache[0]) {
FILE *f = gf_fopen(session_cache, "rb");
if (f) {
gf_fclose(f);
GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Restarting RTSP session from %s\n", session_cache));
RP_FetchSDP(priv, (char *) session_cache, NULL, (char *) url);
return GF_OK;
}
if (!strncmp(session_cache, "http://", 7)) {
GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Restarting RTSP session from %s\n", session_cache));
RP_FetchSDP(priv, (char *) session_cache, NULL, (char *) url);
return GF_OK;
}
}
}
if (strstr(url, "data:application/sdp") || (strnicmp(url, "rtsp", 4) && strstr(url, ".sdp")) ) {
RP_FetchSDP(priv, (char *) url, NULL, NULL);
return GF_OK;
}
if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8) || !strnicmp(url, "satip://", 8)) {
char *the_url = gf_strdup(url);
char *the_ext = strrchr(the_url, '#');
if (the_ext) {
if (!stricmp(the_ext, "#audio")) priv->media_type = GF_MEDIA_OBJECT_AUDIO;
else if (!stricmp(the_ext, "#video")) priv->media_type = GF_MEDIA_OBJECT_VIDEO;
the_ext[0] = 0;
}
sess = RP_NewSession(priv, (char *) the_url);
if (!strnicmp(url, "satip://", 8)) {
sess->satip = GF_TRUE;
sess->satip_server = gf_malloc(GF_MAX_PATH);
Satip_GetServerIP(url, sess->satip_server);
}
gf_free(the_url);
if (!sess) {
gf_service_connect_ack(serv, NULL, GF_NOT_SUPPORTED);
} else {
RP_Describe(sess, 0, NULL);
}
return GF_OK;
}
gf_service_connect_ack(serv, NULL, GF_OK);
RP_SetupObjects(priv);
return GF_OK;
}
GF_Err RP_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url)
{
return RP_ConnectServiceEx(plug, serv, url, GF_FALSE);
}
static void RP_FlushCommands(RTPClient *rtp)
{
u32 i, nb_com;
RTSPSession *sess;
while (1) {
nb_com = 0;
i=0;
while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) {
if (!sess->connect_error)
nb_com += gf_list_count(sess->rtsp_commands);
}
if (!nb_com) break;
gf_sleep(10);
}
}
static GF_Err RP_CloseService(GF_InputService *plug)
{
u32 i;
const char *opt;
RTSPSession *sess;
RTPClient *rtp = (RTPClient *)plug->priv;
GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Closing service\n"));
RP_FlushCommands(rtp);
if (rtp->session_migration) {
opt = gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationPause");
if (opt && !strcmp(opt, "yes")) {
GF_NetworkCommand com;
com.command_type = GF_NET_CHAN_PAUSE;
com.base.on_channel = NULL;
i=0;
while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) {
RP_UserCommand(sess, NULL, &com);
}
}
RP_SaveSessionState(rtp);
} else {
if (rtp->session_state_data) {
gf_free(rtp->session_state_data);
rtp->session_state_data = NULL;
}
i=0;
while ((sess = (RTSPSession *)gf_list_enum(rtp->sessions, &i))) {
RP_Teardown(sess, NULL);
}
}
RP_FlushCommands(rtp);
if (rtp->th_state==1) rtp->th_state = 0;
gf_service_disconnect_ack(rtp->service, NULL, GF_OK);
return GF_OK;
}
static GF_Descriptor *RP_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url)
{
GF_Descriptor *desc;
RTSPSession *sess = NULL;
RTPClient *priv = (RTPClient *)plug->priv;
GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Fetching service descriptor\n"));
sess = gf_list_get(priv->sessions, 0);
if (sess && sess->satip) {
RTPStream *ch = gf_list_get(priv->channels, 0);
if (!ch) return NULL;
return ch->satip_m2ts_ifce->GetServiceDescriptor(ch->satip_m2ts_ifce, expect_type, sub_url);
}
if ((expect_type!=GF_MEDIA_OBJECT_UNDEF) && (expect_type!=GF_MEDIA_OBJECT_SCENE) && (expect_type!=GF_MEDIA_OBJECT_UPDATES)) {
if (priv->session_desc) gf_odf_desc_del(priv->session_desc);
priv->session_desc = NULL;
priv->media_type = expect_type;
return RP_EmulateIOD(priv, sub_url);
}
desc = priv->session_desc;
priv->session_desc = NULL;
return desc;
}
static GF_Err RP_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream)
{
u32 ESID, i;
RTPStream *ch, *next_ch;
RTSPSession *sess = NULL;
char *es_url;
RTPClient *priv = (RTPClient *)plug->priv;
if (upstream) return GF_NOT_SUPPORTED;
GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Connecting channel @%08x - %s\n", channel, url));
sess = gf_list_get(priv->sessions, 0);
if (sess && sess->satip) {
RTPStream *ch = gf_list_get(priv->channels, 0);
if (!ch) return GF_SERVICE_ERROR;
return ch->satip_m2ts_ifce->ConnectChannel(ch->satip_m2ts_ifce, channel, url, upstream);
}
ch = RP_FindChannel(priv, channel, 0, (char *) url, GF_FALSE);
if (ch && (ch->status != RTP_Disconnected) ) return GF_SERVICE_ERROR;
es_url = NULL;
sess = NULL;
if (strstr(url, "ES_ID=")) {
sscanf(url, "ES_ID=%ud", &ESID);
ch = RP_FindChannel(priv, NULL, ESID, NULL, GF_FALSE);
if (!ch) return GF_STREAM_NOT_FOUND;
ch->next_stream = 0;
for (i = 0; i < gf_list_count(priv->channels); i++) {
next_ch = (RTPStream *)gf_list_get(priv->channels, i);
if (next_ch->prev_stream == ch->mid)
{
ch->next_stream = next_ch->mid;
break;
}
}
ch->channel = channel;
sess = ch->rtsp;
}
else if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8) || !strnicmp(url, "satip://", 8)) {
sess = RP_CheckSession(priv, (char *) url);
if (!sess) sess = RP_NewSession(priv, (char *) url);
es_url = (char *) url;
}
else if (strstr(url, "data:application/mpeg4-od-au;base64")
|| strstr(url, "data:application/mpeg4-bifs-au;base64")
|| strstr(url, "data:application/mpeg4-es-au;base64")
) {
GF_SAFEALLOC(ch, RTPStream);
if (!ch) return GF_OUT_OF_MEM;
ch->control = gf_strdup(url);
ch->owner = priv;
ch->channel = channel;
ch->status = RTP_Connected;
gf_list_add(priv->channels, ch);
RP_ConfirmChannelConnect(ch, GF_OK);
return GF_OK;
}
if (ch && (ch->status==RTP_SessionResume)) {
ch->flags |= RTP_CONNECTED;
RP_InitStream(ch, GF_FALSE);
RP_ConfirmChannelConnect(ch, GF_OK);
return GF_OK;
}
if (sess) RP_Describe(sess, es_url, channel);
else RP_ConfirmChannelConnect(ch, GF_OK);
return GF_OK;
}
static GF_Err RP_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel)
{
RTPStream *ch;
RTPClient *priv = (RTPClient *)plug->priv;
RTSPSession *sess = NULL;
GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Disconnecting channel @%08x\n", channel));
sess = gf_list_get(priv->sessions, 0);
if (sess && sess->satip) {
RTPStream *ch = gf_list_get(priv->channels, 0);
if (!ch) return GF_SERVICE_ERROR;
return ch->satip_m2ts_ifce->DisconnectChannel(ch->satip_m2ts_ifce, channel);
}
ch = RP_FindChannel(priv, channel, 0, NULL, GF_FALSE);
if (!ch) return GF_STREAM_NOT_FOUND;
gf_mx_p(priv->mx);
ch->flags &= ~RTP_CONNECTED;
ch->channel = NULL;
gf_mx_v(priv->mx);
gf_service_disconnect_ack(priv->service, channel, GF_OK);
return GF_OK;
}
static void gf_rtp_switch_quality(RTPClient *rtp, Bool switch_up)
{
u32 i,count;
RTPStream *ch, *cur_ch;
GF_NetworkCommand com;
count = gf_list_count(rtp->channels);
ch = cur_ch = NULL;
for (i = 0; i < count; i++) {
cur_ch = (RTPStream *) gf_list_get(rtp->channels, i);
if (cur_ch->mid != rtp->cur_mid) {
cur_ch=NULL;
continue;
}
break;
}
if (!cur_ch) return;
if (switch_up)
{
if (!cur_ch->next_stream)
{
cur_ch->status = RTP_Running;
return;
}
else
{
for (i = 0; i < count; i++) {
ch = (RTPStream *) gf_list_get(rtp->channels, i);
if (ch->mid == cur_ch->next_stream)
{
gf_mx_p(rtp->mx);
RP_InitStream(ch, GF_FALSE);
gf_mx_v(rtp->mx);
ch->status = RTP_Running;
rtp->cur_mid = ch->mid;
break;
}
}
}
}
else
{
if (!cur_ch->prev_stream)
{
cur_ch->status = RTP_Running;
return;
}
else
{
for (i = 0; i < count; i++) {
ch = (RTPStream *) gf_list_get(rtp->channels, i);
if (ch->mid == cur_ch->prev_stream)
{
gf_rtp_stop(cur_ch->rtp_ch);
cur_ch->status = RTP_Connected;
com.command_type = GF_NET_CHAN_RESET;
com.base.on_channel = cur_ch;
gf_service_command(rtp->service, &com, GF_OK);
rtp->cur_mid = ch->mid;
break;
}
}
}
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("Switch from ES%d to ES %d\n", cur_ch->mid, ch->mid));
return;
}
static GF_Err RP_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com)
{
RTPStream *ch;
RTPClient *priv = (RTPClient *)plug->priv;
RTSPSession *sess = NULL;
sess = gf_list_get(priv->sessions, 0);
if (sess && sess->satip) {
RTPStream *ch = gf_list_get(priv->channels, 0);
if (!ch) return GF_SERVICE_ERROR;
return ch->satip_m2ts_ifce->ServiceCommand(ch->satip_m2ts_ifce, com);
}
if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) {
u32 i;
for (i=0; i<gf_list_count(priv->channels); i++) {
ch = (RTPStream*)gf_list_get(priv->channels, i);
if (ch->depacketizer->sl_map.StreamType==GF_STREAM_AUDIO)
return GF_OK;
}
return GF_NOT_SUPPORTED;
}
if (com->command_type==GF_NET_SERVICE_MIGRATION_INFO) {
RP_SaveSessionState(priv);
priv->session_migration = GF_TRUE;
if (priv->session_state_data) {
com->migrate.data = priv->session_state_data;
com->migrate.data_len = (u32) strlen(priv->session_state_data);
return GF_OK;
}
return GF_NOT_SUPPORTED;
}
if (com->command_type == GF_NET_SERVICE_QUALITY_SWITCH)
{
gf_rtp_switch_quality(priv, com->switch_quality.up);
return GF_OK;
}
if (!com->base.on_channel) {
if (com->command_type==GF_NET_IS_CACHABLE) return GF_OK;
return GF_NOT_SUPPORTED;
}
ch = RP_FindChannel(priv, com->base.on_channel, 0, NULL, GF_FALSE);
if (!ch) return GF_STREAM_NOT_FOUND;
switch (com->command_type) {
case GF_NET_CHAN_SET_PULL:
if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_NOT_SUPPORTED;
if (strstr(ch->control, "data:application/")) return GF_OK;
return GF_NOT_SUPPORTED;
case GF_NET_CHAN_INTERACTIVE:
if (!ch->control) return GF_NOT_SUPPORTED;
else if (ch->flags & RTP_FORCE_BROADCAST) return GF_NOT_SUPPORTED;
else if (ch->flags & RTP_HAS_RANGE) return GF_OK;
else if (strstr(ch->control, "application")) return GF_OK;
return GF_NOT_SUPPORTED;
case GF_NET_CHAN_BUFFER:
if (!(ch->rtp_ch || ch->rtsp || !ch->control)) {
com->buffer.max = com->buffer.min = 0;
}
return GF_OK;
case GF_NET_CHAN_DURATION:
com->duration.duration = (ch->flags & RTP_HAS_RANGE) ? (ch->range_end - ch->range_start) : 0;
return GF_OK;
case GF_NET_CHAN_CONFIG:
if (com->cfg.frame_duration) ch->depacketizer->sl_hdr.au_duration = com->cfg.frame_duration;
ch->ts_res = com->cfg.sl_config.timestampResolution;
return GF_OK;
case GF_NET_CHAN_PLAY:
GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Processing play on channel @%08x - %s\n", ch, ch->rtsp ? "RTSP control" : "No control (RTP)" ));
ch->flags &= ~RTP_EOS;
if (ch->rtsp) {
if (ch->status==RTP_SessionResume) {
const char *opt = gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationPause");
if (opt && !strcmp(opt, "yes")) {
ch->status = RTP_Connected;
com->play.start_range = ch->current_start;
} else {
ch->status = RTP_Running;
return GF_OK;
}
}
RP_UserCommand(ch->rtsp, ch, com);
} else {
ch->status = RTP_Running;
if (!ch->next_stream)
priv->cur_mid = ch->mid;
if (ch->rtp_ch) {
ch->rtcp_init = GF_FALSE;
gf_mx_p(priv->mx);
RP_InitStream(ch, (ch->flags & RTP_CONNECTED) ? GF_TRUE : GF_FALSE);
gf_mx_v(priv->mx);
gf_rtp_set_info_rtp(ch->rtp_ch, 0, 0, 0);
} else {
ch->current_start = com->play.start_range;
ch->flags |= GF_RTP_NEW_AU;
gf_rtp_depacketizer_reset(ch->depacketizer, GF_FALSE);
}
}
return GF_OK;
case GF_NET_CHAN_STOP:
if (ch->rtsp) {
if (! ch->owner->session_migration) {
RP_UserCommand(ch->rtsp, ch, com);
}
} else {
ch->status = RTP_Connected;
ch->owner->last_ntp = 0;
}
ch->rtcp_init = GF_FALSE;
return GF_OK;
case GF_NET_CHAN_SET_SPEED:
case GF_NET_CHAN_PAUSE:
case GF_NET_CHAN_RESUME:
assert(ch->rtsp);
RP_UserCommand(ch->rtsp, ch, com);
return GF_OK;
case GF_NET_CHAN_GET_DSI:
if (ch->depacketizer && ch->depacketizer->sl_map.configSize) {
com->get_dsi.dsi_len = ch->depacketizer->sl_map.configSize;
com->get_dsi.dsi = (char*)gf_malloc(sizeof(char)*com->get_dsi.dsi_len);
memcpy(com->get_dsi.dsi, ch->depacketizer->sl_map.config, sizeof(char)*com->get_dsi.dsi_len);
} else {
com->get_dsi.dsi = NULL;
com->get_dsi.dsi_len = 0;
}
return GF_OK;
case GF_NET_GET_STATS:
memset(&com->net_stats, 0, sizeof(GF_NetComStats));
if (ch->rtp_ch) {
u32 time;
Float bps;
com->net_stats.pck_loss_percentage = gf_rtp_get_loss(ch->rtp_ch);
if (ch->flags & RTP_INTERLEAVED) {
com->net_stats.multiplex_port = gf_rtsp_get_session_port(ch->rtsp->session);
com->net_stats.port = gf_rtp_get_low_interleave_id(ch->rtp_ch);
com->net_stats.ctrl_port = gf_rtp_get_hight_interleave_id(ch->rtp_ch);
} else {
com->net_stats.multiplex_port = 0;
gf_rtp_get_ports(ch->rtp_ch, &com->net_stats.port, &com->net_stats.ctrl_port);
}
if (ch->stat_stop_time) {
time = ch->stat_stop_time - ch->stat_start_time;
} else {
time = gf_sys_clock() - ch->stat_start_time;
}
bps = 8.0f * ch->rtp_bytes;
bps *= 1000;
bps /= time;
com->net_stats.bw_down = (u32) bps;
bps = 8.0f * ch->rtcp_bytes;
bps *= 1000;
bps /= time;
com->net_stats.ctrl_bw_down = (u32) bps;
bps = 8.0f * gf_rtp_get_tcp_bytes_sent(ch->rtp_ch);
bps *= 1000;
bps /= time;
com->net_stats.ctrl_bw_up = (u32) bps;
}
return GF_OK;
default:
break;
}
return GF_NOT_SUPPORTED;
}
static GF_Err RP_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data)
{
char *data;
RTPStream *ch;
RTPClient *priv = (RTPClient *)plug->priv;
ch = RP_FindChannel(priv, channel, 0, NULL, GF_FALSE);
if (!ch) return GF_STREAM_NOT_FOUND;
if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_SERVICE_ERROR;
if (ch->status != RTP_Running) return GF_SERVICE_ERROR;
data = strstr(ch->control, ";base64");
if (!data) return GF_SERVICE_ERROR;
if (ch->current_start>=0) {
*sl_compressed = GF_FALSE;
memset(out_sl_hdr, 0, sizeof(GF_SLHeader));
out_sl_hdr->accessUnitEndFlag = 1;
out_sl_hdr->accessUnitStartFlag = 1;
out_sl_hdr->compositionTimeStamp = (u64) (ch->current_start * ch->ts_res);
out_sl_hdr->compositionTimeStampFlag = 1;
out_sl_hdr->randomAccessPointFlag = 1;
*out_reception_status = GF_OK;
*is_new_data = (ch->flags & GF_RTP_NEW_AU) ? GF_TRUE : GF_FALSE;
data = strstr(data, ",");
data += 1;
*out_data_size = gf_base64_decode(data, (u32) strlen(data), ch->buffer, RTP_BUFFER_SIZE);
*out_data_ptr = ch->buffer;
ch->flags &= ~GF_RTP_NEW_AU;
} else {
*out_data_ptr = NULL;
*out_data_size = 0;
*out_reception_status = GF_EOS;
ch->flags |= RTP_EOS;
}
return GF_OK;
}
static GF_Err RP_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel)
{
RTPStream *ch;
RTPClient *priv = (RTPClient *)plug->priv;
ch = RP_FindChannel(priv, channel, 0, NULL, GF_FALSE);
if (!ch) return GF_STREAM_NOT_FOUND;
if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_SERVICE_ERROR;
if (ch->status != RTP_Running) return GF_SERVICE_ERROR;
ch->current_start = -1.0;
return GF_OK;
}
static Bool RP_CanHandleURLInService(GF_InputService *plug, const char *url)
{
RTSPSession *sess;
RTPClient *priv = (RTPClient *)plug->priv;
if (strstr(url, "data:application/mpeg4-od-au;base64")
|| strstr(url, "data:application/mpeg4-bifs-au;base64")
|| strstr(url, "data:application/mpeg4-es-au;base64")
) return GF_TRUE;
if (url[0]=='#') {
Bool st_type = GF_FALSE;
if (!stricmp(url, "#video")) st_type = GF_STREAM_VISUAL;
else if (!stricmp(url, "#audio")) st_type = GF_STREAM_AUDIO;
if (st_type) {
u32 i=0;
RTPStream *st;
while ((st = (RTPStream *)gf_list_enum(priv->channels, &i))) {
if (st->depacketizer && (st->depacketizer->sl_map.StreamType==st_type))
return GF_TRUE;
}
}
return GF_FALSE;
} else if (!RP_CanHandleURL(plug, url)) {
return GF_FALSE;
}
sess = RP_CheckSession(priv, (char *) url);
if (sess) return GF_TRUE;
return GF_FALSE;
}
GF_InputService *RTP_Load()
{
RTPClient *priv;
GF_InputService *plug;
GF_SAFEALLOC(plug, GF_InputService);
if (!plug) return NULL;
GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC RTP/RTSP Client", "gpac distribution")
GF_SAFEALLOC(priv, RTPClient);
if (!priv) {
gf_free(plug);
return NULL;
}
plug->CanHandleURL = RP_CanHandleURL;
plug->CanHandleURLInService = RP_CanHandleURLInService;
plug->ConnectService = RP_ConnectService;
plug->CloseService = RP_CloseService;
plug->GetServiceDescriptor = RP_GetServiceDesc;
plug->ConnectChannel = RP_ConnectChannel;
plug->DisconnectChannel = RP_DisconnectChannel;
plug->ServiceCommand = RP_ServiceCommand;
plug->RegisterMimeTypes = RP_RegisterMimeTypes;
plug->ChannelGetSLP = RP_ChannelGetSLP;
plug->ChannelReleaseSLP = RP_ChannelReleaseSLP;
priv->sessions = gf_list_new();
priv->channels = gf_list_new();
plug->priv = priv;
priv->time_out = RTSP_DEFAULT_TIMEOUT;
priv->mx = gf_mx_new("RTPDemux");
priv->th = gf_th_new("RTPDemux");
return plug;
}
void RTP_Delete(GF_BaseInterface *bi)
{
RTPClient *rtp;
u32 retry;
GF_InputService *plug = (GF_InputService *) bi;
rtp = (RTPClient *)plug->priv;
if (rtp->th_state==1) rtp->th_state = 0;
retry = 20;
while ((rtp->th_state==1) && retry) {
gf_sleep(10);
retry--;
}
assert(retry);
if (rtp->session_state_data) gf_free(rtp->session_state_data);
RP_cleanup(rtp);
gf_th_del(rtp->th);
gf_mx_del(rtp->mx);
gf_list_del(rtp->sessions);
gf_list_del(rtp->channels);
gf_free(rtp);
gf_free(bi);
}
#endif
GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
static u32 si [] = {
#ifndef GPAC_DISABLE_STREAMING
GF_NET_CLIENT_INTERFACE,
#endif
0
};
return si;
}
GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
#ifndef GPAC_DISABLE_STREAMING
if (InterfaceType == GF_NET_CLIENT_INTERFACE) return (GF_BaseInterface *)RTP_Load();
#endif
return NULL;
}
GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
switch (ifce->InterfaceType) {
#ifndef GPAC_DISABLE_STREAMING
case GF_NET_CLIENT_INTERFACE:
RTP_Delete(ifce);
break;
#endif
}
}
GPAC_MODULE_STATIC_DECLARATION( rtp_in )