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) */
|