GF_Err RP_InitStream(RTPStream *ch, Bool ResetOnly) { gf_rtp_depacketizer_reset(ch->depacketizer, !ResetOnly); if (!ResetOnly) { const char *ip_ifce = NULL; u32 reorder_size = 0; if (!ch->owner->transport_mode) { const char *sOpt = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Streaming", "ReorderSize"); if (sOpt) reorder_size = atoi(sOpt); else reorder_size = 10; ip_ifce = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Network", "DefaultMCastInterface"); if (!ip_ifce) { const char *mob_on = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Network", "MobileIPEnabled"); if (mob_on && !strcmp(mob_on, "yes")) { ip_ifce = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Network", "MobileIP"); ch->flags |= RTP_MOBILEIP; } } } return gf_rtp_initialize(ch->rtp_ch, RTP_BUFFER_SIZE, 0, 0, reorder_size, 200, (char *)ip_ifce); } //just reset the sockets gf_rtp_reset_buffers(ch->rtp_ch); return GF_OK; }
RTSPSession *RP_NewSession(RTPClient *rtp, char *session_control) { char *szCtrl, *szExt; RTSPSession *tmp; GF_RTSPSession *rtsp; if (!session_control) return NULL; /*little fix: some servers don't understand DESCRIBE URL/trackID=, so remove the trackID...*/ szCtrl = gf_strdup(session_control); szExt = szCtrl ? strrchr(szCtrl, '.') : NULL; if (szExt) { szExt = strchr(szExt, '/'); if (szExt) { if (!strnicmp(szExt+1, "trackID=", 8) || !strnicmp(szExt+1, "ESID=", 5) || !strnicmp(szExt+1, "ES_ID=", 6)) szExt[0] = 0; } } rtsp = gf_rtsp_session_new(szCtrl, rtp->default_port); gf_free(szCtrl); if (!rtsp) return NULL; GF_SAFEALLOC(tmp, RTSPSession); tmp->owner = rtp; tmp->session = rtsp; szCtrl = (char *)gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Network", "MobileIPEnabled"); if (szCtrl && !strcmp(szCtrl, "yes")) { char *ip = (char *)gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Network", "MobileIP"); gf_rtsp_set_mobile_ip(rtsp, ip); } if (rtp->transport_mode) { gf_rtsp_set_buffer_size(rtsp, RTSP_TCP_BUFFER_SIZE); } else { gf_rtsp_set_buffer_size(rtsp, RTSP_BUFFER_SIZE); } tmp->rtsp_commands = gf_list_new(); tmp->rtsp_rsp = gf_rtsp_response_new(); gf_list_add(rtp->sessions, tmp); return tmp; }
void SDP_NetIO(void *cbk, GF_NETIO_Parameter *param) { GF_Err e; RTPClient *rtp = (RTPClient *)cbk; SDPFetch *sdp = rtp->sdp_temp; gf_service_download_update_stats(rtp->dnload); e = param->error; switch (param->msg_type) { case GF_NETIO_GET_METHOD: if (sdp->original_url) param->name = "POST"; return; case GF_NETIO_GET_CONTENT: if (sdp->original_url) { char szBody[4096], *opt; opt = (char *) gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Network", "MobileIP"); sprintf(szBody, "ipadd\n%s\n\nurl\n%s\n\n", opt, sdp->original_url); param->data = szBody; param->size = (u32) strlen(szBody); } return; case GF_NETIO_DATA_TRANSFERED: if (sdp->original_url) { u32 sdp_size; e = gf_dm_sess_get_stats(rtp->dnload, NULL, NULL, &sdp_size, NULL, NULL, NULL); if (sdp_size) { const char *szFile = gf_dm_sess_get_cache_name(rtp->dnload); if (!szFile) { e = GF_SERVICE_ERROR; } else { e = GF_OK; RP_SDPFromFile(rtp, (char *) szFile, sdp->chan); gf_free(sdp->remote_url); if (sdp->original_url) gf_free(sdp->original_url); gf_free(sdp); rtp->sdp_temp = NULL; return; } } } break; default: if (e == GF_OK) return; } if (sdp->original_url) { char *url = sdp->original_url; gf_free(sdp->remote_url); gf_free(sdp); rtp->sdp_temp = NULL; RP_SendMessage(rtp->service, e, "Error fetching session state - restarting"); RP_ConnectServiceEx(gf_service_get_interface(rtp->service), rtp->service, url, GF_TRUE); gf_free(url); return; } /*error*/ if (sdp->chan) { gf_service_connect_ack(rtp->service, sdp->chan->channel, e); } else { gf_service_connect_ack(rtp->service, NULL, e); rtp->sdp_temp = NULL; } gf_free(sdp->remote_url); if (sdp->original_url) gf_free(sdp->original_url); gf_free(sdp); rtp->sdp_temp = NULL; }
void isor_declare_objects(ISOMReader *read) { GF_ObjectDescriptor *od; GF_ESD *esd; const char *tag; u32 i, count, ocr_es_id, tlen, base_track, j, track_id; Bool highest_stream; ocr_es_id = 0; /*TODO check for alternate tracks*/ count = gf_isom_get_track_count(read->mov); for (i=0; i<count; i++) { if (!gf_isom_is_track_enabled(read->mov, i+1)) continue; switch (gf_isom_get_media_type(read->mov, i+1)) { case GF_ISOM_MEDIA_AUDIO: case GF_ISOM_MEDIA_VISUAL: case GF_ISOM_MEDIA_TEXT: case GF_ISOM_MEDIA_SUBT: case GF_ISOM_MEDIA_SCENE: case GF_ISOM_MEDIA_SUBPIC: break; default: continue; } //some subtypes are not declared as readable objects switch (gf_isom_get_media_subtype(read->mov, i+1, 1)) { case GF_ISOM_SUBTYPE_HVT1: continue; default: break; } /*we declare only the highest video track (i.e the track we play)*/ highest_stream = GF_TRUE; track_id = gf_isom_get_track_id(read->mov, i+1); if (read->play_only_track_id && (read->play_only_track_id != track_id)) continue; for (j = 0; j < count; j++) { if (gf_isom_has_track_reference(read->mov, j+1, GF_ISOM_REF_SCAL, track_id) > 0) { highest_stream = GF_FALSE; break; } } if ((gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_VISUAL) && !highest_stream) continue; esd = gf_media_map_esd(read->mov, i+1); if (esd) { gf_isom_get_reference(read->mov, i+1, GF_ISOM_REF_BASE, 1, &base_track); esd->has_ref_base = base_track ? GF_TRUE : GF_FALSE; if (!esd->langDesc) { esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG); gf_isom_get_media_language(read->mov, i+1, &esd->langDesc->full_lang_code); } od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->service_ifce = read->input; od->objectDescriptorID = 0; if (!ocr_es_id) ocr_es_id = esd->ESID; esd->OCRESID = ocr_es_id; gf_list_add(od->ESDescriptors, esd); if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, (GF_Descriptor*)od, NULL); } else { gf_service_declare_media(read->service, (GF_Descriptor*)od, GF_TRUE); } } } /*if cover art, extract it in cache*/ if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COVER_ART, &tag, &tlen)==GF_OK) { const char *cdir = gf_modules_get_option((GF_BaseInterface *)gf_service_get_interface(read->service), "General", "CacheDirectory"); if (cdir) { char szName[GF_MAX_PATH]; const char *sep; FILE *t; sep = strrchr(gf_isom_get_filename(read->mov), '\\'); if (!sep) sep = strrchr(gf_isom_get_filename(read->mov), '/'); if (!sep) sep = gf_isom_get_filename(read->mov); if ((cdir[strlen(cdir)-1] != '\\') && (cdir[strlen(cdir)-1] != '/')) { sprintf(szName, "%s/%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg"); } else { sprintf(szName, "%s%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg"); } t = gf_fopen(szName, "wb"); if (t) { Bool isom_contains_video = GF_FALSE; /*write cover data*/ assert(!(tlen & 0x80000000)); gf_fwrite(tag, tlen & 0x7FFFFFFF, 1, t); gf_fclose(t); /*don't display cover art when video is present*/ for (i=0; i<gf_isom_get_track_count(read->mov); i++) { if (!gf_isom_is_track_enabled(read->mov, i+1)) continue; if (gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_VISUAL) { isom_contains_video = GF_TRUE; break; } } if (!isom_contains_video) { od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->service_ifce = read->input; od->objectDescriptorID = GF_MEDIA_EXTERNAL_ID; od->URLString = gf_strdup(szName); if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, (GF_Descriptor*)od, NULL); } else { gf_service_declare_media(read->service, (GF_Descriptor*)od, GF_TRUE); } } } } } if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, NULL, NULL); } else { gf_service_declare_media(read->service, NULL, GF_FALSE); } }
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_service_get_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_service_get_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; }
void RP_ProcessCommands(RTSPSession *sess) { GF_RTSPCommand *com; GF_Err e; u32 time; com = RP_GetCommand(sess); /*if asked or command to send, flushout TCP - TODO: check what's going on with ANNOUNCE*/ if ((com && !(sess->flags & RTSP_WAIT_REPLY) ) || (sess->flags & RTSP_TCP_FLUSH) ) { while (1) { e = gf_rtsp_session_read(sess->session); if (e) break; } sess->flags &= ~RTSP_TCP_FLUSH; } /*handle response or announce*/ if ( (com && (sess->flags & RTSP_WAIT_REPLY) ) /*|| (!com && sess->owner->handle_announce)*/) { e = gf_rtsp_get_response(sess->session, sess->rtsp_rsp); if (e!= GF_IP_NETWORK_EMPTY) { e = RP_ProcessResponse(sess, com, e); /*this is a service connect error -> plugin may be discarded */ if (e!=GF_OK) { RP_RemoveCommand(sess); gf_rtsp_command_del(com); gf_service_connect_ack(sess->owner->service, NULL, e); return; } RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; } else { u32 time_out = sess->owner->time_out; /*evaluate timeout*/ time = gf_sys_clock() - sess->command_time; if (!strcmp(com->method, GF_RTSP_DESCRIBE) && (time_out < 10000) ) time_out = 10000; /*don't waste time waiting for teardown ACK, half a sec is enough. If server is not replying in time it is likely to never reply (happens with RTP over RTSP) -> kill session and create new one*/ else if (!strcmp(com->method, GF_RTSP_TEARDOWN) && (time>=500) ) time = time_out; //signal what's going on if (time >= time_out) { if (!strcmp(com->method, GF_RTSP_TEARDOWN)) { gf_rtsp_session_reset(sess->session, 1); } else { GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Request Timeout for command %s after %d ms\n", com->method, time)); } RP_ProcessResponse(sess, com, GF_IP_NETWORK_FAILURE); RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; gf_rtsp_reset_aggregation(sess->session); } } return; } if (!com) return; /*send command - check RTSP session state first*/ switch (gf_rtsp_get_session_state(sess->session)) { case GF_RTSP_STATE_WAITING: case GF_RTSP_STATE_WAIT_FOR_CONTROL: return; case GF_RTSP_STATE_INVALIDATED: RP_SendFailure(sess, com, GF_IP_NETWORK_FAILURE); RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; return; } /*process*/ com->User_Agent = (char*)gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(sess->owner->service), "Downloader", "UserAgent"); if (!com->User_Agent) com->User_Agent = "GPAC " GPAC_VERSION " RTSP Client"; com->Accept_Language = RTSP_LANGUAGE; /*if no session assigned and a session ID is valid, use it*/ if (sess->session_id && !com->Session) com->Session = sess->session_id; e = GF_OK; /*preprocess describe before sending (always the ESD url thing)*/ if (!strcmp(com->method, GF_RTSP_DESCRIBE)) { com->Session = NULL; if (!RP_PreprocessDescribe(sess, com)) { e = GF_BAD_PARAM; goto exit; } } /*preprocess play/stop/pause before sending (aggregation)*/ if (!strcmp(com->method, GF_RTSP_PLAY) || !strcmp(com->method, GF_RTSP_PAUSE) || !strcmp(com->method, GF_RTSP_TEARDOWN)) { //command is skipped if (!RP_PreprocessUserCom(sess, com)) { e = GF_BAD_PARAM; goto exit; } } e = gf_rtsp_send_command(sess->session, com); if (e) { RP_SendFailure(sess, com, e); RP_ProcessResponse(sess, com, e); } else { sess->command_time = gf_sys_clock(); sess->flags |= RTSP_WAIT_REPLY; } exit: /*reset static strings*/ com->User_Agent = NULL; com->Accept_Language = NULL; com->Session = NULL; /*remove command*/ if (e) { RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; } }