Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2017
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / RTP/RTSP input 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 "in_rtp.h"
27 : #include <gpac/internal/ietf_dev.h>
28 :
29 : #ifndef GPAC_DISABLE_STREAMING
30 :
31 : static Bool rtpin_stream_is_valid(GF_RTPIn *rtp, GF_RTPInStream *stream)
32 : {
33 24 : u32 i=0;
34 : GF_RTPInStream *st;
35 28 : while ((st = (GF_RTPInStream *)gf_list_enum(rtp->streams, &i))) {
36 28 : if (st == stream) return GF_TRUE;
37 : }
38 : return GF_FALSE;
39 : }
40 :
41 : /*this prevent sending teardown on session with running channels*/
42 6 : static Bool rtpin_rtsp_is_active(GF_RTPInStream *stream)
43 : {
44 : GF_RTPInStream *a_st;
45 : u32 i, count;
46 6 : i = count = 0;
47 14 : while ((a_st = (GF_RTPInStream *)gf_list_enum(stream->rtpin->streams, &i))) {
48 8 : if (a_st->rtsp != stream->rtsp) continue;
49 : /*count only active channels*/
50 8 : if (a_st->status == RTP_Running) count++;
51 : }
52 6 : return count ? GF_TRUE : GF_FALSE;
53 : }
54 :
55 : static void rtpin_rtsp_queue_command(GF_RTPInRTSP *sess, GF_RTPInStream *stream, GF_RTSPCommand *com, Bool needs_sess_id)
56 : {
57 : if (needs_sess_id) {
58 23 : com->Session = sess->session_id;
59 : }
60 28 : gf_list_add(sess->rtsp_commands, com);
61 : }
62 :
63 :
64 :
65 : /*
66 : channel setup functions
67 : */
68 :
69 6 : void rtpin_rtsp_setup_send(GF_RTPInStream *stream)
70 : {
71 : GF_RTSPCommand *com;
72 : GF_RTSPTransport *trans;
73 :
74 6 : com = gf_rtsp_command_new();
75 6 : com->method = gf_strdup(GF_RTSP_SETUP);
76 :
77 : //setup ports if unicast non interleaved or multicast
78 6 : if (gf_rtp_is_unicast(stream->rtp_ch) && (stream->rtpin->interleave != 1) && !gf_rtp_is_interleaved(stream->rtp_ch) ) {
79 5 : gf_rtp_set_ports(stream->rtp_ch, stream->rtpin->firstport);
80 1 : } else if (stream->rtpin->force_mcast) {
81 0 : gf_rtp_set_ports(stream->rtp_ch, stream->rtpin->firstport);
82 : }
83 :
84 6 : trans = gf_rtsp_transport_clone((GF_RTSPTransport *)gf_rtp_get_transport(stream->rtp_ch));
85 :
86 : /*some servers get confused when trying to resetup on the same remote ports, so reset info*/
87 6 : trans->port_first = trans->port_last = 0;
88 6 : trans->SSRC = 0;
89 :
90 : /*override transport: */
91 : /*1: multicast forced*/
92 6 : if (stream->rtpin->force_mcast) {
93 1 : trans->IsUnicast = GF_FALSE;
94 1 : trans->destination = gf_strdup(stream->rtpin->force_mcast);
95 1 : trans->TTL = stream->rtpin->ttl;
96 1 : if (trans->Profile) gf_free(trans->Profile);
97 1 : trans->Profile = gf_strdup(GF_RTSP_PROFILE_RTP_AVP);
98 1 : if (!(stream->rtsp->flags & RTSP_DSS_SERVER) ) {
99 1 : trans->port_first = trans->client_port_first;
100 1 : trans->port_last = trans->client_port_last;
101 : /*this is correct but doesn't work with DSS: the server expects "client_port" to indicate
102 : the multicast port, not "port" - this will send both*/
103 : //trans->client_port_first = trans->client_port_last = 0;
104 : }
105 1 : gf_rtp_setup_transport(stream->rtp_ch, trans, NULL);
106 : }
107 : /*2: RTP over RTSP forced*/
108 5 : else if (stream->rtsp->flags & RTSP_FORCE_INTER) {
109 1 : if (trans->Profile) gf_free(trans->Profile);
110 1 : trans->Profile = gf_strdup(GF_RTSP_PROFILE_RTP_AVP_TCP);
111 : //some servers expect the interleaved to be set during the setup request
112 1 : trans->IsInterleaved = GF_TRUE;
113 1 : trans->rtpID = 2*gf_list_find(stream->rtpin->streams, stream);
114 1 : trans->rtcpID = trans->rtpID+1;
115 1 : gf_rtp_setup_transport(stream->rtp_ch, trans, NULL);
116 : }
117 :
118 6 : if (trans->source) {
119 6 : gf_free(trans->source);
120 6 : trans->source = NULL;
121 : }
122 :
123 : /*turn off interleaving in case of re-setup, some servers don't like it (we still signal it
124 : through RTP/AVP/TCP profile so it's OK)*/
125 : // trans->IsInterleaved = 0;
126 6 : gf_list_add(com->Transports, trans);
127 6 : if (strlen(stream->control)) com->ControlString = gf_strdup(stream->control);
128 :
129 6 : com->user_data = stream;
130 6 : stream->status = RTP_WaitingForAck;
131 :
132 6 : rtpin_rtsp_queue_command(stream->rtsp, stream, com, GF_TRUE);
133 6 : }
134 :
135 : /*filter setup if no session (rtp only)*/
136 6 : GF_Err rtpin_stream_setup(GF_RTPInStream *stream, RTPIn_StreamDescribe *ch_desc)
137 : {
138 : GF_Err resp;
139 :
140 : /*assign ES_ID of the channel*/
141 6 : if (ch_desc && !stream->ES_ID && ch_desc->ES_ID) stream->ES_ID = ch_desc->ES_ID;
142 :
143 6 : stream->status = RTP_Setup;
144 :
145 : /*assign channel handle if not done*/
146 6 : if (ch_desc && stream->opid) {
147 : assert(stream->opid == ch_desc->opid);
148 0 : } else if (!stream->opid && stream->rtsp && !stream->rtsp->satip) {
149 : assert(ch_desc);
150 : assert(ch_desc->opid);
151 0 : stream->opid = ch_desc->opid;
152 : }
153 :
154 : /*no session , setup for pure rtp*/
155 6 : if (!stream->rtsp) {
156 0 : stream->flags |= RTP_CONNECTED;
157 : /*init rtp*/
158 0 : resp = rtpin_stream_init(stream, GF_FALSE);
159 : /*send confirmation to user*/
160 0 : rtpin_stream_ack_connect(stream, resp);
161 : } else {
162 6 : rtpin_rtsp_setup_send(stream);
163 : }
164 6 : return GF_OK;
165 : }
166 :
167 0 : static GF_Err rtpin_rtsp_tcp_send_report(void *par, void *par2, Bool is_rtcp, u8 *pck, u32 pck_size)
168 : {
169 0 : return GF_OK;
170 : }
171 :
172 6 : void rtpin_rtsp_setup_process(GF_RTPInRTSP *sess, GF_RTSPCommand *com, GF_Err e)
173 : {
174 : GF_RTPInStream *stream;
175 : u32 i;
176 : GF_RTSPTransport *trans;
177 :
178 6 : stream = (GF_RTPInStream *)com->user_data;
179 6 : if (e) goto exit;
180 :
181 6 : switch (sess->rtsp_rsp->ResponseCode) {
182 : case NC_RTSP_OK:
183 : break;
184 : case NC_RTSP_Not_Found:
185 : e = GF_STREAM_NOT_FOUND;
186 : goto exit;
187 0 : default:
188 : e = GF_SERVICE_ERROR;
189 0 : goto exit;
190 : }
191 : e = GF_SERVICE_ERROR;
192 6 : if (!stream) goto exit;
193 :
194 : /*assign session ID*/
195 6 : if (!sess->rtsp_rsp->Session) {
196 : e = GF_SERVICE_ERROR;
197 : goto exit;
198 : }
199 6 : if (!sess->session_id) sess->session_id = gf_strdup(sess->rtsp_rsp->Session);
200 : assert(!stream->session_id);
201 :
202 : /*transport setup: break at the first correct transport */
203 6 : i=0;
204 12 : while ((trans = (GF_RTSPTransport *)gf_list_enum(sess->rtsp_rsp->Transports, &i))) {
205 : /*copy over previous ports (hack for some servers overriding client ports)*/
206 6 : if (stream->rtpin->use_client_ports)
207 0 : gf_rtp_get_ports(stream->rtp_ch, &trans->client_port_first, &trans->client_port_last);
208 :
209 6 : if (gf_rtp_is_interleaved(stream->rtp_ch) && !trans->IsInterleaved) {
210 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Requested interleaved RTP over RTSP but server did not setup interleave - cannot process command\n"));
211 : e = GF_REMOTE_SERVICE_ERROR;
212 0 : continue;
213 : }
214 :
215 6 : e = gf_rtp_setup_transport(stream->rtp_ch, trans, gf_rtsp_get_server_name(sess->session));
216 6 : if (!e) break;
217 : }
218 6 : if (e) goto exit;
219 :
220 6 : e = rtpin_stream_init(stream, GF_FALSE);
221 6 : if (e) goto exit;
222 6 : stream->status = RTP_Connected;
223 :
224 : //in case this is TCP channel, setup callbacks
225 6 : stream->flags &= ~RTP_INTERLEAVED;
226 6 : if (gf_rtp_is_interleaved(stream->rtp_ch)) {
227 1 : stream->flags |= RTP_INTERLEAVED;
228 1 : gf_rtsp_set_interleave_callback(sess->session, rtpin_rtsp_data_cbk);
229 :
230 1 : gf_rtp_set_interleave_callbacks(stream->rtp_ch, rtpin_rtsp_tcp_send_report, stream, stream);
231 1 : sess->flags |= RTSP_TCP_FLUSH;
232 : #ifdef GPAC_ENABLE_COVERAGE
233 1 : if (gf_sys_is_cov_mode())
234 : rtpin_rtsp_tcp_send_report(NULL, NULL, GF_FALSE, NULL, 0);
235 : #endif
236 : }
237 :
238 6 : if (sess->satip) {
239 : RTPIn_StreamControl *ch_ctrl = NULL;
240 0 : GF_RTSPCommand *a_com = gf_rtsp_command_new();
241 0 : a_com->method = gf_strdup(GF_RTSP_PLAY);
242 0 : GF_SAFEALLOC(ch_ctrl, RTPIn_StreamControl);
243 0 : if (ch_ctrl) {
244 0 : ch_ctrl->stream = stream;
245 0 : a_com->user_data = ch_ctrl;
246 : }
247 0 : rtpin_rtsp_queue_command(sess, stream, a_com, GF_TRUE);
248 : }
249 :
250 12 : exit:
251 : /*confirm only on first connect, otherwise this is a re-SETUP of the rtsp session, not the channel*/
252 6 : if (stream && ! (stream->flags & RTP_CONNECTED) ) {
253 6 : if (!e)
254 6 : stream->flags |= RTP_CONNECTED;
255 6 : rtpin_stream_ack_connect(stream, e);
256 : }
257 6 : com->user_data = NULL;
258 6 : }
259 :
260 :
261 : /*
262 : session/channel describe functions
263 : */
264 : /*filter describe commands in case of ESD URLs*/
265 5 : Bool rtpin_rtsp_describe_preprocess(GF_RTPInRTSP *sess, GF_RTSPCommand *com)
266 : {
267 : GF_RTPInStream *stream;
268 : RTPIn_StreamDescribe *ch_desc;
269 : /*not a channel describe*/
270 5 : if (!com->user_data) {
271 5 : rtpin_send_message(sess->rtpin, GF_OK, "Connecting...");
272 5 : return GF_TRUE;
273 : }
274 :
275 : ch_desc = (RTPIn_StreamDescribe *)com->user_data;
276 0 : stream = rtpin_find_stream(sess->rtpin, NULL, ch_desc->ES_ID, ch_desc->esd_url, GF_FALSE);
277 0 : if (!stream) return GF_TRUE;
278 :
279 : /*channel has been described already, skip describe and send setup directly*/
280 0 : rtpin_stream_setup(stream, ch_desc);
281 :
282 0 : if (ch_desc->esd_url) gf_free(ch_desc->esd_url);
283 0 : gf_free(ch_desc);
284 0 : return GF_FALSE;
285 : }
286 :
287 : /*process describe reply*/
288 5 : GF_Err rtpin_rtsp_describe_process(GF_RTPInRTSP *sess, GF_RTSPCommand *com, GF_Err e)
289 : {
290 : GF_RTPInStream *stream;
291 : RTPIn_StreamDescribe *ch_desc;
292 :
293 : stream = NULL;
294 5 : ch_desc = (RTPIn_StreamDescribe *)com->user_data;
295 5 : if (e) goto exit;
296 :
297 5 : switch (sess->rtsp_rsp->ResponseCode) {
298 : //TODO handle all 3xx codes (redirections)
299 0 : case NC_RTSP_Multiple_Choice:
300 0 : e = ch_desc ? GF_STREAM_NOT_FOUND : GF_URL_ERROR;
301 : goto exit;
302 : case NC_RTSP_Not_Found:
303 : e = GF_URL_ERROR;
304 : goto exit;
305 : case NC_RTSP_OK:
306 : break;
307 0 : default:
308 : //we should have a basic error code mapping here
309 : e = GF_SERVICE_ERROR;
310 0 : goto exit;
311 : }
312 :
313 : stream = NULL;
314 5 : if (ch_desc) {
315 0 : stream = rtpin_find_stream(sess->rtpin, ch_desc->opid, ch_desc->ES_ID, ch_desc->esd_url, GF_FALSE);
316 : } else {
317 5 : rtpin_send_message(sess->rtpin, GF_OK, "Connected");
318 : }
319 :
320 : /*error on loading SDP is done internally*/
321 5 : rtpin_load_sdp(sess->rtpin, sess->rtsp_rsp->body, sess->rtsp_rsp->Content_Length, stream);
322 :
323 5 : if (!ch_desc) goto exit;
324 0 : if (!stream) {
325 : e = GF_STREAM_NOT_FOUND;
326 : goto exit;
327 : }
328 0 : e = rtpin_stream_setup(stream, ch_desc);
329 :
330 10 : exit:
331 5 : com->user_data = NULL;
332 5 : if (e) {
333 0 : if (!ch_desc) {
334 0 : sess->connect_error = e;
335 0 : return e;
336 0 : } else if (stream) {
337 0 : rtpin_stream_ack_connect(stream, e);
338 : } else {
339 : //TODO - check if this is correct
340 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTPIn] code not tested file %s line %d !!\n", __FILE__, __LINE__));
341 0 : gf_filter_setup_failure(sess->rtpin->filter, e);
342 : }
343 : }
344 5 : if (ch_desc) gf_free(ch_desc);
345 : return GF_OK;
346 : }
347 :
348 : /*send describe*/
349 5 : void rtpin_rtsp_describe_send(GF_RTPInRTSP *sess, char *esd_url, GF_FilterPid *opid)
350 : {
351 : RTPIn_StreamDescribe *ch_desc;
352 : GF_RTSPCommand *com;
353 :
354 : /*locate the channel by URL - if we have one, this means the channel is already described
355 : this happens when 2 ESD with URL use the same RTSP service - skip describe and send setup*/
356 5 : if (esd_url || opid) {
357 0 : GF_RTPInStream *stream = rtpin_find_stream(sess->rtpin, opid, 0, esd_url, GF_FALSE);
358 0 : if (stream) {
359 0 : if (!stream->opid) stream->opid = opid;
360 0 : switch (stream->status) {
361 0 : case RTP_Connected:
362 : case RTP_Running:
363 0 : rtpin_stream_ack_connect(stream, GF_OK);
364 0 : return;
365 : default:
366 : break;
367 : }
368 0 : ch_desc = (RTPIn_StreamDescribe *)gf_malloc(sizeof(RTPIn_StreamDescribe));
369 0 : ch_desc->esd_url = esd_url ? gf_strdup(esd_url) : NULL;
370 0 : ch_desc->opid = opid;
371 0 : rtpin_stream_setup(stream, ch_desc);
372 :
373 0 : if (esd_url) gf_free(ch_desc->esd_url);
374 0 : gf_free(ch_desc);
375 0 : return;
376 : }
377 : /*channel not found, send describe on service*/
378 : }
379 :
380 : /*send describe*/
381 5 : com = gf_rtsp_command_new();
382 5 : if (!sess->satip) {
383 5 : com->method = gf_strdup(GF_RTSP_DESCRIBE);
384 : } else {
385 : GF_Err e;
386 : GF_RTSPTransport *trans;
387 : GF_RTPInStream *stream = NULL;
388 :
389 0 : com->method = gf_strdup(GF_RTSP_SETUP);
390 :
391 : /*setup transport ports*/
392 0 : GF_SAFEALLOC(trans, GF_RTSPTransport);
393 0 : if (trans) {
394 0 : trans->IsUnicast = GF_TRUE;
395 0 : trans->client_port_first = sess->rtpin->satip_port;
396 0 : trans->client_port_last = sess->rtpin->satip_port+1;
397 0 : trans->Profile = gf_strdup(GF_RTSP_PROFILE_RTP_AVP);
398 0 : gf_list_add(com->Transports, trans);
399 : }
400 :
401 : /*hardcoded channel*/
402 0 : stream = rtpin_stream_new_satip(sess->rtpin, sess->satip_server);
403 0 : if (!stream) {
404 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("SAT>IP: couldn't create the RTP stream.\n"));
405 : return;
406 : }
407 0 : e = rtpin_add_stream(sess->rtpin, stream, "*");
408 0 : if (e) {
409 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("SAT>IP: couldn't add the RTP stream.\n"));
410 : return;
411 : }
412 0 : com->user_data = stream;
413 : }
414 :
415 5 : if (opid || esd_url) {
416 0 : com->Accept = gf_strdup("application/sdp");
417 0 : com->ControlString = esd_url ? gf_strdup(esd_url) : NULL;
418 :
419 0 : ch_desc = (RTPIn_StreamDescribe *)gf_malloc(sizeof(RTPIn_StreamDescribe));
420 0 : ch_desc->esd_url = esd_url ? gf_strdup(esd_url) : NULL;
421 0 : ch_desc->opid = opid;
422 :
423 0 : com->user_data = ch_desc;
424 : } else {
425 : //always accept both SDP and IOD
426 5 : com->Accept = gf_strdup("application/sdp, application/mpeg4-iod");
427 : // com->Accept = gf_strdup("application/sdp");
428 : }
429 :
430 : /*need better tuning ...*/
431 5 : if (sess->rtpin->bandwidth)
432 0 : com->Bandwidth = sess->rtpin->bandwidth;
433 :
434 : rtpin_rtsp_queue_command(sess, NULL, com, GF_FALSE);
435 : }
436 :
437 :
438 12 : static void rtpin_rtsp_skip_command(GF_RTPInStream *stream)
439 : {
440 : u32 i;
441 : GF_RTPInStream *a_st;
442 24 : if (!stream || (stream->flags & RTP_SKIP_NEXT_COM) || !(stream->rtsp->flags & RTSP_AGG_CONTROL) ) return;
443 0 : i=0;
444 0 : while ((a_st = (GF_RTPInStream *)gf_list_enum(stream->rtpin->streams, &i))) {
445 0 : if ((stream == a_st) || (a_st->rtsp != stream->rtsp) ) continue;
446 0 : if (a_st->status>=RTP_Connected)
447 0 : a_st->flags |= RTP_SKIP_NEXT_COM;
448 : }
449 : }
450 :
451 :
452 : /*
453 : channel control functions
454 : */
455 : /*remove command if session is using aggregated control*/
456 17 : Bool rtpin_rtsp_usercom_preprocess(GF_RTPInRTSP *sess, GF_RTSPCommand *com)
457 : {
458 : RTPIn_StreamControl *ch_ctrl;
459 : GF_RTPInStream *stream;
460 : GF_Err e;
461 : Bool skip_it;
462 :
463 : ch_ctrl = NULL;
464 17 : if (strcmp(com->method, GF_RTSP_TEARDOWN)) ch_ctrl = (RTPIn_StreamControl *)com->user_data;
465 12 : if (!ch_ctrl || !ch_ctrl->stream) return GF_TRUE;
466 : stream = ch_ctrl->stream;
467 :
468 12 : if (!sess->satip) {
469 24 : if (!stream->opid || !rtpin_stream_is_valid(sess->rtpin, stream)) {
470 0 : gf_free(ch_ctrl);
471 0 : com->user_data = NULL;
472 0 : return GF_FALSE;
473 : }
474 :
475 : assert(stream->rtsp == sess);
476 : assert(stream->opid == ch_ctrl->evt.base.on_pid);
477 : }
478 :
479 : skip_it = GF_FALSE;
480 12 : if (!com->Session) {
481 : /*re-SETUP failed*/
482 0 : if (!strcmp(com->method, GF_RTSP_PLAY) || !strcmp(com->method, GF_RTSP_PAUSE)) {
483 : e = GF_SERVICE_ERROR;
484 : goto err_exit;
485 : }
486 : /*this is a stop, no need for SessionID just skip*/
487 : skip_it = GF_TRUE;
488 : } else {
489 12 : rtpin_rtsp_skip_command(stream);
490 : }
491 :
492 : /*check if aggregation discards this command*/
493 12 : if (skip_it || ( (sess->flags & RTSP_AGG_CONTROL) && (stream->flags & RTP_SKIP_NEXT_COM) )) {
494 0 : stream->flags &= ~RTP_SKIP_NEXT_COM;
495 0 : gf_free(ch_ctrl);
496 0 : com->user_data = NULL;
497 0 : return GF_FALSE;
498 : }
499 : return GF_TRUE;
500 :
501 : err_exit:
502 0 : gf_rtsp_reset_aggregation(stream->rtsp->session);
503 0 : stream->status = RTP_Disconnected;
504 0 : stream->check_rtp_time = RTP_SET_TIME_NONE;
505 :
506 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Error processing event %s: %s\n", gf_filter_event_name(ch_ctrl->evt.base.type), gf_error_to_string(e) ));
507 :
508 0 : gf_free(ch_ctrl);
509 0 : com->user_data = NULL;
510 0 : return GF_FALSE;
511 : }
512 :
513 12 : void rtpin_rtsp_usercom_process(GF_RTPInRTSP *sess, GF_RTSPCommand *com, GF_Err e)
514 : {
515 : RTPIn_StreamControl *ch_ctrl;
516 : GF_RTPInStream *stream, *agg_st;
517 : u32 i, count;
518 : GF_RTPInfo *info;
519 :
520 12 : ch_ctrl = (RTPIn_StreamControl *)com->user_data;
521 12 : stream = ch_ctrl->stream;
522 :
523 12 : if (stream) {
524 24 : if (!stream->opid || !rtpin_stream_is_valid(sess->rtpin, stream)) {
525 0 : gf_free(ch_ctrl);
526 0 : com->user_data = NULL;
527 0 : return;
528 : }
529 :
530 : assert(stream->opid==ch_ctrl->evt.base.on_pid);
531 : }
532 :
533 : /*some consistency checking: on interleaved sessions, some servers do NOT reply to the
534 : teardown. If our command is STOP just skip the error notif*/
535 12 : if (e) {
536 0 : if (!strcmp(com->method, GF_RTSP_TEARDOWN)) {
537 : goto process_reply;
538 : } else {
539 : /*spec is not really clear about what happens if the server doesn't support
540 : non aggregated operations. Since this happen only on pause/play, we consider
541 : that no error occured and wait for next play*/
542 0 : if (sess->rtsp_rsp->ResponseCode == NC_RTSP_Only_Aggregate_Operation_Allowed) {
543 0 : sess->flags |= RTSP_AGG_ONLY;
544 0 : sess->rtsp_rsp->ResponseCode = NC_RTSP_OK;
545 : } else {
546 : goto err_exit;
547 : }
548 : }
549 : }
550 :
551 12 : switch (sess->rtsp_rsp->ResponseCode) {
552 : //handle all 3xx codes (redirections)
553 : case NC_RTSP_Method_Not_Allowed:
554 : e = GF_NOT_SUPPORTED;
555 : goto err_exit;
556 : case NC_RTSP_OK:
557 : break;
558 0 : default:
559 : //we should have a basic error code mapping here
560 : e = GF_SERVICE_ERROR;
561 0 : goto err_exit;
562 : }
563 :
564 0 : process_reply:
565 :
566 24 : if ( (ch_ctrl->evt.base.type==GF_FEVT_PLAY)
567 : || (ch_ctrl->evt.base.type==GF_FEVT_SET_SPEED)
568 12 : || (ch_ctrl->evt.base.type==GF_FEVT_RESUME) ) {
569 :
570 : //auto-detect any aggregated control if not done yet
571 6 : if (gf_list_count(sess->rtsp_rsp->RTP_Infos) > 1) {
572 0 : sess->flags |= RTSP_AGG_CONTROL;
573 : }
574 :
575 : //process all RTP infos
576 6 : count = gf_list_count(sess->rtsp_rsp->RTP_Infos);
577 6 : for (i=0; i<count; i++) {
578 0 : info = (GF_RTPInfo*)gf_list_get(sess->rtsp_rsp->RTP_Infos, i);
579 0 : agg_st = rtpin_find_stream(sess->rtpin, NULL, 0, info->url, GF_FALSE);
580 :
581 0 : if (!agg_st || (agg_st->rtsp != sess) ) continue;
582 : /*channel is already playing*/
583 0 : if (agg_st->status == RTP_Running) {
584 0 : gf_rtp_set_info_rtp(agg_st->rtp_ch, info->seq, info->rtp_time, info->ssrc);
585 0 : agg_st->check_rtp_time = RTP_SET_TIME_RTP;
586 0 : continue;
587 : }
588 :
589 : /*if play/seeking we must send update RTP/NPT link*/
590 0 : if (ch_ctrl->evt.base.type != GF_FEVT_RESUME) {
591 0 : agg_st->check_rtp_time = RTP_SET_TIME_RTP;
592 : }
593 : /*this is used to discard RTP packets re-sent on resume*/
594 : else {
595 0 : agg_st->check_rtp_time = RTP_SET_TIME_RTP_SEEK;
596 : }
597 : /* reset the buffers */
598 0 : rtpin_stream_init(agg_st, GF_TRUE);
599 :
600 0 : gf_rtp_set_info_rtp(agg_st->rtp_ch, info->seq, info->rtp_time, info->ssrc);
601 0 : agg_st->status = RTP_Running;
602 :
603 : /*skip next play command on this channel if aggregated control*/
604 0 : if ((stream != agg_st) && stream && (stream->rtsp->flags & RTSP_AGG_CONTROL) ) agg_st->flags |= RTP_SKIP_NEXT_COM;
605 :
606 :
607 0 : if (gf_rtp_is_interleaved(agg_st->rtp_ch)) {
608 0 : gf_rtsp_register_interleave(sess->session,
609 : agg_st,
610 0 : gf_rtp_get_low_interleave_id(agg_st->rtp_ch),
611 0 : gf_rtp_get_hight_interleave_id(agg_st->rtp_ch));
612 : }
613 : }
614 : /*no rtp info (just in case), no time mapped - set to 0 and specify we're not interactive*/
615 6 : if (stream && !i) {
616 6 : stream->current_start = 0.0;
617 6 : stream->check_rtp_time = RTP_SET_TIME_RTP;
618 6 : rtpin_stream_init(stream, GF_TRUE);
619 6 : stream->status = RTP_Running;
620 6 : if (gf_rtp_is_interleaved(stream->rtp_ch)) {
621 2 : gf_rtsp_register_interleave(sess->session,
622 2 : stream, gf_rtp_get_low_interleave_id(stream->rtp_ch), gf_rtp_get_hight_interleave_id(stream->rtp_ch));
623 : }
624 : }
625 6 : if (stream) stream->flags &= ~RTP_SKIP_NEXT_COM;
626 6 : } else if (ch_ctrl->evt.base.type == GF_FEVT_PAUSE) {
627 0 : if (stream) {
628 0 : rtpin_rtsp_skip_command(stream);
629 0 : stream->flags &= ~RTP_SKIP_NEXT_COM;
630 :
631 : }
632 6 : } else if (ch_ctrl->evt.base.type == GF_FEVT_STOP) {
633 6 : sess->rtpin->eos_probe_start = gf_sys_clock();
634 6 : if (stream)
635 6 : stream->flags |= RTP_EOS;
636 : }
637 12 : gf_free(ch_ctrl);
638 12 : com->user_data = NULL;
639 12 : return;
640 :
641 :
642 0 : err_exit:
643 0 : if (stream) {
644 0 : stream->status = RTP_Disconnected;
645 0 : stream->flags |= RTP_EOS;
646 0 : gf_rtsp_reset_aggregation(stream->rtsp->session);
647 0 : stream->check_rtp_time = RTP_SET_TIME_NONE;
648 : }
649 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Error processing user command %s\n", gf_error_to_string(e) ));
650 0 : gf_free(ch_ctrl);
651 0 : com->user_data = NULL;
652 0 : if (sess->flags & RTSP_TCP_FLUSH) {
653 0 : sess->rtpin->eos_probe_start = gf_sys_clock();
654 : }
655 : }
656 :
657 :
658 12 : void rtpin_rtsp_usercom_send(GF_RTPInRTSP *sess, GF_RTPInStream *stream, const GF_FilterEvent *evt)
659 : {
660 : RTPIn_StreamControl *ch_ctrl;
661 : u32 i;
662 : Bool needs_setup = GF_FALSE;
663 : GF_RTSPCommand *com;
664 : GF_RTSPRange *range;
665 :
666 12 : switch (evt->base.type) {
667 : case GF_FEVT_PLAY:
668 : case GF_FEVT_RESUME:
669 : needs_setup = GF_TRUE;
670 : break;
671 : case GF_FEVT_PAUSE:
672 : case GF_FEVT_STOP:
673 : break;
674 : //TODO
675 : case GF_FEVT_SET_SPEED:
676 : default:
677 : return;
678 : }
679 :
680 :
681 : /*we may need to re-setup stream/session*/
682 : if (needs_setup) {
683 6 : if (stream->status == RTP_Disconnected) {
684 0 : if (sess->flags & RTSP_AGG_CONTROL) {
685 : GF_RTPInStream *a_st;
686 0 : i=0;
687 0 : while ((a_st = (GF_RTPInStream *)gf_list_enum(sess->rtpin->streams, &i))) {
688 0 : if (a_st->rtsp != sess) continue;
689 0 : if (a_st->status == RTP_Disconnected)
690 0 : rtpin_rtsp_setup_send(a_st);
691 : }
692 : } else {
693 0 : rtpin_rtsp_setup_send(stream);
694 : }
695 : }
696 : }
697 :
698 12 : com = gf_rtsp_command_new();
699 : range = NULL;
700 :
701 12 : if ( (evt->base.type == GF_FEVT_PLAY) || (evt->base.type == GF_FEVT_RESUME) ) {
702 :
703 6 : range = gf_rtsp_range_new();
704 6 : range->start = stream->range_start;
705 6 : range->end = stream->range_end;
706 :
707 6 : com->method = gf_strdup(GF_RTSP_PLAY);
708 :
709 6 : stream->paused = GF_FALSE;
710 :
711 : /*specify pause range on resume - this is not mandatory but most servers need it*/
712 6 : if (evt->base.type == GF_FEVT_RESUME) {
713 0 : range->start = stream->current_start;
714 :
715 0 : stream->stat_start_time -= stream->stat_stop_time;
716 0 : stream->stat_start_time += gf_sys_clock();
717 0 : stream->stat_stop_time = 0;
718 : } else {
719 6 : range->start = stream->range_start;
720 6 : if (evt->play.start_range>=0) range->start += evt->play.start_range;
721 6 : range->end = stream->range_start;
722 6 : if (evt->play.end_range >=0) {
723 6 : range->end += evt->play.end_range;
724 6 : if (range->end > stream->range_end) range->end = stream->range_end;
725 : }
726 :
727 6 : stream->stat_start_time = gf_sys_clock();
728 6 : stream->stat_stop_time = 0;
729 : }
730 : /*if aggregated the command is sent once, so store info at session level*/
731 6 : if (stream->flags & RTP_SKIP_NEXT_COM) {
732 0 : stream->current_start = stream->rtsp->last_range;
733 : } else {
734 6 : stream->rtsp->last_range = range->start;
735 6 : stream->current_start = range->start;
736 : }
737 : /*some RTSP servers don't accept Range=npt:0.0- (for ex, broadcast only...), so skip it if:
738 : - a range was given in initial describe
739 : - the command is not a RESUME
740 : */
741 6 : if (!(stream->flags & RTP_HAS_RANGE) && (evt->base.type != GF_FEVT_RESUME) ) {
742 0 : gf_rtsp_range_del(range);
743 0 : com->Range = NULL;
744 : } else {
745 6 : com->Range = range;
746 : }
747 :
748 6 : if (sess->flags & RTSP_AGG_CONTROL)
749 0 : rtpin_rtsp_skip_command(stream);
750 6 : else if (strlen(stream->control))
751 6 : com->ControlString = gf_strdup(stream->control);
752 :
753 6 : if (rtpin_rtsp_is_active(stream)) {
754 0 : if (!com->ControlString && stream->control) com->ControlString = gf_strdup(stream->control);
755 : } else {
756 6 : if (com->ControlString) {
757 6 : gf_free(com->ControlString);
758 6 : com->ControlString=NULL;
759 : }
760 : }
761 :
762 6 : } else if (evt->base.type == GF_FEVT_PAUSE) {
763 0 : com->method = gf_strdup(GF_RTSP_PAUSE);
764 0 : if (stream) {
765 0 : range = gf_rtsp_range_new();
766 : /*update current time*/
767 0 : stream->current_start += gf_rtp_get_current_time(stream->rtp_ch);
768 0 : stream->stat_stop_time = gf_sys_clock();
769 0 : range->start = stream->current_start;
770 0 : range->end = -1.0;
771 0 : com->Range = range;
772 :
773 0 : if (sess->flags & RTSP_AGG_CONTROL)
774 0 : rtpin_rtsp_skip_command(stream);
775 0 : else if (strlen(stream->control))
776 0 : com->ControlString = gf_strdup(stream->control);
777 :
778 0 : stream->paused = GF_TRUE;
779 : }
780 : }
781 6 : else if (evt->base.type == GF_FEVT_STOP) {
782 6 : stream->current_start = 0;
783 6 : stream->stat_stop_time = gf_sys_clock();
784 :
785 6 : stream->status = RTP_Connected;
786 6 : rtpin_stream_init(stream, GF_TRUE);
787 :
788 : /*if server only support aggregation on pause, skip the command or issue
789 : a teardown if last active stream*/
790 6 : if (stream->rtsp->flags & RTSP_AGG_ONLY) {
791 0 : stream->flags &= ~RTP_SKIP_NEXT_COM;
792 : //remove interleaved
793 0 : if (gf_rtp_is_interleaved(stream->rtp_ch)) {
794 0 : gf_rtsp_unregister_interleave(stream->rtsp->session, gf_rtp_get_low_interleave_id(stream->rtp_ch));
795 : }
796 :
797 0 : if (com) gf_rtsp_command_del(com);
798 0 : if (!rtpin_rtsp_is_active(stream))
799 0 : rtpin_rtsp_teardown(sess, stream);
800 : return;
801 : }
802 : /* otherwise send a PAUSE on the stream */
803 : else {
804 6 : if (stream->paused) {
805 0 : if (com) gf_rtsp_command_del(com);
806 : return;
807 : }
808 6 : range = gf_rtsp_range_new();
809 6 : range->start = 0;
810 6 : range->end = -1;
811 6 : com->method = gf_strdup(GF_RTSP_PAUSE);
812 6 : com->Range = range;
813 : /*only pause the specified stream*/
814 6 : if (stream->control) com->ControlString = gf_strdup(stream->control);
815 : }
816 : } else {
817 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Unsupported command %s\n", gf_filter_event_name(evt->base.type) ));
818 0 : gf_rtsp_command_del(com);
819 0 : return;
820 : }
821 :
822 12 : ch_ctrl = (RTPIn_StreamControl *)gf_malloc(sizeof(RTPIn_StreamControl));
823 12 : ch_ctrl->stream = stream;
824 12 : memcpy(&ch_ctrl->evt, evt, sizeof(GF_FilterEvent));
825 12 : com->user_data = ch_ctrl;
826 :
827 12 : rtpin_rtsp_queue_command(sess, stream, com, GF_TRUE);
828 : return;
829 : }
830 :
831 :
832 : /*
833 : session/channel teardown functions
834 : */
835 5 : void rtpin_rtsp_teardown_process(GF_RTPInRTSP *sess, GF_RTSPCommand *com, GF_Err e)
836 : {
837 5 : GF_RTPInStream *stream = (GF_RTPInStream *)com->user_data;
838 5 : if (stream) {
839 0 : if (stream->session_id) gf_free(stream->session_id);
840 0 : stream->session_id = NULL;
841 : } else {
842 5 : if (sess->session_id) gf_free(sess->session_id);
843 5 : sess->session_id = NULL;
844 : }
845 5 : }
846 :
847 5 : void rtpin_rtsp_teardown(GF_RTPInRTSP *sess, GF_RTPInStream *stream)
848 : {
849 : GF_RTSPCommand *com;
850 :
851 : /*we need a session id*/
852 5 : if (!sess->session_id) return;
853 : /*ignore teardown on channels*/
854 5 : if ((sess->flags & RTSP_AGG_CONTROL) && stream) return;
855 :
856 5 : com = gf_rtsp_command_new();
857 5 : com->method = gf_strdup(GF_RTSP_TEARDOWN);
858 : /*this only works in RTSP2*/
859 5 : if (stream && stream->control) {
860 0 : com->ControlString = gf_strdup(stream->control);
861 0 : com->user_data = stream;
862 : }
863 :
864 5 : rtpin_rtsp_queue_command(sess, stream, com, GF_TRUE);
865 : }
866 :
867 : #endif /*GPAC_DISABLE_STREAMING*/
|