diff options
| -rw-r--r-- | src/modules/rtp/rtsp.c | 345 | ||||
| -rw-r--r-- | src/modules/rtp/rtsp.h | 31 | 
2 files changed, 220 insertions, 156 deletions
diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c index d8440724..55d91018 100644 --- a/src/modules/rtp/rtsp.c +++ b/src/modules/rtp/rtsp.c @@ -49,6 +49,22 @@  #include "rtsp.h" +struct pa_rtsp_context { +    pa_socket_client *sc; +    pa_iochannel *io; +    pa_rtsp_cb_t callback; +    void* userdata; +    const char* useragent; +    pa_headerlist* headers; +    char* localip; +    char* url; +    uint32_t port; +    uint32_t cseq; +    char* session; +    char* transport; +    pa_rtsp_state state; +}; +  /*   * read one line from the file descriptor   * timeout: msec unit, -1 for infinite @@ -112,18 +128,10 @@ static int pa_read_line(pa_iochannel* io, char *line, int maxlen, int timeout)  static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,                          const char* content_type, const char* content,                          int expect_response, -                        pa_headerlist* headers, pa_headerlist** response_headers) { +                        pa_headerlist* headers) {      pa_strbuf* buf;      char* hdrs;      ssize_t l; -    char response[1024]; -    int timeout; -    char* token; -    const char* token_state; -    char delimiters[2]; -    char* header; -    char* delimpos; -      pa_assert(c);      pa_assert(c->url); @@ -167,91 +175,6 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,      l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));      pa_xfree(hdrs); -    /* Do we expect a response? */ -    if (!expect_response) -        return 0; - -    timeout = 5000; -    if (pa_read_line(c->io, response, sizeof(response), timeout) <= 0) { -        /*ERRMSG("%s: request failed\n",__func__);*/ -        return -1; -    } - -    delimiters[0] = ' '; -    delimiters[1] = '\0'; -    token_state = NULL; -    pa_xfree(pa_split(response, delimiters, &token_state)); -    token = pa_split(response, delimiters, &token_state); -    if (!token || strcmp(token, "200")) { -        pa_xfree(token); -        /*ERRMSG("%s: request failed, error %s\n",__func__,token);*/ -        return -1; -    } -    pa_xfree(token); - -    /* We want to return the headers? */ -    if (!response_headers) -    { -        /* We have no storage, so just clear out the response. */ -        while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) { -            /* Reduce timeout for future requests */ -            timeout = 1000; -        } -        return 0; -    } - -    /* TODO: Move header reading into the headerlist. */ -    header = NULL; -    buf = pa_strbuf_new(); -    while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) { -        /* Reduce timeout for future requests */ -        timeout = 1000; - -        /* If the first character is a space, it's a continuation header */ -        if (header && ' ' == response[0]) { -            /* Add this line to the buffer (sans the space. */ -            pa_strbuf_puts(buf, &(response[1])); -            continue; -        } - -        if (header) { -            /* This is not a continuation header so let's dump the full -               header/value into our proplist */ -            pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring_free(buf)); -            pa_xfree(header); -            buf = pa_strbuf_new(); -        } - -        delimpos = strstr(response, ":"); -        if (!delimpos) { -            /*ERRMSG("%s: Request failed, bad header\n",__func__);*/ -            return -1; -        } - -        if (strlen(delimpos) > 1) { -            /* Cut our line off so we can copy the header name out */ -            *delimpos++ = '\0'; - -            /* Trim the front of any spaces */ -            while (' ' == *delimpos) -                ++delimpos; - -            pa_strbuf_puts(buf, delimpos); -        } else { -            /* Cut our line off so we can copy the header name out */ -            *delimpos = '\0'; -        } - -        /* Save the header name */ -        header = pa_xstrdup(response); -    } -    /* We will have a header left from our looping itteration, so add it in :) */ -    if (header) { -        /* This is not a continuation header so let's dump it into our proplist */ -        pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring(buf)); -    } -    pa_strbuf_free(buf); -      return 0;  } @@ -286,6 +209,146 @@ void pa_rtsp_context_free(pa_rtsp_context* c) {  } +static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) { +    pa_strbuf* buf; +    pa_headerlist* response_headers = NULL; +    char response[1024]; +    int timeout; +    char* token; +    char* header; +    char* delimpos; +    char delimiters[] = " "; +    pa_rtsp_context *c = userdata; +    pa_assert(c); + +    /* TODO: convert this to a pa_ioline based reader */ +    if (STATE_CONNECT == c->state) { +        response_headers = pa_headerlist_new(); +    } +    timeout = 5000; +    /* read in any response headers */ +    if (pa_read_line(c->io, response, sizeof(response), timeout) > 0) { +        const char* token_state = NULL; + +        timeout = 1000; +        pa_xfree(pa_split(response, delimiters, &token_state)); +        token = pa_split(response, delimiters, &token_state); +        if (!token || strcmp(token, "200")) { +            pa_xfree(token); +            pa_log("Invalid Response"); +            /* TODO: Bail out completely */ +            return; +        } +        pa_xfree(token); + +        /* We want to return the headers? */ +        if (!response_headers) { +            /* We have no storage, so just clear out the response. */ +            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0); +        } else { +            /* TODO: Move header reading into the headerlist. */ +            header = NULL; +            buf = pa_strbuf_new(); +            while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) { +                /* If the first character is a space, it's a continuation header */ +                if (header && ' ' == response[0]) { +                    /* Add this line to the buffer (sans the space. */ +                    pa_strbuf_puts(buf, &(response[1])); +                    continue; +                } + +                if (header) { +                    /* This is not a continuation header so let's dump the full +                      header/value into our proplist */ +                    pa_headerlist_puts(response_headers, header, pa_strbuf_tostring_free(buf)); +                    pa_xfree(header); +                    buf = pa_strbuf_new(); +                } + +                delimpos = strstr(response, ":"); +                if (!delimpos) { +                    pa_log("Invalid response header"); +                    return; +                } + +                if (strlen(delimpos) > 1) { +                    /* Cut our line off so we can copy the header name out */ +                    *delimpos++ = '\0'; + +                    /* Trim the front of any spaces */ +                    while (' ' == *delimpos) +                        ++delimpos; + +                    pa_strbuf_puts(buf, delimpos); +                } else { +                    /* Cut our line off so we can copy the header name out */ +                    *delimpos = '\0'; +                } + +                /* Save the header name */ +                header = pa_xstrdup(response); +            } +            /* We will have a header left from our looping itteration, so add it in :) */ +            if (header) { +                /* This is not a continuation header so let's dump it into our proplist */ +                pa_headerlist_puts(response_headers, header, pa_strbuf_tostring(buf)); +            } +            pa_strbuf_free(buf); +        } +    } + +    /* Deal with a CONNECT response */ +    if (STATE_CONNECT == c->state) { +        const char* token_state = NULL; +        const char* pc = NULL; +        c->session = pa_xstrdup(pa_headerlist_gets(response_headers, "Session")); +        c->transport = pa_xstrdup(pa_headerlist_gets(response_headers, "Transport")); + +        if (!c->session || !c->transport) { +            pa_headerlist_free(response_headers); +            return; +        } + +        /* Now parse out the server port component of the response. */ +        c->port = 0; +        delimiters[0] = ';'; +        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_xfree(token); +                    break; +                } +            } +            pa_xfree(token); +        } +        if (0 == c->port) { +            /* Error no server_port in response */ +            pa_headerlist_free(response_headers); +            return; +        } +    } + +    /* Call our callback */ +    if (c->callback) +        c->callback(c, c->state, response_headers, c->userdata); + + +    if (response_headers) +        pa_headerlist_free(response_headers); + +    /* +    if (do_read(u) < 0 || do_write(u) < 0) { + +        if (u->io) { +            pa_iochannel_free(u->io); +            u->io = NULL; +        } + +       pa_module_unload_request(u->module); +    } +    */ +}  static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {      pa_rtsp_context *c = userdata; @@ -309,6 +372,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata      }      pa_assert(!c->io);      c->io = io; +    pa_iochannel_set_callback(c->io, io_callback, c);      /* Get the local IP address for use externally */      if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) { @@ -337,9 +401,16 @@ int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* h      }      pa_socket_client_set_callback(c->sc, on_connection, c); +    c->state = STATE_CONNECT;      return 0;  } +void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata) { +    pa_assert(c); + +    c->callback = callback; +    c->userdata = userdata; +}  void pa_rtsp_disconnect(pa_rtsp_context *c) {      pa_assert(c); @@ -356,6 +427,11 @@ const char* pa_rtsp_localip(pa_rtsp_context* c) {      return c->localip;  } +uint32_t pa_rtsp_serverport(pa_rtsp_context* c) { +    pa_assert(c); + +    return c->port; +}  void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {      pa_assert(c); @@ -363,67 +439,46 @@ void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {      c->url = pa_xstrdup(url);  } +void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value) +{ +    pa_assert(c); +    pa_assert(key); +    pa_assert(value); + +    pa_headerlist_puts(c->headers, key, value); +} + +void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key) +{ +    pa_assert(c); +    pa_assert(key); + +    pa_headerlist_remove(c->headers, key); +} +  int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {      pa_assert(c);      if (!sdp)          return -1; -    return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL, NULL); +    c->state = STATE_ANNOUNCE; +    return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);  } -int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) { +int pa_rtsp_setup(pa_rtsp_context* c) {      pa_headerlist* headers; -    pa_headerlist* rheaders; -    char delimiters[2]; -    char* token; -    const char* token_state; -    const char* pc; +    int rv;      pa_assert(c);      headers = pa_headerlist_new(); -    rheaders = pa_headerlist_new();      pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record"); -    if (pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers, &rheaders)) { -        pa_headerlist_free(headers); -        pa_headerlist_free(rheaders); -        return -1; -    } +    c->state = STATE_SETUP; +    rv = pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);      pa_headerlist_free(headers); - -    c->session = pa_xstrdup(pa_headerlist_gets(rheaders, "Session")); -    c->transport = pa_xstrdup(pa_headerlist_gets(rheaders, "Transport")); - -    if (!c->session || !c->transport) { -        pa_headerlist_free(rheaders); -        return -1; -    } - -    /* Now parse out the server port component of the response. */ -    c->port = 0; -    delimiters[0] = ';'; -    delimiters[1] = '\0'; -    token_state = NULL; -    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_xfree(token); -                break; -            } -        } -        pa_xfree(token); -    } -    if (0 == c->port) { -        /* Error no server_port in response */ -        pa_headerlist_free(rheaders); -        return -1; -    } - -    *response_headers = rheaders; -    return 0; +    return rv;  } @@ -441,7 +496,8 @@ int pa_rtsp_record(pa_rtsp_context* c) {      pa_headerlist_puts(headers, "Range", "npt=0-");      pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0"); -    rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers, NULL); +    c->state = STATE_RECORD; +    rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);      pa_headerlist_free(headers);      return rv;  } @@ -450,7 +506,8 @@ int pa_rtsp_record(pa_rtsp_context* c) {  int pa_rtsp_teardown(pa_rtsp_context *c) {      pa_assert(c); -    return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL, NULL); +    c->state = STATE_TEARDOWN; +    return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);  } @@ -459,7 +516,8 @@ int pa_rtsp_setparameter(pa_rtsp_context *c, const char* param) {      if (!param)          return -1; -    return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL, NULL); +    c->state = STATE_SET_PARAMETER; +    return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);  } @@ -472,7 +530,8 @@ int pa_rtsp_flush(pa_rtsp_context *c) {      headers = pa_headerlist_new();      pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0"); -    rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers, NULL); +    c->state = STATE_FLUSH; +    rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);      pa_headerlist_free(headers);      return rv;  } diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp.h index 181d0854..6458f851 100644 --- a/src/modules/rtp/rtsp.h +++ b/src/modules/rtp/rtsp.h @@ -36,30 +36,35 @@  #include "headerlist.h" -typedef struct pa_rtsp_context { -    pa_socket_client *sc; -    pa_iochannel *io; -    const char* useragent; -    pa_headerlist* headers; -    char* localip; -    char* url; -    uint32_t port; -    uint32_t cseq; -    char* session; -    char* transport; -} pa_rtsp_context; +typedef struct pa_rtsp_context pa_rtsp_context; +typedef enum { +  STATE_CONNECT, +  STATE_ANNOUNCE, +  STATE_SETUP, +  STATE_RECORD, +  STATE_TEARDOWN, +  STATE_SET_PARAMETER, +  STATE_FLUSH +} pa_rtsp_state; +typedef void (*pa_rtsp_cb_t)(pa_rtsp_context *c, pa_rtsp_state state, pa_headerlist* hl, void *userdata);  pa_rtsp_context* pa_rtsp_context_new(const char* useragent);  void pa_rtsp_context_free(pa_rtsp_context* c);  int pa_rtsp_connect(pa_rtsp_context* c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port); +void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata); +  void pa_rtsp_disconnect(pa_rtsp_context* c);  const char* pa_rtsp_localip(pa_rtsp_context* c); +uint32_t pa_rtsp_serverport(pa_rtsp_context* c);  void pa_rtsp_set_url(pa_rtsp_context* c, const char* url); +void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value); +void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key); +  int pa_rtsp_announce(pa_rtsp_context* c, const char* sdp); -int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers); +int pa_rtsp_setup(pa_rtsp_context* c);  int pa_rtsp_record(pa_rtsp_context* c);  int pa_rtsp_teardown(pa_rtsp_context* c);  | 
