Add missing headers' include to build on FreeBSD 7.1.
[profile/ivi/pulseaudio-panda.git] / src / modules / rtp / rtsp_client.c
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, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <arpa/inet.h>
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33 #include <netinet/in.h>
34
35 #ifdef HAVE_SYS_FILIO_H
36 #include <sys/filio.h>
37 #endif
38
39 #include <pulse/xmalloc.h>
40
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/socket-util.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/strbuf.h>
47 #include <pulsecore/ioline.h>
48
49 #ifdef HAVE_POLL_H
50 #include <poll.h>
51 #else
52 #include <pulsecore/poll.h>
53 #endif
54
55 #include "rtsp_client.h"
56
57 struct pa_rtsp_client {
58     pa_mainloop_api *mainloop;
59     char *hostname;
60     uint16_t port;
61
62     pa_socket_client *sc;
63     pa_iochannel *io;
64     pa_ioline *ioline;
65
66     pa_rtsp_cb_t callback;
67
68     void *userdata;
69     const char *useragent;
70
71     pa_rtsp_state state;
72     uint8_t waiting;
73
74     pa_headerlist* headers;
75     char *last_header;
76     pa_strbuf *header_buffer;
77     pa_headerlist* response_headers;
78
79     char *localip;
80     char *url;
81     uint16_t rtp_port;
82     uint32_t cseq;
83     char *session;
84     char *transport;
85 };
86
87 pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent) {
88     pa_rtsp_client *c;
89
90     pa_assert(mainloop);
91     pa_assert(hostname);
92     pa_assert(port > 0);
93
94     c = pa_xnew0(pa_rtsp_client, 1);
95     c->mainloop = mainloop;
96     c->hostname = pa_xstrdup(hostname);
97     c->port = port;
98     c->headers = pa_headerlist_new();
99
100     if (useragent)
101         c->useragent = useragent;
102     else
103         c->useragent = "PulseAudio RTSP Client";
104
105     return c;
106 }
107
108
109 void pa_rtsp_client_free(pa_rtsp_client* c) {
110     pa_assert(c);
111
112     if (c->sc)
113         pa_socket_client_unref(c->sc);
114     if (c->ioline)
115         pa_ioline_close(c->ioline);
116     else if (c->io)
117         pa_iochannel_free(c->io);
118
119     pa_xfree(c->hostname);
120     pa_xfree(c->url);
121     pa_xfree(c->localip);
122     pa_xfree(c->session);
123     pa_xfree(c->transport);
124     pa_xfree(c->last_header);
125     if (c->header_buffer)
126         pa_strbuf_free(c->header_buffer);
127     if (c->response_headers)
128         pa_headerlist_free(c->response_headers);
129     pa_headerlist_free(c->headers);
130
131     pa_xfree(c);
132 }
133
134
135 static void headers_read(pa_rtsp_client *c) {
136     char* token;
137     char delimiters[] = ";";
138
139     pa_assert(c);
140     pa_assert(c->response_headers);
141     pa_assert(c->callback);
142
143     /* Deal with a SETUP response */
144     if (STATE_SETUP == c->state) {
145         const char* token_state = NULL;
146         const char* pc = NULL;
147         c->session = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Session"));
148         c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport"));
149
150         if (!c->session || !c->transport) {
151             pa_log("Invalid SETUP response.");
152             return;
153         }
154
155         /* Now parse out the server port component of the response. */
156         while ((token = pa_split(c->transport, delimiters, &token_state))) {
157             if ((pc = strstr(token, "="))) {
158                 if (0 == strncmp(token, "server_port", 11)) {
159                     pa_atou(pc+1, (uint32_t*)(&c->rtp_port));
160                     pa_xfree(token);
161                     break;
162                 }
163             }
164             pa_xfree(token);
165         }
166         if (0 == c->rtp_port) {
167             /* Error no server_port in response */
168             pa_log("Invalid SETUP response (no port number).");
169             return;
170         }
171     }
172
173     /* Call our callback */
174     c->callback(c, c->state, c->response_headers, c->userdata);
175 }
176
177
178 static void line_callback(pa_ioline *line, const char *s, void *userdata) {
179     char *delimpos;
180     char *s2, *s2p;
181
182     pa_rtsp_client *c = userdata;
183     pa_assert(line);
184     pa_assert(c);
185     pa_assert(c->callback);
186
187     if (!s) {
188         /* Keep the ioline/iochannel open as they will be freed automatically */
189         c->ioline = NULL;
190         c->io = NULL;
191         c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
192         return;
193     }
194
195     s2 = pa_xstrdup(s);
196     /* Trim trailing carriage returns */
197     s2p = s2 + strlen(s2) - 1;
198     while (s2p >= s2 && '\r' == *s2p) {
199         *s2p = '\0';
200         s2p -= 1;
201     }
202     if (c->waiting && 0 == strcmp("RTSP/1.0 200 OK", s2)) {
203         c->waiting = 0;
204         if (c->response_headers)
205             pa_headerlist_free(c->response_headers);
206         c->response_headers = pa_headerlist_new();
207         goto exit;
208     }
209     if (c->waiting) {
210         pa_log_warn("Unexpected response: %s", s2);
211         goto exit;;
212     }
213     if (!strlen(s2)) {
214         /* End of headers */
215         /* We will have a header left from our looping iteration, so add it in :) */
216         if (c->last_header) {
217             /* This is not a continuation header so let's dump it into our proplist */
218             pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
219             pa_xfree(c->last_header);
220             c->last_header = NULL;
221             c->header_buffer= NULL;
222         }
223
224         pa_log_debug("Full response received. Dispatching");
225         headers_read(c);
226         c->waiting = 1;
227         goto exit;
228     }
229
230     /* Read and parse a header (we know it's not empty) */
231     /* TODO: Move header reading into the headerlist. */
232
233     /* If the first character is a space, it's a continuation header */
234     if (c->last_header && ' ' == s2[0]) {
235         pa_assert(c->header_buffer);
236
237         /* Add this line to the buffer (sans the space. */
238         pa_strbuf_puts(c->header_buffer, &(s2[1]));
239         goto exit;
240     }
241
242     if (c->last_header) {
243         /* This is not a continuation header so let's dump the full
244           header/value into our proplist */
245         pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
246         pa_xfree(c->last_header);
247         c->last_header = NULL;
248         c->header_buffer = NULL;
249     }
250
251     delimpos = strstr(s2, ":");
252     if (!delimpos) {
253         pa_log_warn("Unexpected response when expecting header: %s", s);
254         goto exit;
255     }
256
257     pa_assert(!c->header_buffer);
258     pa_assert(!c->last_header);
259
260     c->header_buffer = pa_strbuf_new();
261     if (strlen(delimpos) > 1) {
262         /* Cut our line off so we can copy the header name out */
263         *delimpos++ = '\0';
264
265         /* Trim the front of any spaces */
266         while (' ' == *delimpos)
267             ++delimpos;
268
269         pa_strbuf_puts(c->header_buffer, delimpos);
270     } else {
271         /* Cut our line off so we can copy the header name out */
272         *delimpos = '\0';
273     }
274
275     /* Save the header name */
276     c->last_header = pa_xstrdup(s2);
277   exit:
278     pa_xfree(s2);
279 }
280
281
282 static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
283     pa_rtsp_client *c = userdata;
284     union {
285         struct sockaddr sa;
286         struct sockaddr_in in;
287         struct sockaddr_in6 in6;
288     } sa;
289     socklen_t sa_len = sizeof(sa);
290
291     pa_assert(sc);
292     pa_assert(c);
293     pa_assert(STATE_CONNECT == c->state);
294     pa_assert(c->sc == sc);
295     pa_socket_client_unref(c->sc);
296     c->sc = NULL;
297
298     if (!io) {
299         pa_log("Connection failed: %s", pa_cstrerror(errno));
300         return;
301     }
302     pa_assert(!c->io);
303     c->io = io;
304
305     c->ioline = pa_ioline_new(io);
306     pa_ioline_set_callback(c->ioline, line_callback, c);
307
308     /* Get the local IP address for use externally */
309     if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
310         char buf[INET6_ADDRSTRLEN];
311         const char *res = NULL;
312
313         if (AF_INET == sa.sa.sa_family) {
314             if ((res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf)))) {
315                 c->localip = pa_xstrdup(res);
316             }
317         } else if (AF_INET6 == sa.sa.sa_family) {
318             if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) {
319                 c->localip = pa_sprintf_malloc("[%s]", res);
320             }
321         }
322     }
323     pa_log_debug("Established RTSP connection from local ip %s", c->localip);
324
325     if (c->callback)
326         c->callback(c, c->state, NULL, c->userdata);
327 }
328
329 int pa_rtsp_connect(pa_rtsp_client *c) {
330     pa_assert(c);
331     pa_assert(!c->sc);
332
333     pa_xfree(c->session);
334     c->session = NULL;
335
336     if (!(c->sc = pa_socket_client_new_string(c->mainloop, c->hostname, c->port))) {
337         pa_log("failed to connect to server '%s:%d'", c->hostname, c->port);
338         return -1;
339     }
340
341     pa_socket_client_set_callback(c->sc, on_connection, c);
342     c->waiting = 1;
343     c->state = STATE_CONNECT;
344     return 0;
345 }
346
347 void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata) {
348     pa_assert(c);
349
350     c->callback = callback;
351     c->userdata = userdata;
352 }
353
354 void pa_rtsp_disconnect(pa_rtsp_client *c) {
355     pa_assert(c);
356
357     if (c->ioline)
358         pa_ioline_close(c->ioline);
359     else if (c->io)
360         pa_iochannel_free(c->io);
361     c->io = NULL;
362     c->ioline = NULL;
363 }
364
365
366 const char* pa_rtsp_localip(pa_rtsp_client* c) {
367     pa_assert(c);
368
369     return c->localip;
370 }
371
372 uint32_t pa_rtsp_serverport(pa_rtsp_client* c) {
373     pa_assert(c);
374
375     return c->rtp_port;
376 }
377
378 void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) {
379     pa_assert(c);
380
381     c->url = pa_xstrdup(url);
382 }
383
384 void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value)
385 {
386     pa_assert(c);
387     pa_assert(key);
388     pa_assert(value);
389
390     pa_headerlist_puts(c->headers, key, value);
391 }
392
393 void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key)
394 {
395     pa_assert(c);
396     pa_assert(key);
397
398     pa_headerlist_remove(c->headers, key);
399 }
400
401 static int rtsp_exec(pa_rtsp_client* c, const char* cmd,
402                         const char* content_type, const char* content,
403                         int expect_response,
404                         pa_headerlist* headers) {
405     pa_strbuf* buf;
406     char* hdrs;
407     ssize_t l;
408
409     pa_assert(c);
410     pa_assert(c->url);
411
412     if (!cmd)
413         return -1;
414
415     pa_log_debug("Sending command: %s", cmd);
416
417     buf = pa_strbuf_new();
418     pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
419     if (c->session)
420         pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
421
422     /* Add the headers */
423     if (headers) {
424         hdrs = pa_headerlist_to_string(headers);
425         pa_strbuf_puts(buf, hdrs);
426         pa_xfree(hdrs);
427     }
428
429     if (content_type && content) {
430         pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
431           content_type, (int)strlen(content));
432     }
433
434     pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
435
436     if (c->headers) {
437         hdrs = pa_headerlist_to_string(c->headers);
438         pa_strbuf_puts(buf, hdrs);
439         pa_xfree(hdrs);
440     }
441
442     pa_strbuf_puts(buf, "\r\n");
443
444     if (content_type && content) {
445         pa_strbuf_puts(buf, content);
446     }
447
448     /* Our packet is created... now we can send it :) */
449     hdrs = pa_strbuf_tostring_free(buf);
450     /*pa_log_debug("Submitting request:");
451     pa_log_debug(hdrs);*/
452     l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
453     pa_xfree(hdrs);
454
455     return 0;
456 }
457
458
459 int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
460     pa_assert(c);
461     if (!sdp)
462         return -1;
463
464     c->state = STATE_ANNOUNCE;
465     return rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
466 }
467
468
469 int pa_rtsp_setup(pa_rtsp_client* c) {
470     pa_headerlist* headers;
471     int rv;
472
473     pa_assert(c);
474
475     headers = pa_headerlist_new();
476     pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
477
478     c->state = STATE_SETUP;
479     rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
480     pa_headerlist_free(headers);
481     return rv;
482 }
483
484
485 int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime) {
486     pa_headerlist* headers;
487     int rv;
488     char *info;
489
490     pa_assert(c);
491     if (!c->session) {
492         /* No session in progress */
493         return -1;
494     }
495
496     /* Todo: Generate these values randomly as per spec */
497     *seq = *rtptime = 0;
498
499     headers = pa_headerlist_new();
500     pa_headerlist_puts(headers, "Range", "npt=0-");
501     info = pa_sprintf_malloc("seq=%u;rtptime=%u", *seq, *rtptime);
502     pa_headerlist_puts(headers, "RTP-Info", info);
503     pa_xfree(info);
504
505     c->state = STATE_RECORD;
506     rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
507     pa_headerlist_free(headers);
508     return rv;
509 }
510
511
512 int pa_rtsp_teardown(pa_rtsp_client *c) {
513     pa_assert(c);
514
515     c->state = STATE_TEARDOWN;
516     return rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
517 }
518
519
520 int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) {
521     pa_assert(c);
522     if (!param)
523         return -1;
524
525     c->state = STATE_SET_PARAMETER;
526     return rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
527 }
528
529
530 int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
531     pa_headerlist* headers;
532     int rv;
533     char *info;
534
535     pa_assert(c);
536
537     headers = pa_headerlist_new();
538     info = pa_sprintf_malloc("seq=%u;rtptime=%u", seq, rtptime);
539     pa_headerlist_puts(headers, "RTP-Info", info);
540     pa_xfree(info);
541
542     c->state = STATE_FLUSH;
543     rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
544     pa_headerlist_free(headers);
545     return rv;
546 }