📄 rtspapi.c
字号:
/* rtspapi.c - basic rtsp functions for sipum system. * (c) 2000 - 2001 Columbia University. All rights reserved.*/#include <stdio.h>#include <stdlib.h> /* malloc */#include <unistd.h> /* close */#include <sys/types.h> #include <sys/socket.h>#include <netinet/in.h> /* sockaddr_in */#include <arpa/inet.h> /* inet_ntoa */#include "librtsp.h" /* rtspapi calls are declared here */#include <errno.h>#include "dstring.h" /* DString */#include "strdupn.h" /* free_valid */#include "error.h" /* error, debug */#include "string.h" /* strcasecmp */#include "sysdep.h" /* closesocket */#include "request-common.h" /* request_common_t */#include "rfc822.h" /* Parsing routines */#include "http.h"#include "rtsp.h" /* RTSP specific parsing *//** * Global macros for RTSP implementation. * This is used in the request line. */#define RTSP_VERSION "RTSP/1.0"/** * A session object stores all information about the * RTSP session. We use a pointer to this object for * identifying a session by rtspcontext_t */typedef struct rtspsession_t { /* URI for the session */ uri_t uri; /* Socket */ int sock; /* CSeq for transmit side, incremented for every message. */ unsigned int cseq_tx; /* Media IP and port for receiving media. This maps * to client_ports; and destination for play mode. */ struct sockaddr_storage media_rx; /* Media IP and port of the server. */ struct sockaddr_storage media_tx; /* Whether the sock is connected to remote or not */ int connected; /* double: duration of the file in seconds */ float duration; /* session identifier */ char *session; /* mode, record/play */ char *mode;} rtspsession_t;typedef struct rtsp_response_t { parser_t p; char *content;} rtsp_response_t;/********************* API Utility functions *****************/rtspcontext_t RTSP_Create(const char* url){ rtspsession_t *s = NULL; int af; struct sockaddr_storage sin; /* Check if argument is valid */ if (url == NULL) { debug(DEBUG_MISC, "RTSP_Create", "URL passed is NULL\n"); goto failure; } /* Allocate a session object */ s = (rtspsession_t *)calloc(1, sizeof(rtspsession_t)); if (s == NULL) { error("RTSP_Create", "calloc failed\n"); goto failure; } s->duration = 0.; /* Parse the URL */ memset(&s->uri, 0, sizeof(s->uri)); s->uri = URI_Parse(url); af = AF_INET; /* use ipv6 if the host is a v6 host and ipv6 * is usable. */ memset(&sin, 0, sizeof(sin)); if (AFIGetAddrByName(&sin, s->uri.host) == 0) { if (sin.ss_family == AF_INET6 && AFIIPv6Active()) { af = AF_INET6; } } s->media_rx.ss_family = af; s->media_tx.ss_family = af; /* * Check if the URL scheme is valid and create * an TCP socket for rtsp:// and UDP socket for rtspu:// */ if (s->uri.scheme == NULL || *s->uri.scheme == '\0') { debug(DEBUG_MISC, "RTSP_Create", "Could not parse the URL %s\n", url); goto failure; } if (!strcasecmp(s->uri.scheme, "rtsp")) { /* Start a TCP socket */ s->sock = socket(af, SOCK_STREAM, 0); if (s->sock < 0) { error("RTSP_Create", "TCP socket() failed\n"); goto failure; } } else if (!strcasecmp(s->uri.scheme, "rtspu")) { error("RTSP_Create", "rtspu:// not supported\n"); goto failure; } else { debug(DEBUG_MISC, "RTSP_Create", "Unsupported URL scheme %s\n", s->uri.scheme); goto failure; } if (s->uri.port == 0) { debug(DEBUG_MISC, "RTSP_Create", "No port specified, using default.\n"); s->uri.port = 554; /* This is the default port number for RTSP */ } /* Initialize the cseq on tx side. */ s->cseq_tx = 1; /* Initialize the client ports */ AFISetPort(&s->media_rx, 0); return s; /* Reach here on error condition. */failure: debug(DEBUG_MISC, "RTSP_Create", "Failed\n"); if (s != NULL) { /* Close socket. */ if (s->sock >= 0) { closesocket(s->sock); } /* Free URI */ if (s->uri.scheme != NULL) { URI_Free(&s->uri); } } free_valid(s); return NULL;}void RTSP_Destroy(rtspcontext_t c){ rtspsession_t *s = (rtspsession_t *) c; debug(DEBUG_MISC, "RTSP_Destroy", "called\n"); if (s != NULL) { /* Close socket. */ if (s->sock >= 0) { closesocket(s->sock); } /* Free URI */ if (s->uri.scheme != NULL) { URI_Free(&s->uri); } free_valid(s->session); free_valid(s->mode); memset(s, 0, sizeof(*s)); } free_valid(s); return;}void RTSP_SetDestinationAddress(rtspcontext_t c, const char *addr){ struct sockaddr_storage sin; rtspsession_t *s = (rtspsession_t *) c; if (s == NULL || addr == NULL) { debug(DEBUG_MISC, "RTSP_SetDestinationAddress", "context or addr is NULL\n"); return; } memset(&sin, 0, sizeof(sin)); if (AFIGetAddrByName(&sin, addr) == 0) { memcpy(&s->media_rx, &sin, sizeof(sin)); return; } else { error("RTSP_SetDestinationAddress", "Error resolving %s\n", addr); return; } return;}void RTSP_SetClientPort(rtspcontext_t c, unsigned short port){ rtspsession_t *s = (rtspsession_t *) c; if (s == NULL) { debug(DEBUG_MISC, "RTSP_SetClientPort", "Context is NULL\n"); return; } AFISetPort(&s->media_rx, port); return;}char* RTSP_GetServerAddress(rtspcontext_t c){ rtspsession_t *s = (rtspsession_t *) c; struct sockaddr_storage sin; socklen_t len; if (s == NULL || s->sock < 0) { debug(DEBUG_MISC, "RTSP_GetServerAddress", "Context is NULL or socket is not connected\n"); return NULL; } /* Get the remote name of the connected socket. */ len = sizeof(sin); if (getpeername(s->sock, (struct sockaddr *)&sin, &len) == 0) { char *p = AFIGetDottedAddress((struct sockaddr*)&sin); return p; /* no need for strdup, as the dot fn returns a new str */ } else { debug(DEBUG_MISC, "RTSP_GetServerAddress", "getpeername failed\n"); return NULL; }}unsigned short RTSP_GetServerPort(rtspcontext_t c){ rtspsession_t *s = (rtspsession_t *) c; if (s == NULL) { debug(DEBUG_MISC, "RTSP_GetServerPort", "Context is NULL\n"); return 0; } return AFIGetPort(&s->media_tx);}char * RTSP_GetSessionId(rtspcontext_t c){ rtspsession_t *s = (rtspsession_t *) c; if (s == NULL) { debug(DEBUG_MISC, "RTSP_GetSessionId", "Context is NULL\n"); return 0; } if (s->session) { return strdup(s->session); } else { return NULL; }}float RTSP_GetDuration(rtspcontext_t c){ rtspsession_t *s = (rtspsession_t *) c; if (s == NULL) { debug(DEBUG_MISC, "RTSP_GetDuration", "Context is NULL\n"); return 0; } return s->duration;}int RTSP_Connect(rtspcontext_t c){ struct sockaddr_storage sin; int sinlen; rtspsession_t *s = (rtspsession_t *) c; if (s == NULL || s->sock < 0 || s->uri.host == NULL || s->uri.port == 0) { debug(DEBUG_MISC, "RTSP_Connect", "Parameter is incorrect\n"); return -1; } s->connected = 0; memset(&sin, 0, sizeof(sin)); if (AFIGetAddrByName(&sin, s->uri.host) == 0) { AFISetPort(&sin, s->uri.port); sinlen = (sin.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); if (connect(s->sock, (const struct sockaddr *)&sin, sinlen) != 0) { error("RTSP_Connect", "Connect failed\n"); return -1; } s->connected = 1; } else { error("RTSP_Connect", "Unable to resolve address %s\n", s->uri.host); } return 0;}int RTSP_Send(rtspsession_t *s, DString *msg){ int e, retrycount = 2; if (s == NULL || msg== NULL) { debug(DEBUG_MISC, "RTSP_Send", "incorrect parameter\n"); return -1; } while (--retrycount >= 0) { if (!s->connected && RTSP_Connect(s) < 0) { error("RTSP_Send", "Couldnot connect to the server\n"); return -1; } debug(DEBUG_MISC, "RTSP_Send", "Sending %d bytes to socket %d,\n%s\n", DStringLength(msg), s->sock, DStringValue(msg)); if ((e = send(s->sock, DStringValue(msg), DStringLength(msg), 0)) <= 0) { if (retrycount && (errno == EBADF || errno == ENOTSOCK)) { s->connected = 0; continue; } debug(DEBUG_MISC, "RTSP_Send", "send() failed\n"); return -1; } else { break; } } /* while */ debug(DEBUG_MISC, "RTSP_Send", "Sent %d bytes\n", e); return 0;}void RTSP_ResponseFree(rtsp_response_t *r){ if (r) { free_valid(r->content); HTTP_Free(&r->p); } free_valid(r);}/***************** Parsing related functions ****************/rtsp_response_t *RTSP_ReceiveResponse(rtspsession_t * s){ enum {Header, CR, CRNL, CRNLCR, NL, Body} state = Header; int content_length; /* Content-Length field */ int cl = 0; /* bytes read in content (body) */ int c; /* current character */ char ch; /* same, as char */ DString header, body; /* header and body */ rtsp_response_t *r = NULL; /* response message */ FILE_SOCKET *f; /* socket, as FILE */ if (s == NULL) { debug(DEBUG_MISC, "RTSP_ReceiveResponse", "incorrect parameter\n"); return NULL; } f = fdopen_socket(s->sock, "r"); if (f == NULL) { error ("RTSP_ReceiveResponse", "Couldn't fdopen socket %d for reading", s->sock); return NULL; } c = EOF; /* To avoid warning */ /* Once around the loop for each message. */ DStringInit(&header); DStringInit(&body); state = Header; content_length = INFINITE_CONTENT_LENGTH; cl = 0; r = NULL; debug(DEBUG_MISC, "RTSP_ReceiveResponse", "Waiting to receive packet\n"); while ((cl < content_length) && ((c = getc_socket(f)) != EOF)) { ch = c; DStringAppend(state == Body ? &body : &header, &ch, 1); switch (state) { case Header: if (c == '\r') state = CR; else if (c == '\n') state = NL; break; case CR: if (c == '\n') state = CRNL; else state = Header; break; case CRNL: if (c == '\r') state = CRNLCR; else state = Header; break; case NL: case CRNLCR: if (c == '\n') { if (header.length > 4) { debug(DEBUG_MISC, "RTSP_ReceiveResponse", "Incoming TCP message on socket %d (%d bytes)\n%s\n", s->sock, header.length, DStringValue(&header)); r = (rtsp_response_t *)calloc(1, sizeof(rtsp_response_t)); if (HTTP_Parse(DStringValue(&header), &r->p) < 0) { /* If this was a request whose headers didn't parse, we want * to try to send a 400 response to it. On the other hand, * there's not much we can do with responses that didn't parse, * so just mark them as the unknown message type so that the * server ignores them and frees their memory. */ if (r->p.type == http_request) { r->p.status = 400; } else { r->p.type = http_unknown_type; } } content_length = HTTP_ContentLength(&r->p); /* INFINITE_CONTENT_LENGTH is the "default" value returned by the parser, implying content length was not specified */ if (content_length == INFINITE_CONTENT_LENGTH) { content_length = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -