LCOV - code coverage report
Current view: top level - filters - out_rtsp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 531 653 81.3 %
Date: 2021-04-29 23:48:07 Functions: 23 24 95.8 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2019-2020
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / rtsp output filter
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include <gpac/internal/media_dev.h>
      27             : #include <gpac/constants.h>
      28             : #include <gpac/maths.h>
      29             : 
      30             : #if !defined(GPAC_DISABLE_STREAMING)
      31             : 
      32             : #include <gpac/filters.h>
      33             : #include <gpac/ietf.h>
      34             : #include <gpac/config_file.h>
      35             : #include <gpac/base_coding.h>
      36             : #include <gpac/rtp_streamer.h>
      37             : 
      38             : //common functions for rtpout and rtspout
      39             : #include "out_rtp.h"
      40             : 
      41             : enum
      42             : {
      43             :         SDP_NONE = 0,
      44             :         SDP_WAIT,
      45             :         SDP_LOADED
      46             : };
      47             : 
      48             : enum
      49             : {
      50             :         MCAST_OFF = 0,
      51             :         MCAST_ON,
      52             :         MCAST_MIRROR
      53             : };
      54             : 
      55             : 
      56             : typedef struct
      57             : {
      58             :         //options
      59             :         char *dst, *user_agent;
      60             :         GF_PropStringList mounts;
      61             :         u32 port, firstport;
      62             :         Bool xps;
      63             :         u32 mtu;
      64             :         u32 ttl;
      65             :         char *ifce;
      66             :         u32 payt, tt;
      67             :         s32 delay;
      68             :         char *info, *url, *email;
      69             :         s32 runfor, tso;
      70             :         u32 maxc;
      71             :         u32 block_size;
      72             :         Bool close, loop, dynurl, mpeg4;
      73             :         u32 mcast;
      74             :         Bool latm;
      75             : 
      76             :         GF_Socket *server_sock;
      77             :         GF_List *sessions;
      78             : 
      79             :         u32 next_wake_us;
      80             :         char *ip;
      81             :         Bool done;
      82             : } GF_RTSPOutCtx;
      83             : 
      84             : 
      85             : typedef struct __rtspout_session
      86             : {
      87             :         GF_RTSPOutCtx *ctx;
      88             : 
      89             :         struct __rtspout_session *mcast_mirror;
      90             : 
      91             :         GF_RTSPSession *rtsp;
      92             :         GF_RTSPCommand *command;
      93             :         GF_RTSPResponse *response;
      94             :         /*list of streams in session*/
      95             :         GF_List *streams;
      96             :         Bool loop_disabled, loop;
      97             : 
      98             :         char *service_name;
      99             :         char *sessionID;
     100             :         char peer_address[GF_MAX_IP_NAME_LEN];
     101             : 
     102             :         u32 play_state;
     103             :         Double start_range;
     104             :         u32 last_cseq;
     105             :         Bool interleave;
     106             : 
     107             :         /*base stream if this stream contains a media decoding dependency, 0 otherwise*/
     108             :         u32 base_pid_id;
     109             : 
     110             :         Bool first_RTCP_sent;
     111             : 
     112             :         GF_RTPOutStream *active_stream;
     113             :         u32 active_stream_idx;
     114             :         u64 active_min_ts_microsec;
     115             : 
     116             :         /*timeline origin of our session (all tracks) in microseconds*/
     117             :         u64 sys_clock_at_init;
     118             : 
     119             :         Bool wait_for_loop;
     120             :         u64 microsec_ts_init;
     121             : 
     122             :         Bool single_session;
     123             :         char *server_path;
     124             :         GF_List *filter_srcs;
     125             : 
     126             :         u32 sdp_state;
     127             : 
     128             :         u32 next_stream_id;
     129             : 
     130             :         u64 pause_sys_clock;
     131             :         Bool request_pending;
     132             :         char *multicast_ip;
     133             :         u64 sdp_id;
     134             : } GF_RTSPOutSession;
     135             : 
     136             : 
     137          28 : static void rtspout_send_response(GF_RTSPOutCtx *ctx, GF_RTSPOutSession *sess)
     138             : {
     139          28 :         sess->response->User_Agent = ctx->user_agent;
     140          28 :         sess->response->Session = sess->sessionID;
     141          28 :         if (ctx->close && !sess->interleave)
     142          24 :                 sess->response->Connection = "close";
     143          28 :         gf_rtsp_send_response(sess->rtsp, sess->response);
     144          28 :         sess->response->User_Agent = NULL;
     145          28 :         sess->response->Session = NULL;
     146          28 :         if (ctx->close && !sess->interleave) {
     147          24 :                 sess->response->Connection = NULL;
     148          24 :                 gf_rtsp_session_del(sess->rtsp);
     149          24 :                 sess->rtsp = NULL;
     150             :         }
     151          28 : }
     152             : 
     153           5 : static void rtspout_check_last_sess(GF_RTSPOutCtx *ctx)
     154             : {
     155           5 :         if (gf_list_count(ctx->sessions) ) return;
     156             : 
     157           5 :         if (ctx->dst)
     158           2 :                 ctx->done = GF_TRUE;
     159           3 :         else if (ctx->runfor>0)
     160           3 :                 ctx->done = GF_TRUE;
     161             : }
     162             : 
     163           5 : static GF_Err rtspout_send_sdp(GF_RTSPOutSession *sess)
     164             : {
     165             :         FILE *sdp_out;
     166             :         u32 fsize;
     167             :         GF_Err e;
     168           5 :         const char *ip = sess->ctx->ip;
     169           5 :         if (!ip) ip = sess->ctx->ifce;
     170           5 :         if (!ip) ip = "127.0.0.1";
     171             : 
     172           5 :         if (sess->mcast_mirror) {
     173           0 :                 ip = sess->mcast_mirror->multicast_ip;
     174           0 :                 e = rtpout_create_sdp(sess->mcast_mirror->streams, GF_FALSE, ip, sess->ctx->info, "livesession", sess->ctx->url, sess->ctx->email, sess->mcast_mirror->base_pid_id, &sdp_out, &sess->sdp_id);
     175             :         } else {
     176           5 :                 e = rtpout_create_sdp(sess->streams, GF_TRUE, ip, sess->ctx->info, "livesession", sess->ctx->url, sess->ctx->email, sess->base_pid_id, &sdp_out, &sess->sdp_id);
     177             :         }
     178           5 :         if (e) return e;
     179             : 
     180           5 :         fsize = (u32) gf_ftell(sdp_out);
     181           5 :         char *sdp_output = gf_malloc(sizeof(char)*(fsize+1));
     182           5 :         gf_fseek(sdp_out, 0, SEEK_SET);
     183           5 :         u32 read = (u32) gf_fread(sdp_output, fsize, sdp_out);
     184           5 :         sdp_output[read]=0;
     185           5 :         gf_fclose(sdp_out);
     186             : 
     187             : 
     188           5 :         gf_rtsp_response_reset(sess->response);
     189           5 :         sess->response->ResponseCode = NC_RTSP_OK;
     190           5 :         sess->response->CSeq = sess->command->CSeq;
     191           5 :         sess->response->body = sdp_output;
     192             : 
     193           5 :         rtspout_send_response(sess->ctx, sess);
     194           5 :         sess->response->body = NULL;
     195           5 :         gf_free(sdp_output);
     196             : 
     197           5 :         return GF_OK;
     198             : }
     199             : 
     200             : 
     201           6 : static void rtspout_del_stream(GF_RTPOutStream *st)
     202             : {
     203           6 :         if (st->rtp) gf_rtp_streamer_del(st->rtp);
     204           6 :         if (st->pck) gf_filter_pid_drop_packet(st->pid);
     205           6 :         if (st->avcc)
     206           0 :                 gf_odf_avc_cfg_del(st->avcc);
     207           6 :         if (st->hvcc)
     208           0 :                 gf_odf_hevc_cfg_del(st->hvcc);
     209           6 :         gf_free(st);
     210           6 : }
     211             : 
     212          27 : static void rtspout_del_session(GF_RTSPOutSession *sess)
     213             : {
     214             :         //server mode, cleanup
     215          60 :         while (gf_list_count(sess->streams)) {
     216           6 :                 GF_RTPOutStream *stream = gf_list_pop_back(sess->streams);
     217           6 :                 rtspout_del_stream(stream);
     218             :         }
     219          27 :         gf_list_del(sess->streams);
     220             : 
     221          27 :         if (sess->service_name)
     222           5 :                 gf_free(sess->service_name);
     223          27 :         if (sess->sessionID)
     224           5 :                 gf_free(sess->sessionID);
     225          27 :         gf_list_del(sess->filter_srcs);
     226          27 :         gf_rtsp_session_del(sess->rtsp);
     227          27 :         gf_rtsp_command_del(sess->command);
     228          27 :         gf_rtsp_response_del(sess->response);
     229          27 :         gf_list_del_item(sess->ctx->sessions, sess);
     230          27 :         if (sess->multicast_ip) gf_free(sess->multicast_ip);
     231          27 :         gf_free(sess);
     232          27 : }
     233             : 
     234             : 
     235           6 : GF_RTSPOutSession *rtspout_locate_session_for_pid(GF_Filter *filter, GF_RTSPOutCtx *ctx, GF_FilterPid *pid)
     236             : {
     237           6 :         u32 i, count = gf_list_count(ctx->sessions);
     238           6 :         if (ctx->dst) {
     239           0 :                 for (i=0; i<count; i++) {
     240           2 :                         GF_RTSPOutSession *sess = gf_list_get(ctx->sessions, i);
     241           2 :                         if (sess->single_session) return sess;
     242             :                 }
     243             :                 return NULL;
     244             :         }
     245           0 :         for (i=0; i<count; i++) {
     246             :                 u32 j, nb_filters;
     247           4 :                 GF_RTSPOutSession *sess = gf_list_get(ctx->sessions, i);
     248           4 :                 if (sess->single_session) continue;
     249           4 :                 nb_filters = gf_list_count(sess->filter_srcs);
     250           5 :                 for (j=0; j<nb_filters; j++) {
     251           5 :                         GF_Filter *srcf = gf_list_get(sess->filter_srcs, j);
     252           5 :                         if (gf_filter_pid_is_filter_in_parents(pid, srcf))
     253             :                                 return sess;
     254             :                 }
     255             :         }
     256             : 
     257             :         return NULL;
     258             : }
     259             : 
     260           6 : static GF_Err rtspout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     261             : {
     262           6 :         GF_RTSPOutCtx *ctx = (GF_RTSPOutCtx *) gf_filter_get_udta(filter);
     263             :         GF_Err e = GF_OK;
     264             :         GF_RTPOutStream *stream;
     265             :         GF_RTSPOutSession *sess;
     266             :         u32 streamType, payt;
     267             :         const GF_PropertyValue *p;
     268             : 
     269           6 :         sess = rtspout_locate_session_for_pid(filter, ctx, pid);
     270           6 :         if (!sess) return GF_SERVICE_ERROR;
     271             : 
     272           6 :         if (is_remove) {
     273           0 :                 GF_RTPOutStream *t = gf_filter_pid_get_udta(pid);
     274           0 :                 if (t) {
     275           0 :                         if (sess->active_stream==t) sess->active_stream = NULL;
     276           0 :                         gf_list_del_item(sess->streams, t);
     277           0 :                         rtspout_del_stream(t);
     278             :                 }
     279           0 :                 if (!gf_list_count(sess->streams)) {
     280           0 :                         rtspout_del_session(sess);
     281             :                 }
     282             :                 return GF_OK;
     283             :         }
     284           6 :         stream = gf_filter_pid_get_udta(pid);
     285             : 
     286           6 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
     287           6 :         streamType = p ? p->value.uint : 0;
     288             : 
     289           6 :         switch (streamType) {
     290             :         case GF_STREAM_VISUAL:
     291             :         case GF_STREAM_AUDIO:
     292             :                 break;
     293           0 :         case GF_STREAM_FILE:
     294             :         case GF_STREAM_UNKNOWN:
     295           0 :                 if (stream) {
     296           0 :                         if (sess->active_stream==stream) sess->active_stream = NULL;
     297           0 :                         gf_list_del_item(sess->streams, stream);
     298           0 :                         rtspout_del_stream(stream);
     299             :                 }
     300             :                 return GF_FILTER_NOT_SUPPORTED;
     301             :         default:
     302             :                 break;
     303             :         }
     304           6 :         if (!stream) {
     305           6 :                 GF_SAFEALLOC(stream, GF_RTPOutStream);
     306           6 :                 if (!stream) return GF_OUT_OF_MEM;
     307           6 :                 gf_list_add(sess->streams, stream);
     308           6 :                 stream->pid = pid;
     309           6 :                 stream->streamtype = streamType;
     310           6 :                 stream->min_dts = GF_FILTER_NO_TS;
     311           6 :                 gf_filter_pid_set_udta(pid, stream);
     312             :         }
     313             : 
     314           6 :         stream->ctrl_id = sess->next_stream_id+1;
     315           6 :         sess->next_stream_id++;
     316             : 
     317           6 :         payt = ctx->payt + gf_list_find(sess->streams, stream);
     318             : 
     319           6 :         e = rtpout_init_streamer(stream, ctx->ifce ? ctx->ifce : "127.0.0.1", ctx->xps, ctx->mpeg4, ctx->latm, payt, ctx->mtu, ctx->ttl, ctx->ifce, GF_TRUE, &sess->base_pid_id, 0);
     320           6 :         if (e) return e;
     321             : 
     322           6 :         if (ctx->loop) {
     323           6 :                 p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAYBACK_MODE);
     324           6 :                 if (!p || (p->value.uint<GF_PLAYBACK_MODE_FASTFORWARD)) {
     325           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSPOut] PID %s cannot be seek, disabling loop\n", gf_filter_pid_get_name(pid) ));
     326             : 
     327           0 :                         sess->loop_disabled = GF_TRUE;
     328             :                 }
     329             :         }
     330             :         return GF_OK;
     331             : }
     332             : 
     333        2826 : static GF_Err rtspout_check_new_session(GF_RTSPOutCtx *ctx, Bool single_session)
     334             : {
     335             :         GF_RTSPOutSession *sess;
     336             :         GF_RTSPSession *new_sess = NULL;
     337             : 
     338        2826 :         if (!single_session) {
     339        2824 :                 new_sess = gf_rtsp_session_new_server(ctx->server_sock);
     340        2824 :                 if (!new_sess) return GF_OK;
     341             :         }
     342             : 
     343          27 :         GF_SAFEALLOC(sess, GF_RTSPOutSession);
     344          27 :         if (!sess) {
     345           0 :                 gf_rtsp_session_del(new_sess);
     346           0 :                 return GF_OUT_OF_MEM;
     347             :         }
     348          27 :         sess->rtsp = new_sess;
     349          27 :         sess->command = gf_rtsp_command_new();
     350          27 :         sess->response = gf_rtsp_response_new();
     351          27 :         sess->streams = gf_list_new();
     352          27 :         sess->filter_srcs = gf_list_new();
     353             : 
     354          27 :         if (new_sess) {
     355          25 :                 gf_rtsp_set_buffer_size(new_sess, ctx->block_size);
     356          25 :                 gf_rtsp_get_remote_address(new_sess, sess->peer_address);
     357          25 :                 GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Accepting new connection from %s\n", sess->peer_address));
     358             :         } else {
     359           2 :                 sess->single_session = GF_TRUE;
     360             :         }
     361          27 :         sess->ctx = ctx;
     362          27 :         gf_list_add(ctx->sessions, sess);
     363          27 :         return GF_OK;
     364             : }
     365             : 
     366           5 : static GF_Err rtspout_initialize(GF_Filter *filter)
     367             : {
     368             :         char szIP[1024];
     369             :         GF_Err e;
     370             :         u16 port;
     371             :         char *ip;
     372           5 :         GF_RTSPOutCtx *ctx = (GF_RTSPOutCtx *) gf_filter_get_udta(filter);
     373           5 :         if (!ctx->payt) ctx->payt = 96;
     374           5 :         if (!ctx->port) ctx->port = 554;
     375           5 :         if (!ctx->firstport) ctx->firstport = 7000;
     376           5 :         if (!ctx->mtu) ctx->mtu = 1450;
     377           5 :         if (ctx->payt<96) ctx->payt = 96;
     378           5 :         if (ctx->payt>127) ctx->payt = 127;
     379           5 :         ctx->sessions = gf_list_new();
     380             : 
     381           5 :         port = ctx->port;
     382           5 :         ip = ctx->ifce;
     383             : 
     384           5 :         if (!ctx->dst) {
     385           3 :                 if (! ctx->mounts.nb_items) {
     386           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSPOut] No root dir for server, cannot run\n" ));
     387             :                         return GF_BAD_PARAM;
     388             :                 }
     389           3 :                 gf_filter_make_sticky(filter);
     390             :         } else {
     391             :                 GF_RTSPOutSession *sess;
     392           2 :                 char *sep = strchr(ctx->dst+7, '/');
     393           2 :                 if (sep) {
     394           2 :                         u32 cplen = (u32) (sep-ctx->dst-7);
     395           2 :                         if (cplen>1023) cplen = 1023;
     396           2 :                         strncpy(szIP, ctx->dst+7, cplen);
     397           2 :                         szIP[1023] = 0;
     398           2 :                         sep = strchr(szIP, ':');
     399           2 :                         if (sep) {
     400           4 :                                 port = atoi(sep+1);
     401           2 :                                 if (!port) port = ctx->port;
     402           2 :                                 sep[0] = 0;
     403             :                         }
     404           2 :                         if (strlen(szIP)) ip = szIP;
     405             :                 }
     406           2 :                 rtspout_check_new_session(ctx, GF_TRUE);
     407           2 :                 sess = gf_list_get(ctx->sessions, 0);
     408           2 :                 if (!sess) return GF_SERVICE_ERROR;
     409           2 :                 sess->server_path = ctx->dst;
     410           2 :                 sess->sdp_state = SDP_LOADED;
     411             :         }
     412             : 
     413           5 :         if (ip)
     414           2 :                 ctx->ip = gf_strdup(ip);
     415             : 
     416           5 :         ctx->server_sock = gf_sk_new(GF_SOCK_TYPE_TCP);
     417           5 :         e = gf_sk_bind(ctx->server_sock, NULL, port, ip, 0, GF_SOCK_REUSE_PORT);
     418           5 :         if (!e) e = gf_sk_listen(ctx->server_sock, ctx->maxc);
     419           5 :         if (e) {
     420           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSPOut] failed to start server on port %d: %s\n", ctx->port, gf_error_to_string(e) ));
     421             :                 return e;
     422             :         }
     423             : 
     424           5 :         gf_sk_server_mode(ctx->server_sock, GF_TRUE);
     425           5 :         GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPOut] Server running on port %d\n", ctx->port));
     426           5 :         gf_filter_post_process_task(filter);
     427           5 :         return GF_OK;
     428             : }
     429             : 
     430           5 : static void rtspout_finalize(GF_Filter *filter)
     431             : {
     432           5 :         GF_RTSPOutCtx *ctx = (GF_RTSPOutCtx *) gf_filter_get_udta(filter);
     433             : 
     434          10 :         while (gf_list_count(ctx->sessions)) {
     435           0 :                 GF_RTSPOutSession *tmp = gf_list_get(ctx->sessions, 0);
     436           0 :                 rtspout_del_session(tmp);
     437             :         }
     438           5 :         gf_list_del(ctx->sessions);
     439             : 
     440           5 :         gf_sk_del(ctx->server_sock);
     441           5 :         if (ctx->ip) gf_free(ctx->ip);    
     442           5 : }
     443             : 
     444             : 
     445          12 : static Bool rtspout_init_clock(GF_RTSPOutCtx *ctx, GF_RTSPOutSession *sess)
     446             : {
     447             :         u64 min_dts = GF_FILTER_NO_TS;
     448          12 :         u32 i, count = gf_list_count(sess->streams);
     449             : 
     450          21 :         for (i=0; i<count; i++) {
     451             :                 u64 dts;
     452             :                 GF_FilterPacket *pck;
     453          15 :                 GF_RTPOutStream *stream = gf_list_get(sess->streams, i);
     454          15 :                 if (!stream->selected) continue;
     455             : 
     456             :                 while (1) {
     457          14 :                         pck = gf_filter_pid_get_packet(stream->pid);
     458          14 :                         if (!pck) return GF_FALSE;
     459           8 :                         if (gf_filter_pck_get_seek_flag(pck)) {
     460           0 :                                 gf_filter_pid_drop_packet(stream->pid);
     461           0 :                                 continue;
     462             :                         }
     463             :                         break;
     464             :                 }
     465             : 
     466           8 :                 dts = gf_filter_pck_get_dts(pck);
     467           8 :                 if (dts==GF_FILTER_NO_TS)
     468           0 :                         dts = gf_filter_pck_get_cts(pck);
     469             : 
     470           8 :                 if (dts==GF_FILTER_NO_TS) dts=0;
     471             : 
     472           8 :                 dts *= 1000000;
     473           8 :                 dts /= stream->timescale;
     474           8 :                 if (min_dts > dts)
     475             :                         min_dts = dts;
     476             : 
     477           8 :                 if (ctx->tso>0) {
     478           8 :                         u64 offset = ctx->tso;
     479           8 :                         offset *= stream->timescale;
     480           8 :                         offset /= 1000000;
     481           8 :                         stream->rtp_ts_offset = (u32) offset;
     482             :                 }
     483           8 :                 stream->current_cts = gf_filter_pck_get_cts(pck);
     484             :         }
     485           6 :         sess->sys_clock_at_init = gf_sys_clock_high_res();
     486           6 :         sess->microsec_ts_init = min_dts;
     487           6 :         GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPOut] Session %s: RTP clock initialized - time origin set to "LLU" us (sys clock) / "LLU" us (media clock)\n", sess->service_name, sess->sys_clock_at_init, sess->microsec_ts_init));
     488           6 :         if (ctx->tso<0) {
     489           0 :                 gf_rand_init(GF_FALSE);
     490           0 :                 for (i=0; i<count; i++) {
     491           0 :                         GF_RTPOutStream *stream = gf_list_get(sess->streams, i);
     492           0 :                         stream->rtp_ts_offset = gf_rand();
     493           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPOut] Session %s: RTP stream %d initial RTP TS set to %d\n", sess->service_name, i+1, stream->rtp_ts_offset));
     494             :                 }
     495             :         }
     496             : 
     497             : 
     498           6 :         gf_rtsp_response_reset(sess->response);
     499           6 :         sess->response->ResponseCode = NC_RTSP_OK;
     500          14 :         for (i=0; i<count; i++) {
     501             :                 GF_RTPInfo *rtpi;
     502           8 :                 GF_RTPOutStream *stream = gf_list_get(sess->streams, i);
     503           8 :                 if (!stream->selected) continue;
     504           7 :                 if (!stream->send_rtpinfo) continue;
     505           6 :                 stream->send_rtpinfo = GF_FALSE;
     506             : 
     507           6 :                 GF_SAFEALLOC(rtpi, GF_RTPInfo);
     508           6 :                 if (rtpi) {
     509           6 :                         rtpi->url = gf_malloc(sizeof(char) * (strlen(sess->service_name)+50));
     510           6 :                         sprintf(rtpi->url, "%s/trackID=%d", sess->service_name, stream->ctrl_id);
     511           6 :                         rtpi->seq = gf_rtp_streamer_get_next_rtp_sn(stream->rtp);
     512           6 :                         rtpi->rtp_time = (u32) (stream->current_cts + stream->ts_offset + stream->rtp_ts_offset);
     513             : 
     514           6 :                         gf_list_add(sess->response->RTP_Infos, rtpi);
     515             :                 }
     516             :         }
     517           6 :         GF_SAFEALLOC(sess->response->Range, GF_RTSPRange);
     518           6 :         if (sess->response->Range)
     519           6 :                 sess->response->Range->start = sess->start_range;
     520             : 
     521           6 :         sess->response->CSeq = sess->last_cseq;
     522           6 :         rtspout_send_response(ctx, sess);
     523           6 :         sess->request_pending = GF_FALSE;
     524           6 :         return GF_TRUE;
     525             : }
     526             : 
     527          16 : static void rtspout_send_event(GF_RTSPOutSession *sess, Bool send_stop, Bool send_play, Double start_range)
     528             : {
     529             :         GF_FilterEvent fevt;
     530          16 :         u32 i, count = gf_list_count(sess->streams);
     531             : 
     532             :         memset(&fevt, 0, sizeof(GF_FilterEvent));
     533             : 
     534          20 :         for (i=0; i<count; i++) {
     535          20 :                 GF_RTPOutStream *stream = gf_list_get(sess->streams, i);
     536          20 :                 if (!stream->selected) continue;
     537             : 
     538          19 :                 fevt.base.on_pid = stream->pid;
     539          19 :                 if (send_stop && stream->is_playing) {
     540           6 :                         stream->is_playing = GF_FALSE;
     541           6 :                         fevt.base.type = GF_FEVT_STOP;
     542           6 :                         gf_filter_pid_send_event(stream->pid, &fevt);
     543             :                 }
     544          19 :                 if (send_play && !stream->is_playing) {
     545           6 :                         stream->is_playing = GF_TRUE;
     546           6 :                         fevt.base.type = GF_FEVT_PLAY;
     547           6 :                         fevt.play.start_range = start_range;
     548           6 :                         gf_filter_pid_send_event(stream->pid, &fevt);
     549             : 
     550           6 :                         stream->send_rtpinfo = GF_TRUE;
     551             :                 }
     552             :         }
     553          16 : }
     554             : 
     555        2712 : static GF_Err rtspout_process_rtp(GF_Filter *filter, GF_RTSPOutCtx *ctx, GF_RTSPOutSession *sess)
     556             : {
     557             :         GF_Err e = GF_OK;
     558        2712 :         u32 repost_delay_us=0;
     559             : 
     560             :         /*init session timeline - all sessions are sync'ed for packet scheduling purposes*/
     561        2712 :         if (!sess->sys_clock_at_init) {
     562          12 :                 if (!rtspout_init_clock(ctx, sess)) return GF_OK;
     563             :         }
     564             : 
     565        2706 :         if (ctx->runfor>0) {
     566        1775 :                 s64 diff = gf_sys_clock_high_res();
     567        1775 :                 diff -= sess->sys_clock_at_init;
     568        1775 :                 diff /= 1000;
     569        1775 :                 if ((s32) diff > ctx->runfor) {
     570           9 :                         u32 i, count = gf_list_count(sess->streams);
     571          12 :                         for (i=0; i<count; i++) {
     572          12 :                                 GF_RTPOutStream *stream = gf_list_get(sess->streams, i);
     573          12 :                                 gf_filter_pid_set_discard(stream->pid, GF_TRUE);
     574          12 :                                 stream->pck = NULL;
     575             :                         }
     576             :                         return GF_EOS;
     577             :                 }
     578             :         }
     579             : 
     580        2697 :         e = rtpout_process_rtp(sess->streams, &sess->active_stream, sess->loop, ctx->delay, &sess->active_stream_idx, sess->sys_clock_at_init, &sess->active_min_ts_microsec, sess->microsec_ts_init, &sess->wait_for_loop, &repost_delay_us, &sess->first_RTCP_sent, sess->base_pid_id);
     581             : 
     582        2697 :         if (e) return e;
     583        2697 :         if (ctx->next_wake_us > repost_delay_us)
     584        2697 :                 ctx->next_wake_us = (u32) repost_delay_us;
     585             :         return GF_OK;
     586             : }
     587             : 
     588          14 : static GF_Err rtspout_interleave_packet(void *cbk1, void *cbk2, Bool is_rtcp, u8 *pck, u32 pck_size)
     589             : {
     590             :         GF_RTSPOutSession *sess = (GF_RTSPOutSession *)cbk1;
     591             :         GF_RTPOutStream *stream = (GF_RTPOutStream *)cbk2;
     592             : 
     593          14 :         u32 idx = is_rtcp ? stream->rtcp_id : stream->rtp_id;
     594          14 :         return gf_rtsp_session_write_interleaved(sess->rtsp, idx, pck, pck_size);
     595             : }
     596             : 
     597           0 : void rtspout_on_filter_setup_error(GF_Filter *f, void *on_setup_error_udta, GF_Err e)
     598             : {
     599             :         GF_RTSPOutSession *sess = (GF_RTSPOutSession *)on_setup_error_udta;
     600             : 
     601           0 :         gf_list_del_item(sess->filter_srcs, f);
     602           0 :         if (gf_list_count(sess->filter_srcs)) return;
     603             : 
     604           0 :         if (sess->sdp_state != SDP_LOADED) {
     605           0 :                 sess->sdp_state = SDP_LOADED;
     606           0 :                 gf_rtsp_response_reset(sess->response);
     607           0 :                 sess->response->ResponseCode = NC_RTSP_Internal_Server_Error;
     608           0 :                 sess->response->CSeq = sess->command->CSeq;
     609           0 :                 rtspout_send_response(sess->ctx, sess);
     610             :         }
     611           0 :         rtspout_del_session(sess);
     612             : }
     613             : 
     614           4 : static GF_Err rtspout_load_media_service(GF_Filter *filter, GF_RTSPOutCtx *ctx, GF_RTSPOutSession *sess, char *src_url)
     615             : {
     616             :         GF_Err e;
     617             :         Bool found = GF_FALSE;
     618           4 :         u32 i, count = gf_list_count(sess->filter_srcs);
     619           5 :         for (i=0; i<count; i++) {
     620           1 :                 GF_Filter *src = gf_list_get(sess->filter_srcs, i);
     621           1 :                 const char *url = gf_filter_get_arg_str(src, "src", NULL);
     622           1 :                 if (url && !strcmp(src_url, url)) {
     623             :                         found = GF_TRUE;
     624             :                         break;
     625             :                 }
     626             :         }
     627           4 :         if (!found) {
     628           4 :                 GF_Filter *filter_src = gf_filter_connect_source(filter, src_url, NULL, GF_FALSE, &e);
     629           4 :                 if (!filter_src) {
     630           0 :                         gf_rtsp_response_reset(sess->response);
     631           0 :                         sess->response->ResponseCode = NC_RTSP_Session_Not_Found;
     632           0 :                         sess->response->CSeq = sess->command->CSeq;
     633           0 :                         rtspout_send_response(ctx, sess);
     634           0 :                         return e;
     635             :                 }
     636           4 :                 gf_list_add(sess->filter_srcs, filter_src);
     637           4 :                 gf_filter_set_setup_failure_callback(filter, filter_src, rtspout_on_filter_setup_error, sess);
     638           4 :                 sess->sdp_state = SDP_WAIT;
     639             :         }
     640           4 :         if (sess->sdp_state==SDP_LOADED) {
     641             :                 //single session, create SDP
     642           0 :                 rtspout_send_sdp(sess);
     643             :         } else {
     644           4 :                 sess->sdp_state = SDP_WAIT;
     645           4 :                 sess->request_pending = GF_TRUE;
     646             :         }
     647             :         return GF_OK;
     648             : }
     649             : 
     650           3 : static GF_Err rtspout_check_sdp(GF_Filter *filter, GF_RTSPOutSession *sess)
     651             : {
     652           3 :         u32 i, count = gf_list_count(sess->streams);
     653           3 :         u32 j, nb_filters = gf_list_count(sess->filter_srcs);
     654             : 
     655           7 :         for (j=0; j<nb_filters; j++) {
     656             :                 Bool found = GF_FALSE;
     657           4 :                 GF_Filter *srcf = gf_list_get(sess->filter_srcs, j);
     658             :                 //check we have at least one pid
     659           5 :                 for (i=0; i<count; i++) {
     660           5 :                         GF_RTPOutStream *stream = gf_list_get(sess->streams, i);
     661           5 :                         if (gf_filter_pid_is_filter_in_parents(stream->pid, srcf)) {
     662             :                                 found = GF_TRUE;
     663             :                                 break;
     664             :                         }
     665             :                 }
     666             :                 //not yet connected
     667           4 :                 if (!found) return GF_OK;
     668             : 
     669             :                 //check we don't have other pid connection pendings
     670           4 :                 if (gf_filter_has_pid_connection_pending(srcf, filter))
     671             :                         return GF_OK;
     672             :         }
     673             :         //all streams should be ready - note that we don't know handle dynamic pid insertion in source service yet
     674           3 :         sess->sdp_state = SDP_LOADED;
     675           3 :         sess->request_pending = GF_FALSE;
     676           3 :         rtspout_send_sdp(sess);
     677           3 :         return GF_OK;
     678             : }
     679             : 
     680           1 : static void rtspout_get_next_mcast_port(GF_RTSPOutCtx *ctx, GF_RTSPOutSession *sess, u32 *port)
     681             : {
     682           1 :         u32 i, count = gf_list_count(ctx->sessions);
     683           1 :         u32 min_port=ctx->firstport;
     684           1 :         for (i=0; i<count; i++) {
     685             :                 u32 j, count2;
     686           1 :                 GF_RTSPOutSession *asess = gf_list_get(ctx->sessions, i);
     687           1 :                 if (asess == sess) continue;
     688           0 :                 if (!asess->multicast_ip || !sess->multicast_ip) continue;
     689             :                 //reuse port number if different multicast groups
     690           0 :                 if (strcmp(asess->multicast_ip, sess->multicast_ip)) continue;
     691             : 
     692           0 :                 count2 = gf_list_count(asess->streams);
     693           0 :                 for (j=0; j<count2; j++) {
     694           0 :                         GF_RTPOutStream *stream = gf_list_get(asess->streams, j);
     695           0 :                         if (stream->mcast_port>min_port) min_port = stream->mcast_port;
     696           0 :                         if (stream->mcast_port == *port) *port = 0;
     697             :                 }
     698             :         }
     699           1 :         if (! *port) *port = min_port;
     700           1 : }
     701             : 
     702           1 : static GF_RTSPOutSession *rtspout_locate_mcast(GF_RTSPOutCtx *ctx, char *res_path)
     703             : {
     704           1 :         u32 i, count = gf_list_count(ctx->sessions);
     705           1 :         for (i=0; i<count; i++) {
     706             :                 char *a_sess_path=NULL;
     707           1 :                 GF_RTSPOutSession *a_sess = gf_list_get(ctx->sessions, i);
     708           1 :                 if (!a_sess->multicast_ip) continue;
     709           0 :                 if (!a_sess->service_name) continue;
     710             : 
     711           0 :                 a_sess_path = strstr(a_sess->service_name, "://");
     712           0 :                 if (a_sess_path) a_sess_path = strchr(a_sess_path+3, '/');
     713           0 :                 if (a_sess_path) a_sess_path++;
     714           0 :                 if (a_sess_path && !strcmp(a_sess_path, res_path))
     715             :                         return a_sess;
     716             :         }
     717             :         return NULL;
     718             : }
     719             : 
     720           4 : static char *rtspout_get_local_res_path(GF_RTSPOutCtx *ctx, char *res_path)
     721             : {
     722             :         u32 i;
     723           4 :         char *src_url=NULL;
     724           0 :         for (i=0; i<ctx->mounts.nb_items; i++) {
     725           4 :                 char *mpoint = ctx->mounts.vals[i];
     726             : 
     727           4 :                 gf_dynstrcat(&src_url, mpoint, NULL);
     728           4 :                 gf_dynstrcat(&src_url, res_path, "/");
     729           4 :                 if (gf_file_exists(src_url))
     730             :                         break;
     731           0 :                 gf_free(src_url);
     732           0 :                 src_url = NULL;
     733             :         }
     734           4 :         return src_url;
     735             : }
     736             : 
     737        2783 : static GF_Err rtspout_process_session_signaling(GF_Filter *filter, GF_RTSPOutCtx *ctx, GF_RTSPOutSession **sess_ptr)
     738             : {
     739             :         GF_Err e;
     740        2783 :         GF_RTSPOutSession *sess = *sess_ptr;
     741             :         char *ctrl=NULL;
     742        2783 :         u32 stream_ctrl_id=0;
     743             : 
     744             :         //no rtsp connection on this session
     745        2783 :         if (!sess->rtsp) return GF_OK;
     746             : 
     747         427 :         if (sess->sdp_state==SDP_WAIT) {
     748           3 :                 return rtspout_check_sdp(filter, sess);
     749             :         }
     750             : 
     751         424 :         if (sess->request_pending) return GF_OK;
     752             : 
     753         418 :         e = gf_rtsp_get_command(sess->rtsp, sess->command);
     754         418 :         if (e==GF_IP_NETWORK_EMPTY) {
     755             :                 return GF_OK;
     756             :         }
     757             :         //
     758          28 :         if (e==GF_IP_CONNECTION_CLOSED) {
     759           0 :                 gf_rtsp_session_del(sess->rtsp);
     760           0 :                 sess->rtsp = NULL;
     761           0 :                 rtspout_check_last_sess(ctx);
     762           0 :                 return GF_OK;
     763             :         }
     764          28 :         if (e)
     765             :                 return e;
     766             : 
     767          28 :         GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got request %s from %s\n", sess->command->method, sess->peer_address));
     768             : 
     769             :         //restore session if needed
     770          28 :         if (!sess->service_name) {
     771          25 :                 u32 i, count = gf_list_count(ctx->sessions);
     772          28 :                 for (i=0; i<count; i++) {
     773             :                         Bool swap_sess = GF_FALSE;
     774          25 :                         GF_RTSPOutSession *a_sess = gf_list_get(ctx->sessions, i);
     775          25 :                         if (a_sess->rtsp) continue;
     776             : 
     777          22 :                         if (a_sess->sessionID && sess->command->Session && !strcmp(a_sess->sessionID, sess->command->Session) ) {
     778             :                                 swap_sess = GF_TRUE;
     779             :                         }
     780           7 :                         else if (!a_sess->sessionID && !sess->response->Session) {
     781             :                                 char *sname;
     782           7 :                                 char *cmd_path = strstr(sess->command->service_name, "://");
     783           7 :                                 if (cmd_path) cmd_path = strchr(cmd_path+3, '/');
     784           7 :                                 if (cmd_path) cmd_path++;
     785             : 
     786           7 :                                 sname = a_sess->service_name;
     787           7 :                                 if (!sname) sname = a_sess->server_path;
     788             : 
     789           7 :                                 if (sname) sname = strstr(sname, "://");
     790           7 :                                 if (sname) sname = strchr(sname+3, '/');
     791           7 :                                 if (sname) sname++;
     792             : 
     793           7 :                                 if (cmd_path && sname && !strncmp(cmd_path, sname, strlen(sname))) {
     794           7 :                                         if (a_sess->service_name) {
     795             :                                                 //no session ID, match based on peer address
     796           5 :                                                 if (!strcmp(sess->peer_address, a_sess->peer_address))
     797             :                                                         swap_sess = GF_TRUE;
     798             :                                         } else {
     799             :                                                 swap_sess = GF_TRUE;
     800             :                                         }
     801             :                                 }
     802             :                         }
     803             : 
     804           0 :                         if (!swap_sess) continue;
     805          22 :                         gf_rtsp_command_del(a_sess->command);
     806          22 :                         a_sess->command = sess->command;
     807          22 :                         a_sess->rtsp = sess->rtsp;
     808          22 :                         sess->rtsp = NULL;
     809          22 :                         sess->command = NULL;
     810          22 :                         memcpy(a_sess->peer_address, sess->peer_address, sizeof(char)*GF_MAX_IP_NAME_LEN);
     811          22 :                         rtspout_del_session(sess);
     812          22 :                         *sess_ptr = sess = a_sess;
     813          22 :                         break;
     814             :                 }
     815             :         }
     816          28 :         if (!sess->sessionID && sess->command->Session) {
     817           0 :                 gf_rtsp_response_reset(sess->response);
     818           0 :                 sess->response->ResponseCode = NC_RTSP_Session_Not_Found;
     819           0 :                 sess->response->CSeq = sess->command->CSeq;
     820           0 :                 rtspout_send_response(ctx, sess);
     821           0 :                 return GF_OK;
     822             :         }
     823             : 
     824             :         //process options
     825          28 :         if (!strcmp(sess->command->method, GF_RTSP_OPTIONS)) {
     826           0 :                 gf_rtsp_response_reset(sess->response);
     827           0 :                 sess->response->ResponseCode = NC_RTSP_OK;
     828           0 :                 sess->response->Public = "DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE";
     829           0 :                 sess->response->CSeq = sess->command->CSeq;
     830           0 :                 rtspout_send_response(ctx, sess);
     831           0 :                 sess->response->Public = NULL;
     832           0 :                 return GF_OK;
     833             :         }
     834             : 
     835             :         //process describe
     836          28 :         if (!strcmp(sess->command->method, GF_RTSP_DESCRIBE)) {
     837             :                 u32 rsp_code = NC_RTSP_OK;
     838             :                 char *res_path = NULL;
     839           5 :                 if (sess->command->service_name) {
     840           5 :                         res_path = strstr(sess->command->service_name, "://");
     841           5 :                         if (res_path) res_path = strchr(res_path+3, '/');
     842           5 :                         if (res_path) res_path++;
     843             :                 }
     844             : 
     845           5 :                 if (res_path && (ctx->mcast==MCAST_MIRROR) ) {
     846           1 :                         GF_RTSPOutSession *a_sess = rtspout_locate_mcast(ctx, res_path);
     847           1 :                         if (a_sess) {
     848           0 :                                 sess->mcast_mirror = a_sess;
     849           0 :                                 rtspout_send_sdp(sess);
     850           0 :                                 return GF_OK;
     851             :                         }
     852             :                 }
     853             : 
     854           5 :                 if (!res_path) {
     855             :                         rsp_code = NC_RTSP_Not_Found;
     856           5 :                 } else if (ctx->dst) {
     857           2 :                         if (sess->server_path) {
     858           2 :                                 char *sepp = strstr(sess->server_path, "://");
     859           2 :                                 if (sepp) sepp = strchr(sepp+3, '/');
     860           2 :                                 if (sepp) sepp++;
     861           2 :                                 if (!sepp || strcmp(sepp, res_path))
     862             :                                         rsp_code = NC_RTSP_Not_Found;
     863             :                         }
     864             :                 }
     865           3 :                 else if ((res_path[0] == '?') || (res_path[0] == '@') ) {
     866           1 :                         if (!ctx->dynurl) {
     867           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTSP] client %s wants dynamic services, not enabled\n", sess->peer_address));
     868             :                                 rsp_code = NC_RTSP_Forbidden;
     869             :                         } else {
     870           1 :                                 GF_List *paths = gf_list_new();
     871             :                                 rsp_code = NC_RTSP_OK;
     872           1 :                                 res_path++;
     873           3 :                                 while (res_path) {
     874             :                                         char sep_c=0;
     875             :                                         char *src_url = NULL;
     876           2 :                                         char *sep = strchr(res_path, '&');
     877           2 :                                         if (!sep) sep = strchr(res_path, '@');
     878           2 :                                         if (sep) {
     879           1 :                                                 sep_c = sep[0];
     880           1 :                                                 sep[0] = 0;
     881             :                                         }
     882             : 
     883           2 :                                         if (!strstr(res_path, "://")) {
     884           2 :                                                 src_url = rtspout_get_local_res_path(ctx, res_path);
     885           2 :                                                 if (src_url) gf_list_add(paths, src_url);
     886             :                                                 else
     887             :                                                         rsp_code = NC_RTSP_Not_Found;
     888             :                                         } else {
     889           0 :                                                 if (gf_filter_is_supported_source(filter, src_url, NULL)) {
     890           0 :                                                         src_url = gf_strdup(res_path);
     891           0 :                                                         gf_list_add(paths, src_url);
     892             :                                                 } else {
     893             :                                                         rsp_code = NC_RTSP_Service_Unavailable;
     894             :                                                 }
     895             :                                         }
     896             : 
     897           2 :                                         if (!sep) break;
     898           1 :                                         sep[0] = sep_c;
     899           1 :                                         res_path = sep+1;
     900           1 :                                         if (rsp_code != NC_RTSP_OK)
     901             :                                                 break;
     902             :                                 }
     903           3 :                                 while (gf_list_count(paths)) {
     904           2 :                                         char *src_url = gf_list_pop_front(paths);
     905           2 :                                         if (rsp_code == NC_RTSP_OK) {
     906             :                                                 //load media service
     907           2 :                                                 e = rtspout_load_media_service(filter, ctx, sess, src_url);
     908           2 :                                                 if (e) {
     909             :                                                         rsp_code = NC_RTSP_Service_Unavailable;
     910             :                                                 }
     911             :                                         }
     912           2 :                                         gf_free(src_url);
     913             :                                 }
     914           1 :                                 gf_list_del(paths);
     915             :                         }
     916             :                 } else {
     917             :                         rsp_code = NC_RTSP_Not_Found;
     918           2 :                         char *src_url = rtspout_get_local_res_path(ctx, res_path);
     919           2 :                         if (src_url) {
     920             :                                 rsp_code = NC_RTSP_OK;
     921             :                                 //load media service
     922           2 :                                 e = rtspout_load_media_service(filter, ctx, sess, src_url);
     923           2 :                                 gf_free(src_url);
     924           2 :                                 if (e) {
     925             :                                         rsp_code = NC_RTSP_Service_Unavailable;
     926             :                                 }
     927             :                         }
     928             :                 }
     929             : 
     930           5 :                 if (sess->service_name) gf_free(sess->service_name);
     931           5 :                 sess->service_name = gf_strdup(sess->command->service_name);
     932             : 
     933           5 :                 if (rsp_code != NC_RTSP_OK) {
     934           0 :                         gf_rtsp_response_reset(sess->response);
     935           0 :                         sess->response->ResponseCode = rsp_code;
     936           0 :                         sess->response->CSeq = sess->command->CSeq;
     937           0 :                         rtspout_send_response(ctx, sess);
     938           0 :                         return GF_OK;
     939             :                 }
     940             : 
     941           5 :                 if (sess->sdp_state==SDP_LOADED) {
     942             :                         //single session, create SDP
     943           2 :                         rtspout_send_sdp(sess);
     944           2 :                         return GF_OK;
     945             :                 }
     946             :                 //store cseq and wait for SDP to be loadable
     947           3 :                 sess->last_cseq = sess->command->CSeq;
     948           3 :                 sess->request_pending = GF_TRUE;
     949           3 :                 return GF_OK;
     950             :         }
     951             : 
     952             :         //forbid any access to the streams, ony describe is allowed
     953          23 :         if (sess->mcast_mirror) {
     954           0 :                 gf_rtsp_response_reset(sess->response);
     955           0 :                 sess->response->ResponseCode = NC_RTSP_Unauthorized;
     956           0 :                 sess->response->CSeq = sess->command->CSeq;
     957           0 :                 rtspout_send_response(ctx, sess);
     958           0 :                 return GF_OK;
     959             :         }
     960             : 
     961             :         //extract control string if any
     962          23 :         if (sess->service_name) {
     963          23 :                 char *sep = strstr(sess->service_name, "://");
     964          23 :                 if (sep) sep = strchr(sep+3, '/');
     965          23 :                 if (sep) sep = strstr(sess->command->service_name, sep);
     966             : 
     967          23 :                 if (sep) {
     968          23 :                         ctrl = strrchr(sess->command->service_name, '/');
     969             :                 }
     970             :         } else {
     971           0 :                 ctrl = strrchr(sess->command->service_name, '/');
     972             :         }
     973          23 :         if (ctrl) {
     974          23 :                 sscanf(ctrl, "/trackID=%d", &stream_ctrl_id);
     975             :         }
     976             : 
     977             :         //process setup
     978          23 :         if (!strcmp(sess->command->method, GF_RTSP_SETUP)) {
     979             :                 char remoteIP[GF_MAX_IP_NAME_LEN];
     980             :                 GF_RTPOutStream *stream = NULL;
     981           6 :                 GF_RTSPTransport *transport = gf_list_get(sess->command->Transports, 0);
     982             :                 u32 rsp_code=NC_RTSP_OK;
     983             :                 Bool enable_multicast = GF_FALSE;
     984             :                 Bool reset_transport_dest = GF_FALSE;
     985             : 
     986           6 :                 if (!ctrl || !transport) {
     987             :                         rsp_code = NC_RTSP_Bad_Request;
     988           6 :                 } else if (sess->sessionID && sess->command->Session && strcmp(sess->sessionID, sess->command->Session)) {
     989             :                         rsp_code = NC_RTSP_Bad_Request;
     990           6 :                 } else if (sess->sessionID && !sess->command->Session) {
     991             :                         rsp_code = NC_RTSP_Not_Implemented;
     992             :                 } else {
     993           6 :                         u32 i, count = gf_list_count(sess->streams);
     994           7 :                         for (i=0; i<count; i++) {
     995           7 :                                 stream = gf_list_get(sess->streams, i);
     996           7 :                                 if (stream_ctrl_id==stream->ctrl_id)
     997             :                                         break;
     998             :                                 stream=NULL;
     999             :                         }
    1000           6 :                         if (!stream_ctrl_id)
    1001             :                                 rsp_code = NC_RTSP_Not_Found;
    1002             :                 }
    1003             : 
    1004           6 :                 if (!stream) {
    1005           0 :                         gf_rtsp_response_reset(sess->response);
    1006           0 :                         sess->response->ResponseCode = rsp_code;
    1007           0 :                         sess->response->CSeq = sess->command->CSeq;
    1008           0 :                         rtspout_send_response(ctx, sess);
    1009           0 :                         return GF_OK;
    1010             :                 }
    1011             : 
    1012           6 :                 gf_rtsp_response_reset(sess->response);
    1013           6 :                 sess->response->CSeq = sess->command->CSeq;
    1014             : 
    1015           6 :                 stream->selected = GF_TRUE;
    1016           6 :                 if (transport && (rsp_code==NC_RTSP_OK) ) {
    1017           6 :                         if (!transport->IsInterleaved) {
    1018           5 :                                 u32 st_idx = gf_list_find(sess->streams, stream);
    1019           5 :                                 transport->port_first = ctx->firstport + 2 * st_idx;
    1020           5 :                                 transport->port_last = transport->port_first + 1;
    1021           5 :                                 if (sess->interleave)
    1022             :                                         rsp_code = NC_RTSP_Not_Implemented;
    1023             :                         } else {
    1024           1 :                                 if (!sess->sessionID) {
    1025           1 :                                         sess->interleave = GF_TRUE;
    1026           0 :                                 } else if (!sess->interleave) {
    1027             :                                         rsp_code = NC_RTSP_Not_Implemented;
    1028             :                                 }
    1029             :                         }
    1030           6 :                         transport->SSRC = rand();
    1031           6 :                         transport->is_sender = GF_TRUE;
    1032             : 
    1033           6 :                         if (transport->IsUnicast) {
    1034           5 :                                 if (transport->destination && gf_sk_is_multicast_address(transport->destination)) {
    1035             :                                         rsp_code = NC_RTSP_Bad_Request;
    1036           5 :                                 } else if (!transport->destination) {
    1037           5 :                                         transport->destination = sess->peer_address;
    1038             :                                         reset_transport_dest = GF_TRUE;
    1039             :                                 }
    1040           5 :                                 if (sess->multicast_ip)
    1041             :                                         rsp_code = NC_RTSP_Forbidden;
    1042             : 
    1043           5 :                                 if (ctx->dst && (strstr(ctx->dst, "://127.0.0.1") || strstr(ctx->dst, "://localhost") || strstr(ctx->dst, "://::1/128") ) ) {
    1044           2 :                                         if (!reset_transport_dest && transport->destination) gf_free(transport->destination);
    1045           2 :                                         transport->destination = "127.0.0.1";
    1046             :                                         reset_transport_dest = GF_TRUE;
    1047             :                                 }
    1048             :                         }
    1049             :                         else {
    1050           1 :                                 if (transport->destination && !gf_sk_is_multicast_address(transport->destination)) {
    1051             :                                         rsp_code = NC_RTSP_Bad_Request;
    1052             :                                 } else {
    1053           1 :                                         if (ctx->mcast != MCAST_OFF) {
    1054             :                                                 enable_multicast = GF_TRUE;
    1055             :                                                 //we don't allow seting up streams on different mcast addresses
    1056           1 :                                                 if (!sess->multicast_ip) {
    1057           1 :                                                         sess->multicast_ip = transport->destination; //detach memory
    1058             :                                                 }
    1059           1 :                                                 transport->source = transport->destination = sess->multicast_ip;
    1060             :                                                 reset_transport_dest = GF_TRUE;
    1061             : 
    1062           1 :                                                 transport->client_port_first = 0;
    1063           1 :                                                 transport->client_port_last = 0;
    1064           1 :                                                 if (ctx->ttl) transport->TTL = ctx->ttl;
    1065           1 :                                                 if (!transport->TTL) transport->TTL = 1;
    1066           1 :                                                 stream->mcast_port = transport->port_first;
    1067           1 :                                                 rtspout_get_next_mcast_port(ctx, sess, &stream->mcast_port);
    1068           1 :                                                 transport->port_first = stream->mcast_port;
    1069           1 :                                                 transport->port_last = stream->mcast_port+1;
    1070             :                                         } else {
    1071           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] SETUP requests a multicast to %s, not allowed\n", transport->destination));
    1072             :                                                 rsp_code = NC_RTSP_Forbidden;
    1073             :                                         }
    1074             :                                 }
    1075             :                         }
    1076             :                 }
    1077             : 
    1078           6 :                 if (rsp_code != NC_RTSP_OK) {
    1079           0 :                         sess->response->ResponseCode = rsp_code;
    1080             :                 } else {
    1081           6 :                         e = gf_rtp_streamer_init_rtsp(stream->rtp, ctx->mtu, transport, ctx->ifce);
    1082           6 :                         if (e) {
    1083           0 :                                 sess->response->ResponseCode = NC_RTSP_Internal_Server_Error;
    1084             :                         } else {
    1085           6 :                                 if (!sess->sessionID)
    1086           5 :                                         sess->sessionID = gf_rtsp_generate_session_id(sess->rtsp);
    1087             : 
    1088           6 :                                 sess->response->ResponseCode = NC_RTSP_OK;
    1089           6 :                                 sess->response->Session = sess->sessionID;
    1090           6 :                                 if (!enable_multicast) {
    1091           5 :                                         gf_rtsp_get_session_ip(sess->rtsp, remoteIP);
    1092           5 :                                         if (!reset_transport_dest && transport->destination) gf_free(transport->destination);
    1093           5 :                                         transport->destination = NULL;
    1094           5 :                                         transport->source = remoteIP;
    1095             :                                 }
    1096           6 :                                 gf_list_add(sess->response->Transports, transport);
    1097             :                         }
    1098             :                 }
    1099             : 
    1100             : 
    1101           6 :                 if (sess->interleave) {
    1102           1 :                         stream->rtp_id = transport->rtpID;
    1103           1 :                         stream->rtcp_id = transport->rtcpID;
    1104           1 :                         gf_rtp_streamer_set_interleave_callbacks(stream->rtp, rtspout_interleave_packet, sess, stream);
    1105             :                 }
    1106             : 
    1107           6 :                 rtspout_send_response(ctx, sess);
    1108           6 :                 gf_list_reset(sess->response->Transports);
    1109           6 :                 sess->response->Session = NULL;
    1110           6 :                 if (reset_transport_dest)
    1111           6 :                         transport->destination = NULL;
    1112             : 
    1113           6 :                 transport->source = NULL;
    1114           6 :                 return GF_OK;
    1115             :         }
    1116             : 
    1117             :         //process play
    1118          17 :         if (!strcmp(sess->command->method, GF_RTSP_PLAY)) {
    1119             :                 Double start_range=-1;
    1120             :                 u32 rsp_code=NC_RTSP_OK;
    1121           6 :                 if (sess->sessionID && sess->command->Session && strcmp(sess->sessionID, sess->command->Session)) {
    1122             :                         rsp_code = NC_RTSP_Bad_Request;
    1123           6 :                 } else if (!sess->command->Session || !sess->sessionID) {
    1124             :                         rsp_code = NC_RTSP_Bad_Request;
    1125             :                 }
    1126           6 :                 if (sess->command->Range)
    1127           6 :                         start_range = sess->command->Range->start;
    1128             : 
    1129           6 :                 if (stream_ctrl_id) {
    1130             :                         rsp_code=NC_RTSP_Only_Aggregate_Operation_Allowed;
    1131             :                 }
    1132             : 
    1133           6 :                 if (rsp_code!=NC_RTSP_OK) {
    1134           0 :                         gf_rtsp_response_reset(sess->response);
    1135           0 :                         sess->response->ResponseCode = rsp_code;
    1136           0 :                         sess->response->CSeq = sess->command->CSeq;
    1137           0 :                         rtspout_send_response(ctx, sess);
    1138           0 :                         return GF_OK;
    1139             :                 } else {
    1140             :                         //loop enabled, only if multicast session or single session mode
    1141           6 :                         if (ctx->loop && !sess->loop_disabled && (sess->single_session || sess->multicast_ip))
    1142           3 :                                 sess->loop = GF_TRUE;
    1143             : 
    1144           6 :                         if ((sess->play_state==2) && !sess->command->Range) {
    1145           0 :                                 u64 ellapsed_us = gf_sys_clock_high_res() - sess->pause_sys_clock;
    1146           0 :                                 sess->pause_sys_clock = 0;
    1147           0 :                                 sess->play_state = 1;
    1148           0 :                                 sess->sys_clock_at_init += ellapsed_us;
    1149             : 
    1150           0 :                                 gf_rtsp_response_reset(sess->response);
    1151           0 :                                 sess->response->ResponseCode = NC_RTSP_OK;
    1152           0 :                                 sess->response->CSeq = sess->command->CSeq;
    1153           0 :                                 rtspout_send_response(ctx, sess);
    1154             :                         } else {
    1155           6 :                                 sess->play_state = 1;
    1156           6 :                                 sess->sys_clock_at_init = 0;
    1157           6 :                                 sess->start_range = start_range;
    1158           6 :                                 sess->last_cseq = sess->command->CSeq;
    1159           6 :                                 sess->request_pending = GF_TRUE;
    1160           6 :                                 rtspout_send_event(sess, GF_FALSE, GF_TRUE, start_range);
    1161             :                         }
    1162             :                 }
    1163             :                 return GF_OK;
    1164             :         }
    1165             : 
    1166             :         //process pause (we don't implement range on pause yet)
    1167          11 :         if (!strcmp(sess->command->method, GF_RTSP_PAUSE)) {
    1168           6 :                 if (sess->play_state!=2) {
    1169           5 :                         sess->play_state = 2;
    1170           5 :                         sess->pause_sys_clock = gf_sys_clock_high_res();
    1171             :                 }
    1172           6 :                 gf_rtsp_response_reset(sess->response);
    1173           6 :                 sess->response->ResponseCode = NC_RTSP_OK;
    1174           6 :                 sess->response->CSeq = sess->command->CSeq;
    1175           6 :                 rtspout_send_response(ctx, sess);
    1176           6 :                 return GF_OK;
    1177             :         }
    1178             :         //process teardown
    1179           5 :         if (!strcmp(sess->command->method, GF_RTSP_TEARDOWN)) {
    1180           5 :                 sess->play_state = 0;
    1181           5 :                 rtspout_send_event(sess, GF_TRUE, GF_FALSE, 0);
    1182             : 
    1183           5 :                 gf_rtsp_response_reset(sess->response);
    1184           5 :                 sess->response->ResponseCode = NC_RTSP_OK;
    1185           5 :                 sess->response->CSeq = sess->command->CSeq;
    1186           5 :                 rtspout_send_response(ctx, sess);
    1187             : 
    1188           5 :                 rtspout_send_event(sess, GF_TRUE, GF_FALSE, 0);
    1189             : 
    1190           5 :                 rtspout_del_session(sess);
    1191           5 :                 rtspout_check_last_sess(ctx);
    1192           5 :                 *sess_ptr = NULL;
    1193           5 :                 return GF_OK;
    1194             :         }
    1195             :         return GF_OK;
    1196             : }
    1197             : 
    1198             : 
    1199        2868 : static GF_Err rtspout_process(GF_Filter *filter)
    1200             : {
    1201             :         GF_Err e=GF_OK;
    1202             :         u32 i, count;
    1203        2868 :         GF_RTSPOutCtx *ctx = gf_filter_get_udta(filter);
    1204             : 
    1205        2868 :         if (ctx->done)
    1206             :                 return GF_EOS;
    1207             : 
    1208        2824 :         ctx->next_wake_us = 50000;
    1209        2824 :         e = rtspout_check_new_session(ctx, GF_FALSE);
    1210        2824 :         if (e==GF_IP_NETWORK_EMPTY) {
    1211             :                 e = GF_OK;
    1212             :         }
    1213             : 
    1214        2824 :         count = gf_list_count(ctx->sessions);
    1215        5607 :         for (i=0; i<count; i++) {
    1216             :                 GF_Err sess_err;
    1217        2783 :                 GF_RTSPOutSession *sess = gf_list_get(ctx->sessions, i);
    1218        2783 :                 sess_err = rtspout_process_session_signaling(filter, ctx, &sess);
    1219        2783 :                 if (sess_err) e |= sess_err;
    1220             : 
    1221        2783 :                 if (sess && sess->play_state) {
    1222        2712 :                         sess_err = rtspout_process_rtp(filter, ctx, sess);
    1223        2712 :                         if (sess_err) e |= sess_err;
    1224             :                 }
    1225             :         }
    1226             : 
    1227        2824 :         if (e==GF_EOS) {
    1228           9 :                 if (ctx->dst) return GF_EOS;
    1229             :                 e=GF_OK;
    1230             :         }
    1231             : 
    1232        2824 :         if (ctx->next_wake_us)
    1233        2429 :                 gf_filter_ask_rt_reschedule(filter, ctx->next_wake_us);
    1234             : 
    1235             :         return e;
    1236             : }
    1237             : 
    1238        2198 : static GF_FilterProbeScore rtspout_probe_url(const char *url, const char *mime)
    1239             : {
    1240        2198 :         if (!strnicmp(url, "rtsp://", 7)) return GF_FPROBE_SUPPORTED;
    1241        2196 :         return GF_FPROBE_NOT_SUPPORTED;
    1242             : }
    1243             : 
    1244             : static const GF_FilterCapability RTSPOutCaps[] =
    1245             : {
    1246             :         //anything else (not file and framed) result in manifest PID
    1247             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    1248             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
    1249             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
    1250             : 
    1251             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    1252             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, "sdp"),
    1253             :         CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, "application/sdp"),
    1254             :         {0},
    1255             :         //anything else (not file and framed) result in media pids not file
    1256             :         CAP_UINT(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER,  GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
    1257             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_UNFRAMED, GF_TRUE),
    1258             : };
    1259             : 
    1260             : 
    1261             : #define OFFS(_n)        #_n, offsetof(GF_RTSPOutCtx, _n)
    1262             : static const GF_FilterArgs RTSPOutArgs[] =
    1263             : {
    1264             :         { OFFS(dst), "location of destination resource - see filter help", GF_PROP_NAME, NULL, NULL, 0},
    1265             :         { OFFS(port), "server port", GF_PROP_UINT, "554", NULL, 0},
    1266             :         { OFFS(firstport), "port for first stream in session", GF_PROP_UINT, "6000", NULL, GF_FS_ARG_HINT_ADVANCED},
    1267             :         { OFFS(mtu), "size of RTP MTU in bytes", GF_PROP_UINT, "1460", NULL, 0},
    1268             :         { OFFS(ttl), "time-to-live for multicast packets. A value of 0 uses client requested TTL, or 1", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    1269             :         { OFFS(ifce), "default network interface to use", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
    1270             :         { OFFS(payt), "payload type to use for dynamic configs", GF_PROP_UINT, "96", "96-127", GF_FS_ARG_HINT_EXPERT},
    1271             :         { OFFS(mpeg4), "send all streams using MPEG-4 generic payload format if posible", GF_PROP_BOOL, "false", NULL, 0},
    1272             :         { OFFS(delay), "send delay for packet (negative means send earlier)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
    1273             :         { OFFS(tt), "time tolerance in microseconds. Whenever schedule time minus realtime is below this value, the packet is sent right away", GF_PROP_UINT, "1000", NULL, GF_FS_ARG_HINT_ADVANCED},
    1274             :         { OFFS(runfor), "run the session for the given time in ms. Negative value means run for ever (if loop) or source duration, 0 only outputs the sdp", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_EXPERT},
    1275             :         { OFFS(tso), "set timestamp offset in microseconds. Negative value means random initial timestamp", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_EXPERT},
    1276             :         { OFFS(xps), "force parameter set injection at each SAP. If not set, only inject if different from SDP ones", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    1277             :         { OFFS(latm), "use latm for AAC payload format", GF_PROP_BOOL, "false", NULL, 0},
    1278             :         { OFFS(mounts), "list of directories to expose in server mode", GF_PROP_STRING_LIST, NULL, NULL, 0},
    1279             :         { OFFS(block_size), "block size used to read TCP socket", GF_PROP_UINT, "10000", NULL, GF_FS_ARG_HINT_ADVANCED},
    1280             :         { OFFS(maxc), "maximum number of connections", GF_PROP_UINT, "100", NULL, GF_FS_ARG_HINT_ADVANCED},
    1281             :         { OFFS(user_agent), "user agent string, by default solved from GPAC preferences", GF_PROP_STRING, "$GUA", NULL, 0},
    1282             :         { OFFS(close), "close RTSP connection after each request, except when RTP over RTSP is used", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
    1283             :         { OFFS(loop), "loop all streams in session (not always possible depending on source type) - see filter help", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
    1284             :         { OFFS(dynurl), "allow dynamic service assembly - see filter help", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
    1285             :         { OFFS(mcast), "control multicast setup of a session\n"
    1286             :                                 "- off: clients are never allowed to create a multicast\n"
    1287             :                                 "- on: clients can create multicast sessions\n"
    1288             :                                 "- mirror: clients can create a multicast session. Any later request to the same URL will use that multicast session"
    1289             :                 , GF_PROP_UINT, "off", "off|on|mirror", GF_FS_ARG_HINT_EXPERT},
    1290             : 
    1291             :         {0}
    1292             : };
    1293             : 
    1294             : 
    1295             : GF_FilterRegister RTSPOutRegister = {
    1296             :         .name = "rtspout",
    1297             :         GF_FS_SET_DESCRIPTION("RTSP Server")
    1298             :         GF_FS_SET_HELP("The RTSP server partially implements RTSP 1.0, with support for OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE and TEARDOWN.\n"\
    1299             :                 "Multiple PLAY ranges are not supported, PLAY range end is not supported, PAUSE range is not supported.\n"
    1300             :                 "Only aggregated control is supported for PLAY and PAUSE, PAUSE/PLAY on single stream is not supported.\n"\
    1301             :                 "The server only runs on TCP, and handles request in sequence (will not probe for commands until previous response was sent).\n"\
    1302             :                 "The server supports both RTP over UDP delivery and RTP interleaved over RTSP delivery.\n"\
    1303             :                 "\n"\
    1304             :                 "The filter can work as a simple output filter by specifying the [-dst]() option:\n"\
    1305             :                 "EX gpac -i source -o rtsp://myip/sessionname\n"\
    1306             :                 "EX gpac -i source dst=rtsp://myip/sessionname\n"\
    1307             :                 "In this mode, only one session is possible. It is possible to [-loop]() the input source(s).\n"\
    1308             :                 "\n"\
    1309             :                 "The filter can work as a regular RTSP server by specifying the [-mounts]() option to indicate paths of media file to be served:\n"\
    1310             :                 "EX gpac rtspout:mounts=mydir1,mydir2\n"\
    1311             :                 "In server mode, it is possible to load any source supported by gpac by setting the option [-dynurl]().\n"\
    1312             :                 "The expected syntax of the dynamic RTSP URLs is `rtsp://servername/?URL1[&URLN]` or `rtsp://servername/@URL1[@URLN]` \n"\
    1313             :                 "Each URL can be absolute or local, in which case it is resolved against the mount point(s).\n"\
    1314             :                 "EX gpac -i rtsp://localhost/?pipe://mynamepipe&myfile.mp4 [dst filters]\n"\
    1315             :                 "The server will resolve this URL in a new session containing streams from myfile.mp4 and streams from pipe mynamepipe.\n"\
    1316             :                 "When setting [-runfor]() in server mode, the server will exit at the end of the last session being closed.\n"\
    1317             :                 "\n"\
    1318             :                 "In both modes, clients can setup multicast if the [-mcast]() option is `on` or `mirror`.\n"\
    1319             :                 "When [-mcast]() is set to `mirror` mode, any DESCRIBE command on a resource already delivered through a multicast session will use that multicast.\n"\
    1320             :                 "Consequently, only DESCRIBE methods are processed for such sessions, other methods will return Unauthorized.\n"\
    1321             :                 "\n"\
    1322             :                 "The scheduling algorithm and RTP options are the same as the RTP output filter, see [gpac -h rtpout](rtpout)\n"\
    1323             :         )
    1324             :         .private_size = sizeof(GF_RTSPOutCtx),
    1325             :         .max_extra_pids = -1,
    1326             :         .args = RTSPOutArgs,
    1327             :         .probe_url = rtspout_probe_url,
    1328             :         .initialize = rtspout_initialize,
    1329             :         .finalize = rtspout_finalize,
    1330             :         SETCAPS(RTSPOutCaps),
    1331             :         .configure_pid = rtspout_configure_pid,
    1332             :         .process = rtspout_process
    1333             : };
    1334             : 
    1335             : 
    1336        2877 : const GF_FilterRegister *rtspout_register(GF_FilterSession *session)
    1337             : {
    1338        2877 :         return &RTSPOutRegister;
    1339             : }
    1340             : 
    1341             : #else
    1342             : 
    1343             : const GF_FilterRegister *rtspout_register(GF_FilterSession *session)
    1344             : {
    1345             :         return NULL;
    1346             : }
    1347             : 
    1348             : #endif /* !defined(GPAC_DISABLE_STREAMING) */

Generated by: LCOV version 1.13