• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2008 Colin Guthrie
5 
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/ioctl.h>
29 #include <netinet/in.h>
30 #include <pulse/rtclock.h>
31 #include <pulse/timeval.h>
32 
33 #ifdef HAVE_SYS_FILIO_H
34 #include <sys/filio.h>
35 #endif
36 
37 #include <pulse/xmalloc.h>
38 
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/core-util.h>
41 #include <pulsecore/log.h>
42 #include <pulsecore/macro.h>
43 #include <pulsecore/strbuf.h>
44 #include <pulsecore/ioline.h>
45 #include <pulsecore/arpa-inet.h>
46 #include <pulsecore/random.h>
47 #include <pulsecore/core-rtclock.h>
48 
49 #include "rtsp_client.h"
50 
51 #define RECONNECT_INTERVAL (5 * PA_USEC_PER_SEC)
52 
53 struct pa_rtsp_client {
54     pa_mainloop_api *mainloop;
55     char *hostname;
56     uint16_t port;
57 
58     pa_socket_client *sc;
59     pa_ioline *ioline;
60 
61     pa_rtsp_cb_t callback;
62 
63     void *userdata;
64     const char *useragent;
65 
66     pa_rtsp_state_t state;
67     pa_rtsp_status_t status;
68     uint8_t waiting;
69 
70     pa_headerlist* headers;
71     char *last_header;
72     pa_strbuf *header_buffer;
73     pa_headerlist* response_headers;
74 
75     char *localip;
76     char *url;
77     uint16_t rtp_port;
78     uint32_t cseq;
79     char *session;
80     char *transport;
81     pa_time_event *reconnect_event;
82     bool autoreconnect;
83 };
84 
pa_rtsp_client_new(pa_mainloop_api * mainloop,const char * hostname,uint16_t port,const char * useragent,bool autoreconnect)85 pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent, bool autoreconnect) {
86     pa_rtsp_client *c;
87 
88     pa_assert(mainloop);
89     pa_assert(hostname);
90     pa_assert(port > 0);
91 
92     c = pa_xnew0(pa_rtsp_client, 1);
93     c->mainloop = mainloop;
94     c->hostname = pa_xstrdup(hostname);
95     c->port = port;
96     c->headers = pa_headerlist_new();
97 
98     if (useragent)
99         c->useragent = useragent;
100     else
101         c->useragent = "PulseAudio RTSP Client";
102 
103     c->autoreconnect = autoreconnect;
104     return c;
105 }
106 
free_events(pa_rtsp_client * c)107 static void free_events(pa_rtsp_client *c) {
108     pa_assert(c);
109 
110     if (c->reconnect_event) {
111         c->mainloop->time_free(c->reconnect_event);
112         c->reconnect_event = NULL;
113     }
114 }
115 
pa_rtsp_client_free(pa_rtsp_client * c)116 void pa_rtsp_client_free(pa_rtsp_client *c) {
117     pa_assert(c);
118 
119     free_events(c);
120     if (c->sc)
121         pa_socket_client_unref(c->sc);
122 
123     pa_rtsp_disconnect(c);
124 
125     pa_xfree(c->hostname);
126     pa_xfree(c->url);
127     pa_xfree(c->localip);
128     pa_xfree(c->session);
129     pa_xfree(c->transport);
130     pa_xfree(c->last_header);
131     if (c->header_buffer)
132         pa_strbuf_free(c->header_buffer);
133     if (c->response_headers)
134         pa_headerlist_free(c->response_headers);
135     pa_headerlist_free(c->headers);
136 
137     pa_xfree(c);
138 }
139 
headers_read(pa_rtsp_client * c)140 static void headers_read(pa_rtsp_client *c) {
141     char delimiters[] = ";";
142     char* token;
143 
144     pa_assert(c);
145     pa_assert(c->response_headers);
146     pa_assert(c->callback);
147 
148     /* Deal with a SETUP response */
149     if (STATE_SETUP == c->state) {
150         const char* token_state = NULL;
151         const char* pc = NULL;
152         c->session = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Session"));
153         c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport"));
154 
155         if (!c->session || !c->transport) {
156             pa_log("Invalid SETUP response.");
157             return;
158         }
159 
160         /* Now parse out the server port component of the response. */
161         while ((token = pa_split(c->transport, delimiters, &token_state))) {
162             if ((pc = strchr(token, '='))) {
163                 if (0 == strncmp(token, "server_port", 11)) {
164                     uint32_t p;
165 
166                     if (pa_atou(pc + 1, &p) < 0 || p <= 0 || p > 0xffff) {
167                         pa_log("Invalid SETUP response (invalid server_port).");
168                         pa_xfree(token);
169                         return;
170                     }
171 
172                     c->rtp_port = p;
173                     pa_xfree(token);
174                     break;
175                 }
176             }
177             pa_xfree(token);
178         }
179         if (0 == c->rtp_port) {
180             /* Error no server_port in response */
181             pa_log("Invalid SETUP response (no port number).");
182             return;
183         }
184     }
185 
186     /* Call our callback */
187     c->callback(c, c->state, c->status, c->response_headers, c->userdata);
188 }
189 
line_callback(pa_ioline * line,const char * s,void * userdata)190 static void line_callback(pa_ioline *line, const char *s, void *userdata) {
191     pa_rtsp_client *c = userdata;
192     char *delimpos;
193     char *s2, *s2p;
194 
195     pa_assert(line);
196     pa_assert(c);
197     pa_assert(c->callback);
198 
199     if (!s) {
200         /* Keep the ioline/iochannel open as they will be freed automatically */
201         c->ioline = NULL;
202         c->callback(c, STATE_DISCONNECTED, STATUS_NO_RESPONSE, NULL, c->userdata);
203         return;
204     }
205 
206     s2 = pa_xstrdup(s);
207     /* Trim trailing carriage returns */
208     s2p = s2 + strlen(s2) - 1;
209     while (s2p >= s2 && '\r' == *s2p) {
210         *s2p = '\0';
211         s2p -= 1;
212     }
213 
214     if (c->waiting && pa_streq(s2, "RTSP/1.0 200 OK")) {
215         if (c->response_headers)
216             pa_headerlist_free(c->response_headers);
217         c->response_headers = pa_headerlist_new();
218 
219         c->status = STATUS_OK;
220         c->waiting = 0;
221         goto exit;
222     } else if (c->waiting && pa_streq(s2, "RTSP/1.0 401 Unauthorized")) {
223         if (c->response_headers)
224             pa_headerlist_free(c->response_headers);
225         c->response_headers = pa_headerlist_new();
226 
227         c->status = STATUS_UNAUTHORIZED;
228         c->waiting = 0;
229         goto exit;
230     } else if (c->waiting) {
231         pa_log_warn("Unexpected/Unhandled response: %s", s2);
232 
233         if (pa_streq(s2, "RTSP/1.0 400 Bad Request"))
234             c->status = STATUS_BAD_REQUEST;
235         else if (pa_streq(s2, "RTSP/1.0 500 Internal Server Error"))
236             c->status = STATUS_INTERNAL_ERROR;
237         else
238             c->status = STATUS_NO_RESPONSE;
239         goto exit;
240     }
241 
242     if (!strlen(s2)) {
243         /* End of headers */
244         /* We will have a header left from our looping iteration, so add it in :) */
245         if (c->last_header) {
246             char *tmp = pa_strbuf_to_string_free(c->header_buffer);
247             /* This is not a continuation header so let's dump it into our proplist */
248             pa_headerlist_puts(c->response_headers, c->last_header, tmp);
249             pa_xfree(tmp);
250             pa_xfree(c->last_header);
251             c->last_header = NULL;
252             c->header_buffer = NULL;
253         }
254 
255         pa_log_debug("Full response received. Dispatching");
256         headers_read(c);
257         goto exit;
258     }
259 
260     /* Read and parse a header (we know it's not empty) */
261     /* TODO: Move header reading into the headerlist. */
262 
263     /* If the first character is a space, it's a continuation header */
264     if (c->last_header && ' ' == s2[0]) {
265         pa_assert(c->header_buffer);
266 
267         /* Add this line to the buffer (sans the space) */
268         pa_strbuf_puts(c->header_buffer, &(s2[1]));
269         goto exit;
270     }
271 
272     if (c->last_header) {
273         char *tmp = pa_strbuf_to_string_free(c->header_buffer);
274         /* This is not a continuation header so let's dump the full
275           header/value into our proplist */
276         pa_headerlist_puts(c->response_headers, c->last_header, tmp);
277         pa_xfree(tmp);
278         pa_xfree(c->last_header);
279         c->last_header = NULL;
280         c->header_buffer = NULL;
281     }
282 
283     delimpos = strstr(s2, ":");
284     if (!delimpos) {
285         pa_log_warn("Unexpected response when expecting header: %s", s);
286         goto exit;
287     }
288 
289     pa_assert(!c->header_buffer);
290     pa_assert(!c->last_header);
291 
292     c->header_buffer = pa_strbuf_new();
293     if (strlen(delimpos) > 1) {
294         /* Cut our line off so we can copy the header name out */
295         *delimpos++ = '\0';
296 
297         /* Trim the front of any spaces */
298         while (' ' == *delimpos)
299             ++delimpos;
300 
301         pa_strbuf_puts(c->header_buffer, delimpos);
302     } else {
303         /* Cut our line off so we can copy the header name out */
304         *delimpos = '\0';
305     }
306 
307     /* Save the header name */
308     c->last_header = pa_xstrdup(s2);
309 
310   exit:
311     pa_xfree(s2);
312 }
313 
reconnect_cb(pa_mainloop_api * a,pa_time_event * e,const struct timeval * t,void * userdata)314 static void reconnect_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
315     if (userdata) {
316         pa_rtsp_client *c = userdata;
317         pa_rtsp_connect(c);
318     }
319 }
320 
on_connection(pa_socket_client * sc,pa_iochannel * io,void * userdata)321 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
322     pa_rtsp_client *c = userdata;
323     union {
324         struct sockaddr sa;
325         struct sockaddr_in in;
326         struct sockaddr_in6 in6;
327     } sa;
328     socklen_t sa_len = sizeof(sa);
329 
330     pa_assert(sc);
331     pa_assert(c);
332     pa_assert(STATE_CONNECT == c->state);
333     pa_assert(c->sc == sc);
334     pa_socket_client_unref(c->sc);
335     c->sc = NULL;
336 
337     if (!io) {
338         if (c->autoreconnect) {
339             struct timeval tv;
340 
341             pa_log_warn("Connection to server %s:%d failed: %s - will try later", c->hostname, c->port, pa_cstrerror(errno));
342 
343             if (!c->reconnect_event)
344                 c->reconnect_event = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + RECONNECT_INTERVAL, true), reconnect_cb, c);
345             else
346                 c->mainloop->time_restart(c->reconnect_event, pa_timeval_rtstore(&tv, pa_rtclock_now() + RECONNECT_INTERVAL, true));
347         } else {
348             pa_log("Connection to server %s:%d failed: %s", c->hostname, c->port, pa_cstrerror(errno));
349         }
350         return;
351     }
352     pa_assert(!c->ioline);
353 
354     c->ioline = pa_ioline_new(io);
355     pa_ioline_set_callback(c->ioline, line_callback, c);
356 
357     /* Get the local IP address for use externally */
358     if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
359         char buf[INET6_ADDRSTRLEN];
360         const char *res = NULL;
361 
362         if (AF_INET == sa.sa.sa_family) {
363             if ((res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf)))) {
364                 c->localip = pa_xstrdup(res);
365             }
366         } else if (AF_INET6 == sa.sa.sa_family) {
367             if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) {
368                 c->localip = pa_xstrdup(res);
369             }
370         }
371     }
372     pa_log_debug("Established RTSP connection from local ip %s", c->localip);
373 
374     if (c->callback)
375         c->callback(c, c->state, STATUS_OK, NULL, c->userdata);
376 }
377 
pa_rtsp_connect(pa_rtsp_client * c)378 int pa_rtsp_connect(pa_rtsp_client *c) {
379     pa_assert(c);
380     pa_assert(!c->sc);
381 
382     pa_xfree(c->session);
383     c->session = NULL;
384 
385     pa_log_debug("Attempting to connect to server '%s:%d'", c->hostname, c->port);
386     if (!(c->sc = pa_socket_client_new_string(c->mainloop, true, c->hostname, c->port))) {
387         pa_log("failed to connect to server '%s:%d'", c->hostname, c->port);
388         return -1;
389     }
390 
391     pa_socket_client_set_callback(c->sc, on_connection, c);
392     c->waiting = 1;
393     c->state = STATE_CONNECT;
394     c->status = STATUS_NO_RESPONSE;
395     return 0;
396 }
397 
pa_rtsp_set_callback(pa_rtsp_client * c,pa_rtsp_cb_t callback,void * userdata)398 void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata) {
399     pa_assert(c);
400 
401     c->callback = callback;
402     c->userdata = userdata;
403 }
404 
pa_rtsp_disconnect(pa_rtsp_client * c)405 void pa_rtsp_disconnect(pa_rtsp_client *c) {
406     pa_assert(c);
407 
408     if (c->ioline) {
409         pa_ioline_close(c->ioline);
410         pa_ioline_unref(c->ioline);
411     }
412     c->ioline = NULL;
413 }
414 
pa_rtsp_localip(pa_rtsp_client * c)415 const char* pa_rtsp_localip(pa_rtsp_client *c) {
416     pa_assert(c);
417 
418     return c->localip;
419 }
420 
pa_rtsp_serverport(pa_rtsp_client * c)421 uint32_t pa_rtsp_serverport(pa_rtsp_client *c) {
422     pa_assert(c);
423 
424     return c->rtp_port;
425 }
426 
pa_rtsp_exec_ready(const pa_rtsp_client * c)427 bool pa_rtsp_exec_ready(const pa_rtsp_client *c) {
428     pa_assert(c);
429 
430     return c->url != NULL && c->ioline != NULL;
431 }
432 
pa_rtsp_set_url(pa_rtsp_client * c,const char * url)433 void pa_rtsp_set_url(pa_rtsp_client *c, const char *url) {
434     pa_assert(c);
435 
436     c->url = pa_xstrdup(url);
437 }
438 
pa_rtsp_has_header(pa_rtsp_client * c,const char * key)439 bool pa_rtsp_has_header(pa_rtsp_client *c, const char *key) {
440     pa_assert(c);
441     pa_assert(key);
442 
443     return pa_headerlist_contains(c->headers, key);
444 }
445 
pa_rtsp_add_header(pa_rtsp_client * c,const char * key,const char * value)446 void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value) {
447     pa_assert(c);
448     pa_assert(key);
449     pa_assert(value);
450 
451     pa_headerlist_puts(c->headers, key, value);
452 }
453 
pa_rtsp_get_header(pa_rtsp_client * c,const char * key)454 const char* pa_rtsp_get_header(pa_rtsp_client *c, const char *key) {
455     pa_assert(c);
456     pa_assert(key);
457 
458     return pa_headerlist_gets(c->headers, key);
459 }
460 
pa_rtsp_remove_header(pa_rtsp_client * c,const char * key)461 void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
462     pa_assert(c);
463     pa_assert(key);
464 
465     pa_headerlist_remove(c->headers, key);
466 }
467 
rtsp_exec(pa_rtsp_client * c,const char * cmd,const char * content_type,const char * content,int expect_response,pa_headerlist * headers)468 static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
469                         const char *content_type, const char *content,
470                         int expect_response,
471                         pa_headerlist *headers) {
472     pa_strbuf *buf;
473     char *hdrs;
474 
475     pa_assert(c);
476     pa_assert(c->url);
477     pa_assert(cmd);
478     pa_assert(c->ioline);
479 
480     pa_log_debug("Sending command: %s", cmd);
481 
482     buf = pa_strbuf_new();
483     pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
484     if (c->session)
485         pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
486 
487     /* Add the headers */
488     if (headers) {
489         hdrs = pa_headerlist_to_string(headers);
490         pa_strbuf_puts(buf, hdrs);
491         pa_xfree(hdrs);
492     }
493 
494     if (content_type && content) {
495         pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
496           content_type, (int)strlen(content));
497     }
498 
499     pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
500 
501     if (c->headers) {
502         hdrs = pa_headerlist_to_string(c->headers);
503         pa_strbuf_puts(buf, hdrs);
504         pa_xfree(hdrs);
505     }
506 
507     pa_strbuf_puts(buf, "\r\n");
508 
509     if (content_type && content) {
510         pa_strbuf_puts(buf, content);
511     }
512 
513     /* Our packet is created... now we can send it :) */
514     hdrs = pa_strbuf_to_string_free(buf);
515     /*pa_log_debug("Submitting request:");
516     pa_log_debug(hdrs);*/
517     pa_ioline_puts(c->ioline, hdrs);
518     pa_xfree(hdrs);
519     /* The command is sent we can configure the rtsp client structure to handle a new answer */
520     c->waiting = 1;
521     return 0;
522 }
523 
pa_rtsp_options(pa_rtsp_client * c)524 int pa_rtsp_options(pa_rtsp_client *c) {
525     char *url;
526     int rv;
527 
528     pa_assert(c);
529 
530     url = c->url;
531     c->state = STATE_OPTIONS;
532 
533     c->url = (char *)"*";
534     rv = rtsp_exec(c, "OPTIONS", NULL, NULL, 0, NULL);
535 
536     c->url = url;
537     return rv;
538 }
539 
pa_rtsp_announce(pa_rtsp_client * c,const char * sdp)540 int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp) {
541     int rv;
542 
543     pa_assert(c);
544 
545     if (!sdp)
546         return -1;
547 
548     c->state = STATE_ANNOUNCE;
549     rv = rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
550 
551     return rv;
552 }
553 
pa_rtsp_setup(pa_rtsp_client * c,const char * transport)554 int pa_rtsp_setup(pa_rtsp_client *c, const char *transport) {
555     pa_headerlist *headers;
556     int rv;
557 
558     pa_assert(c);
559 
560     headers = pa_headerlist_new();
561     if (!transport)
562         pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
563     else
564         pa_headerlist_puts(headers, "Transport", transport);
565 
566     c->state = STATE_SETUP;
567     rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
568 
569     pa_headerlist_free(headers);
570     return rv;
571 }
572 
pa_rtsp_record(pa_rtsp_client * c,uint16_t * seq,uint32_t * rtptime)573 int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime) {
574     pa_headerlist *headers;
575     char *info;
576     int rv;
577 
578     pa_assert(c);
579 
580     if (!c->session) {
581         /* No session in progress */
582         return -1;
583     }
584 
585     pa_random(seq, sizeof(*seq));
586     pa_random(rtptime, sizeof(*rtptime));
587 
588     headers = pa_headerlist_new();
589     pa_headerlist_puts(headers, "Range", "npt=0-");
590     info = pa_sprintf_malloc("seq=%u;rtptime=%u", *seq, *rtptime);
591     pa_headerlist_puts(headers, "RTP-Info", info);
592     pa_xfree(info);
593 
594     c->state = STATE_RECORD;
595     rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
596 
597     pa_headerlist_free(headers);
598     return rv;
599 }
600 
pa_rtsp_setparameter(pa_rtsp_client * c,const char * param)601 int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param) {
602     int rv;
603 
604     pa_assert(c);
605 
606     if (!param)
607         return -1;
608 
609     c->state = STATE_SET_PARAMETER;
610     rv = rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
611 
612     return rv;
613 }
614 
pa_rtsp_flush(pa_rtsp_client * c,uint16_t seq,uint32_t rtptime)615 int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
616     pa_headerlist* headers;
617     char *info;
618     int rv;
619 
620     pa_assert(c);
621 
622     headers = pa_headerlist_new();
623     info = pa_sprintf_malloc("seq=%u;rtptime=%u", seq, rtptime);
624     pa_headerlist_puts(headers, "RTP-Info", info);
625     pa_xfree(info);
626 
627     c->state = STATE_FLUSH;
628     rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
629 
630     pa_headerlist_free(headers);
631     return rv;
632 }
633 
pa_rtsp_teardown(pa_rtsp_client * c)634 int pa_rtsp_teardown(pa_rtsp_client *c) {
635     int rv;
636 
637     pa_assert(c);
638 
639     c->state = STATE_TEARDOWN;
640     rv = rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
641 
642     return rv;
643 }
644