]> code.delx.au - pulseaudio/blobdiff - src/modules/rtp/rtsp_client.c
Merge remote branch 'origin/master-tx'
[pulseaudio] / src / modules / rtp / rtsp_client.c
index 5665c9f633a898bb907318fae374722937feb842..915c1072e3f00a36ea097e5bbe7c12ab4de57b20 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
 /***
   This file is part of PulseAudio.
 
@@ -7,7 +5,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
@@ -32,6 +30,7 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <arpa/inet.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
+#include <netinet/in.h>
 
 #ifdef HAVE_SYS_FILIO_H
 #include <sys/filio.h>
 
 #ifdef HAVE_SYS_FILIO_H
 #include <sys/filio.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/strbuf.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/strbuf.h>
-#include <pulsecore/poll.h>
 #include <pulsecore/ioline.h>
 
 #include <pulsecore/ioline.h>
 
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+#include <pulsecore/poll.h>
+#endif
+
 #include "rtsp_client.h"
 
 struct pa_rtsp_client {
 #include "rtsp_client.h"
 
 struct pa_rtsp_client {
+    pa_mainloop_api *mainloop;
+    char *hostname;
+    uint16_t port;
+
     pa_socket_client *sc;
     pa_iochannel *io;
     pa_ioline *ioline;
     pa_socket_client *sc;
     pa_iochannel *io;
     pa_ioline *ioline;
@@ -70,72 +78,23 @@ struct pa_rtsp_client {
 
     char *localip;
     char *url;
 
     char *localip;
     char *url;
-    uint32_t port;
+    uint16_t rtp_port;
     uint32_t cseq;
     char *session;
     char *transport;
 };
 
     uint32_t cseq;
     char *session;
     char *transport;
 };
 
-static int pa_rtsp_exec(pa_rtsp_client* c, const char* cmd,
-                        const char* content_type, const char* content,
-                        int expect_response,
-                        pa_headerlist* headers) {
-    pa_strbuf* buf;
-    char* hdrs;
-    ssize_t l;
-
-    pa_assert(c);
-    pa_assert(c->url);
-
-    if (!cmd)
-        return -1;
-
-    buf = pa_strbuf_new();
-    pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
-    if (c->session)
-        pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
-
-    /* Add the headers */
-    if (headers) {
-        hdrs = pa_headerlist_to_string(headers);
-        pa_strbuf_puts(buf, hdrs);
-        pa_xfree(hdrs);
-    }
-
-    if (content_type && content) {
-        pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
-          content_type, (int)strlen(content));
-    }
-
-    pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
-
-    if (c->headers) {
-        hdrs = pa_headerlist_to_string(c->headers);
-        pa_strbuf_puts(buf, hdrs);
-        pa_xfree(hdrs);
-    }
-
-    pa_strbuf_puts(buf, "\r\n");
-
-    if (content_type && content) {
-        pa_strbuf_puts(buf, content);
-    }
-
-    /* Our packet is created... now we can send it :) */
-    hdrs = pa_strbuf_tostring_free(buf);
-    /*pa_log_debug("Submitting request:");
-    pa_log_debug(hdrs);*/
-    l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
-    pa_xfree(hdrs);
-
-    return 0;
-}
-
-
-pa_rtsp_client* pa_rtsp_client_new(const char* useragent) {
+pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent) {
     pa_rtsp_client *c;
 
     pa_rtsp_client *c;
 
+    pa_assert(mainloop);
+    pa_assert(hostname);
+    pa_assert(port > 0);
+
     c = pa_xnew0(pa_rtsp_client, 1);
     c = pa_xnew0(pa_rtsp_client, 1);
+    c->mainloop = mainloop;
+    c->hostname = pa_xstrdup(hostname);
+    c->port = port;
     c->headers = pa_headerlist_new();
 
     if (useragent)
     c->headers = pa_headerlist_new();
 
     if (useragent)
@@ -148,21 +107,27 @@ pa_rtsp_client* pa_rtsp_client_new(const char* useragent) {
 
 
 void pa_rtsp_client_free(pa_rtsp_client* c) {
 
 
 void pa_rtsp_client_free(pa_rtsp_client* c) {
-    if (c) {
-        if (c->sc)
-            pa_socket_client_unref(c->sc);
-
-        pa_xfree(c->url);
-        pa_xfree(c->localip);
-        pa_xfree(c->session);
-        pa_xfree(c->transport);
-        pa_xfree(c->last_header);
-        if (c->header_buffer)
-            pa_strbuf_free(c->header_buffer);
-        if (c->response_headers)
-            pa_headerlist_free(c->response_headers);
-        pa_headerlist_free(c->headers);
-    }
+    pa_assert(c);
+
+    if (c->sc)
+        pa_socket_client_unref(c->sc);
+    if (c->ioline)
+        pa_ioline_close(c->ioline);
+    else if (c->io)
+        pa_iochannel_free(c->io);
+
+    pa_xfree(c->hostname);
+    pa_xfree(c->url);
+    pa_xfree(c->localip);
+    pa_xfree(c->session);
+    pa_xfree(c->transport);
+    pa_xfree(c->last_header);
+    if (c->header_buffer)
+        pa_strbuf_free(c->header_buffer);
+    if (c->response_headers)
+        pa_headerlist_free(c->response_headers);
+    pa_headerlist_free(c->headers);
+
     pa_xfree(c);
 }
 
     pa_xfree(c);
 }
 
@@ -173,6 +138,7 @@ static void headers_read(pa_rtsp_client *c) {
 
     pa_assert(c);
     pa_assert(c->response_headers);
 
     pa_assert(c);
     pa_assert(c->response_headers);
+    pa_assert(c->callback);
 
     /* Deal with a SETUP response */
     if (STATE_SETUP == c->state) {
 
     /* Deal with a SETUP response */
     if (STATE_SETUP == c->state) {
@@ -182,8 +148,6 @@ static void headers_read(pa_rtsp_client *c) {
         c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport"));
 
         if (!c->session || !c->transport) {
         c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport"));
 
         if (!c->session || !c->transport) {
-            pa_headerlist_free(c->response_headers);
-            c->response_headers = NULL;
             pa_log("Invalid SETUP response.");
             return;
         }
             pa_log("Invalid SETUP response.");
             return;
         }
@@ -192,28 +156,22 @@ static void headers_read(pa_rtsp_client *c) {
         while ((token = pa_split(c->transport, delimiters, &token_state))) {
             if ((pc = strstr(token, "="))) {
                 if (0 == strncmp(token, "server_port", 11)) {
         while ((token = pa_split(c->transport, delimiters, &token_state))) {
             if ((pc = strstr(token, "="))) {
                 if (0 == strncmp(token, "server_port", 11)) {
-                    pa_atou(pc+1, &c->port);
+                    pa_atou(pc+1, (uint32_t*)(&c->rtp_port));
                     pa_xfree(token);
                     break;
                 }
             }
             pa_xfree(token);
         }
                     pa_xfree(token);
                     break;
                 }
             }
             pa_xfree(token);
         }
-        if (0 == c->port) {
+        if (0 == c->rtp_port) {
             /* Error no server_port in response */
             /* Error no server_port in response */
-            pa_headerlist_free(c->response_headers);
-            c->response_headers = NULL;
             pa_log("Invalid SETUP response (no port number).");
             return;
         }
     }
 
     /* Call our callback */
             pa_log("Invalid SETUP response (no port number).");
             return;
         }
     }
 
     /* Call our callback */
-    if (c->callback)
-        c->callback(c, c->state, c->response_headers, c->userdata);
-
-    pa_headerlist_free(c->response_headers);
-    c->response_headers = NULL;
+    c->callback(c, c->state, c->response_headers, c->userdata);
 }
 
 
 }
 
 
@@ -224,12 +182,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     pa_rtsp_client *c = userdata;
     pa_assert(line);
     pa_assert(c);
     pa_rtsp_client *c = userdata;
     pa_assert(line);
     pa_assert(c);
+    pa_assert(c->callback);
 
     if (!s) {
 
     if (!s) {
-        pa_log_warn("Connection closed");
-        pa_ioline_unref(c->ioline);
+        /* Keep the ioline/iochannel open as they will be freed automatically */
         c->ioline = NULL;
         c->ioline = NULL;
-        pa_rtsp_disconnect(c);
+        c->io = NULL;
+        c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
         return;
     }
 
         return;
     }
 
@@ -242,7 +201,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     }
     if (c->waiting && 0 == strcmp("RTSP/1.0 200 OK", s2)) {
         c->waiting = 0;
     }
     if (c->waiting && 0 == strcmp("RTSP/1.0 200 OK", s2)) {
         c->waiting = 0;
-        pa_assert(!c->response_headers);
+        if (c->response_headers)
+            pa_headerlist_free(c->response_headers);
         c->response_headers = pa_headerlist_new();
         goto exit;
     }
         c->response_headers = pa_headerlist_new();
         goto exit;
     }
@@ -252,13 +212,15 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     }
     if (!strlen(s2)) {
         /* End of headers */
     }
     if (!strlen(s2)) {
         /* End of headers */
-        /* We will have a header left from our looping itteration, so add it in :) */
+        /* We will have a header left from our looping iteration, so add it in :) */
         if (c->last_header) {
         if (c->last_header) {
+            char *tmp = pa_strbuf_tostring_free(c->header_buffer);
             /* This is not a continuation header so let's dump it into our proplist */
             /* This is not a continuation header so let's dump it into our proplist */
-            pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
+            pa_headerlist_puts(c->response_headers, c->last_header, tmp);
+            pa_xfree(tmp);
             pa_xfree(c->last_header);
             c->last_header = NULL;
             pa_xfree(c->last_header);
             c->last_header = NULL;
-            c->header_buffer= NULL;
+            c->header_buffer = NULL;
         }
 
         pa_log_debug("Full response received. Dispatching");
         }
 
         pa_log_debug("Full response received. Dispatching");
@@ -280,9 +242,11 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
     }
 
     if (c->last_header) {
     }
 
     if (c->last_header) {
+        char *tmp = pa_strbuf_tostring_free(c->header_buffer);
         /* This is not a continuation header so let's dump the full
           header/value into our proplist */
         /* This is not a continuation header so let's dump the full
           header/value into our proplist */
-        pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
+        pa_headerlist_puts(c->response_headers, c->last_header, tmp);
+        pa_xfree(tmp);
         pa_xfree(c->last_header);
         c->last_header = NULL;
         c->header_buffer = NULL;
         pa_xfree(c->last_header);
         c->last_header = NULL;
         c->header_buffer = NULL;
@@ -330,8 +294,8 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
 
     pa_assert(sc);
     pa_assert(c);
 
     pa_assert(sc);
     pa_assert(c);
+    pa_assert(STATE_CONNECT == c->state);
     pa_assert(c->sc == sc);
     pa_assert(c->sc == sc);
-
     pa_socket_client_unref(c->sc);
     c->sc = NULL;
 
     pa_socket_client_unref(c->sc);
     c->sc = NULL;
 
@@ -351,33 +315,35 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
         const char *res = NULL;
 
         if (AF_INET == sa.sa.sa_family) {
         const char *res = NULL;
 
         if (AF_INET == sa.sa.sa_family) {
-            res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf));
+            if ((res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf)))) {
+                c->localip = pa_xstrdup(res);
+            }
         } else if (AF_INET6 == sa.sa.sa_family) {
         } else if (AF_INET6 == sa.sa.sa_family) {
-            res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf));
+            if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) {
+                c->localip = pa_sprintf_malloc("[%s]", res);
+            }
         }
         }
-        if (res)
-            c->localip = pa_xstrdup(res);
     }
     pa_log_debug("Established RTSP connection from local ip %s", c->localip);
 
     }
     pa_log_debug("Established RTSP connection from local ip %s", c->localip);
 
-    c->waiting = 1;
-    c->state = STATE_CONNECT;
     if (c->callback)
         c->callback(c, c->state, NULL, c->userdata);
 }
 
     if (c->callback)
         c->callback(c, c->state, NULL, c->userdata);
 }
 
-int pa_rtsp_connect(pa_rtsp_client *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {
+int pa_rtsp_connect(pa_rtsp_client *c) {
     pa_assert(c);
     pa_assert(c);
-    pa_assert(mainloop);
-    pa_assert(hostname);
-    pa_assert(port > 0);
+    pa_assert(!c->sc);
+
+    pa_xfree(c->session);
+    c->session = NULL;
 
 
-    if (!(c->sc = pa_socket_client_new_string(mainloop, hostname, port))) {
-        pa_log("failed to connect to server '%s:%d'", hostname, port);
+    if (!(c->sc = pa_socket_client_new_string(c->mainloop, TRUE, c->hostname, c->port))) {
+        pa_log("failed to connect to server '%s:%d'", c->hostname, c->port);
         return -1;
     }
 
     pa_socket_client_set_callback(c->sc, on_connection, c);
         return -1;
     }
 
     pa_socket_client_set_callback(c->sc, on_connection, c);
+    c->waiting = 1;
     c->state = STATE_CONNECT;
     return 0;
 }
     c->state = STATE_CONNECT;
     return 0;
 }
@@ -392,9 +358,12 @@ void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userda
 void pa_rtsp_disconnect(pa_rtsp_client *c) {
     pa_assert(c);
 
 void pa_rtsp_disconnect(pa_rtsp_client *c) {
     pa_assert(c);
 
-    if (c->io)
+    if (c->ioline)
+        pa_ioline_close(c->ioline);
+    else if (c->io)
         pa_iochannel_free(c->io);
     c->io = NULL;
         pa_iochannel_free(c->io);
     c->io = NULL;
+    c->ioline = NULL;
 }
 
 
 }
 
 
@@ -407,7 +376,7 @@ const char* pa_rtsp_localip(pa_rtsp_client* c) {
 uint32_t pa_rtsp_serverport(pa_rtsp_client* c) {
     pa_assert(c);
 
 uint32_t pa_rtsp_serverport(pa_rtsp_client* c) {
     pa_assert(c);
 
-    return c->port;
+    return c->rtp_port;
 }
 
 void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) {
 }
 
 void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) {
@@ -433,13 +402,73 @@ void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key)
     pa_headerlist_remove(c->headers, key);
 }
 
     pa_headerlist_remove(c->headers, key);
 }
 
+static int rtsp_exec(pa_rtsp_client* c, const char* cmd,
+                        const char* content_type, const char* content,
+                        int expect_response,
+                        pa_headerlist* headers) {
+    pa_strbuf* buf;
+    char* hdrs;
+    ssize_t l;
+
+    pa_assert(c);
+    pa_assert(c->url);
+
+    if (!cmd)
+        return -1;
+
+    pa_log_debug("Sending command: %s", cmd);
+
+    buf = pa_strbuf_new();
+    pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
+    if (c->session)
+        pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
+
+    /* Add the headers */
+    if (headers) {
+        hdrs = pa_headerlist_to_string(headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    if (content_type && content) {
+        pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
+          content_type, (int)strlen(content));
+    }
+
+    pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
+
+    if (c->headers) {
+        hdrs = pa_headerlist_to_string(c->headers);
+        pa_strbuf_puts(buf, hdrs);
+        pa_xfree(hdrs);
+    }
+
+    pa_strbuf_puts(buf, "\r\n");
+
+    if (content_type && content) {
+        pa_strbuf_puts(buf, content);
+    }
+
+    /* Our packet is created... now we can send it :) */
+    hdrs = pa_strbuf_tostring_free(buf);
+    /*pa_log_debug("Submitting request:");
+    pa_log_debug(hdrs);*/
+    l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
+    pa_xfree(hdrs);
+
+    /* FIXME: this is broken, not necessarily all bytes are written */
+
+    return 0;
+}
+
+
 int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
     pa_assert(c);
     if (!sdp)
         return -1;
 
     c->state = STATE_ANNOUNCE;
 int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
     pa_assert(c);
     if (!sdp)
         return -1;
 
     c->state = STATE_ANNOUNCE;
-    return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
+    return rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
 }
 
 
 }
 
 
@@ -453,28 +482,34 @@ int pa_rtsp_setup(pa_rtsp_client* c) {
     pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
 
     c->state = STATE_SETUP;
     pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
 
     c->state = STATE_SETUP;
-    rv = pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
+    rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }
 
 
     pa_headerlist_free(headers);
     return rv;
 }
 
 
-int pa_rtsp_record(pa_rtsp_client* c) {
+int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime) {
     pa_headerlist* headers;
     int rv;
     pa_headerlist* headers;
     int rv;
+    char *info;
 
     pa_assert(c);
     if (!c->session) {
 
     pa_assert(c);
     if (!c->session) {
-        /* No seesion in progres */
+        /* No session in progress */
         return -1;
     }
 
         return -1;
     }
 
+    /* Todo: Generate these values randomly as per spec */
+    *seq = *rtptime = 0;
+
     headers = pa_headerlist_new();
     pa_headerlist_puts(headers, "Range", "npt=0-");
     headers = pa_headerlist_new();
     pa_headerlist_puts(headers, "Range", "npt=0-");
-    pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
+    info = pa_sprintf_malloc("seq=%u;rtptime=%u", *seq, *rtptime);
+    pa_headerlist_puts(headers, "RTP-Info", info);
+    pa_xfree(info);
 
     c->state = STATE_RECORD;
 
     c->state = STATE_RECORD;
-    rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
+    rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }
     pa_headerlist_free(headers);
     return rv;
 }
@@ -484,7 +519,7 @@ int pa_rtsp_teardown(pa_rtsp_client *c) {
     pa_assert(c);
 
     c->state = STATE_TEARDOWN;
     pa_assert(c);
 
     c->state = STATE_TEARDOWN;
-    return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
+    return rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
 }
 
 
 }
 
 
@@ -494,21 +529,24 @@ int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) {
         return -1;
 
     c->state = STATE_SET_PARAMETER;
         return -1;
 
     c->state = STATE_SET_PARAMETER;
-    return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
+    return rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
 }
 
 
 }
 
 
-int pa_rtsp_flush(pa_rtsp_client *c) {
+int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
     pa_headerlist* headers;
     int rv;
     pa_headerlist* headers;
     int rv;
+    char *info;
 
     pa_assert(c);
 
     headers = pa_headerlist_new();
 
     pa_assert(c);
 
     headers = pa_headerlist_new();
-    pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
+    info = pa_sprintf_malloc("seq=%u;rtptime=%u", seq, rtptime);
+    pa_headerlist_puts(headers, "RTP-Info", info);
+    pa_xfree(info);
 
     c->state = STATE_FLUSH;
 
     c->state = STATE_FLUSH;
-    rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
+    rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
     pa_headerlist_free(headers);
     return rv;
 }
     pa_headerlist_free(headers);
     return rv;
 }