├── Makefile ├── README ├── base64.c ├── base64.h ├── buffer.h ├── h264dec.c ├── h264dec.h ├── http ├── Makefile.in ├── README ├── channels.h ├── html.h ├── html │ └── index.html ├── main.c ├── network.c └── network.h ├── network.c ├── network.h ├── rtcp.c ├── rtcp.h ├── rtp.c ├── rtp.h ├── rtsp.c ├── rtsp.h ├── streamer.c ├── streamer.h ├── utils.c └── utils.h /Makefile: -------------------------------------------------------------------------------- 1 | ARM_TOOLCHAIN = /home/edsiper/ToolChain/Mozart_Toolchain/ 2 | ARM_ROOT = $(ARM_TOOLCHAIN)/arm-eabi-uclibc 3 | CFLAGS = -O0 -g3 -Wall -Wno-format-security 4 | OBJ = streamer.o network.o base64.o utils.o rtp.o rtcp.o rtsp.o h264dec.o 5 | SOURCES = streamer.c network.c base64.c utils.c rtp.c rtcp.c rtsp.c h264dec.c 6 | LIBS = -lpthread 7 | 8 | all: h264dec 9 | 10 | h264dec: $(OBJ) 11 | gcc $(CFLAGS) -o $@ $(OBJ) $(LIBS) 12 | 13 | arm: $(OBJ) 14 | arm-linux-gcc --sysroot=$(ARM_ROOT) $(SOURCES) -o h264dec $(LIBS) 15 | 16 | clean: 17 | rm -rf *~ *.o rtsp 18 | 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | H264Dec 2 | ======== 3 | This programs connects to a RTSP server and record 4 | in a file the H264 incoming stream. 5 | 6 | Known Bugs: 7 | ----------- 8 | RTCP support is partially broken and not well defined, 9 | so some servers can complain about the stats and close 10 | the connections. 11 | 12 | -------------------------------------------------------------------------------- /base64.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* 4 | * Base64 encoding/decoding (RFC1341) 5 | * Copyright (c) 2005, Jouni Malinen 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as 9 | * published by the Free Software Foundation. 10 | * 11 | * Alternatively, this software may be distributed under the terms of BSD 12 | * license. 13 | * 14 | * See README and COPYING for more details. 15 | */ 16 | 17 | /* 18 | * @OBJ_NAME: Base64 19 | * @OBJ_DESC: The base64 package allows you to encode or decode text using 20 | * the Base64 algorithm. 21 | * @PKG_HEADER: #include "packages/base64/base64.h" 22 | * @PKG_INIT: duda_load_package(base64, "base64"); 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include "base64.h" 29 | 30 | static const unsigned char base64_table[64] = 31 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 32 | 33 | /** 34 | * base64_decode - Base64 decode 35 | * @src: Data to be decoded 36 | * @len: Length of the data to be decoded 37 | * @out_len: Pointer to output length variable 38 | * Returns: Allocated buffer of out_len bytes of decoded data, 39 | * or %NULL on failure 40 | * 41 | * Caller is responsible for freeing the returned buffer. 42 | */ 43 | 44 | /* 45 | * @METHOD_NAME: decode 46 | * @METHOD_DESC: Decode Base64 data 47 | * @METHOD_PROTO: unsigned char *decode(const unsigned char *src, size_t len, size_t *out_len) 48 | * @METHOD_PARAM: src The data to be decoded 49 | * @METHOD_PARAM: len Length of the data to be decoded 50 | * @METHOD_PARAM: out_len Pointer to output length variable. 51 | * @METHOD_RETURN: Returns a new allocated buffer of out_len bytes of decoded data, or NULL on failure 52 | */ 53 | unsigned char *base64_decode(const unsigned char *src, size_t len, 54 | size_t *out_len) 55 | { 56 | unsigned char dtable[256], *out, *pos, in[4], block[4], tmp; 57 | size_t i, count; 58 | 59 | memset(dtable, 0x80, 256); 60 | for (i = 0; i < sizeof(base64_table); i++) 61 | dtable[base64_table[i]] = i; 62 | dtable['='] = 0; 63 | 64 | count = 0; 65 | for (i = 0; i < len; i++) { 66 | if (dtable[src[i]] != 0x80) 67 | count++; 68 | } 69 | 70 | if (count % 4) 71 | return NULL; 72 | 73 | pos = out = malloc(count); 74 | if (out == NULL) 75 | return NULL; 76 | 77 | count = 0; 78 | for (i = 0; i < len; i++) { 79 | tmp = dtable[src[i]]; 80 | if (tmp == 0x80) 81 | continue; 82 | 83 | in[count] = src[i]; 84 | block[count] = tmp; 85 | count++; 86 | if (count == 4) { 87 | *pos++ = (block[0] << 2) | (block[1] >> 4); 88 | *pos++ = (block[1] << 4) | (block[2] >> 2); 89 | *pos++ = (block[2] << 6) | block[3]; 90 | count = 0; 91 | } 92 | } 93 | 94 | if (pos > out) { 95 | if (in[2] == '=') 96 | pos -= 2; 97 | else if (in[3] == '=') 98 | pos--; 99 | } 100 | 101 | *out_len = pos - out; 102 | return out; 103 | } 104 | -------------------------------------------------------------------------------- /base64.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #ifndef BASE64_H 4 | #define BASE64_H 5 | #include 6 | 7 | unsigned char *base64_decode(const unsigned char *src, size_t len, 8 | size_t *out_len); 9 | #endif 10 | -------------------------------------------------------------------------------- /buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef BUFFER_H 2 | #define BUFFER_H 3 | 4 | #define MAX_BUF_SIZE (65536 * 10) 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /h264dec.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTSP Client 4 | * ----------- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | #define _GNU_SOURCE 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /* local headers */ 24 | #include "rtsp.h" 25 | #include "streamer.h" 26 | #include "h264dec.h" 27 | 28 | void help(int status) 29 | { 30 | printf("Usage: h264dec [-v] [-V] [-d FILENAME] -n CHANNEL_NAME -s rtsp://stream\n\n"); 31 | printf(" -d, --dump=FILENAME Dump H264 video data to a file\n"); 32 | printf(" -s, --stream=URL RTSP media stream address\n"); 33 | printf(" -n, --name=CHANNEL_NAME Name for local channel identifier\n"); 34 | printf(" -h, --help print this help\n"); 35 | printf(" -v, --version print version number\n"); 36 | printf(" -V, --verbose enable verbose mode\n"); 37 | printf("\n"); 38 | exit(status); 39 | } 40 | 41 | void banner() 42 | { 43 | printf("H264Dec v%s\n\n", VERSION); 44 | } 45 | 46 | int main(int argc, char **argv) 47 | { 48 | int opt; 49 | 50 | /* defaults */ 51 | opt_stdout = 0; 52 | opt_verbose = 0; 53 | opt_stream = NULL; 54 | opt_name = NULL; 55 | stream_port = RTSP_PORT; 56 | client_port = RTSP_CLIENT_PORT; 57 | stream_dump = NULL; 58 | 59 | static const struct option long_opts[] = { 60 | { "stdout", no_argument , NULL, 'o' }, 61 | { "dump", required_argument, NULL, 'd' }, 62 | { "stream", required_argument, NULL, 's' }, 63 | { "port", required_argument, NULL, 'p' }, 64 | { "name", required_argument, NULL, 'n' }, 65 | { "version", no_argument, NULL, 'v' }, 66 | { "verbose", no_argument, NULL, 'V' }, 67 | { "help", no_argument, NULL, 'h' }, 68 | { NULL, 0, NULL, 0 } 69 | }; 70 | 71 | while ((opt = getopt_long(argc, argv, "od:s:p:n:vVh", 72 | long_opts, NULL)) != -1) { 73 | switch (opt) { 74 | case 'o': 75 | opt_stdout = 1; 76 | break; 77 | case 'd': 78 | stream_dump = strdup(optarg); 79 | break; 80 | case 's': 81 | opt_stream = strdup(optarg); 82 | break; 83 | case 'p': 84 | client_port = atoi(optarg); 85 | break; 86 | case 'n': 87 | opt_name = strdup(optarg); 88 | break; 89 | case 'v': 90 | banner(); 91 | exit(EXIT_SUCCESS); 92 | case 'V': 93 | opt_verbose = 1; 94 | break; 95 | case 'h': 96 | help(EXIT_SUCCESS); 97 | case '?': 98 | help(EXIT_FAILURE); 99 | } 100 | } 101 | 102 | 103 | if (!opt_stream || strncmp(opt_stream, PROTOCOL_PREFIX, 104 | sizeof(PROTOCOL_PREFIX) - 1) != 0) { 105 | printf("Error: Invalid stream input.\n\n"); 106 | help(EXIT_FAILURE); 107 | } 108 | 109 | if (!opt_name) { 110 | printf("Error: Local channel name not specified.\n\n"); 111 | help(EXIT_FAILURE); 112 | } 113 | 114 | /* RTSP loop */ 115 | while (1) { 116 | rtsp_loop(); 117 | printf("[ERROR] RTSP Loop stopped, waiting 5 seconds...\n"); 118 | sleep(5); 119 | exit(1); 120 | } 121 | 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /h264dec.h: -------------------------------------------------------------------------------- 1 | #ifndef H264DEC_H 2 | #define H264DEC_H 3 | 4 | /* global variables */ 5 | int rtsp_cseq; 6 | int client_port; 7 | int opt_verbose; 8 | int opt_stdout; 9 | char *opt_stream; 10 | char *opt_name; 11 | char *stream_host; 12 | char *dsp; 13 | unsigned long stream_port; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /http/Makefile.in: -------------------------------------------------------------------------------- 1 | CC = gcc -g -Wall -DDEBUG 2 | INCDIR = ../../src -I../../../../src/include/ -I../../ 3 | OBJECTS = main.o network.o 4 | 5 | all: h264streamer.duda 6 | 7 | h264streamer.duda: $(OBJECTS) 8 | $(CC) $(CFLAGS) $(LDFLAGS) $(DEFS) -shared -o $@ $^ -lc 9 | 10 | .c.o: 11 | $(CC) $(CFLAGS) $(LDFLAGS) $(DEFS) -I$(INCDIR) -fPIC -c $< 12 | 13 | clean: 14 | rm -rf *.o *~ *.*duda* 15 | -------------------------------------------------------------------------------- /http/README: -------------------------------------------------------------------------------- 1 | In order to run this web server you must use the following 2 | URL address: 3 | 4 | http://localhost:2001/h264streamer/ 5 | -------------------------------------------------------------------------------- /http/channels.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #ifndef CHANNELS_H 4 | #define CHANNELS_H 5 | 6 | /* Root path for channel list */ 7 | #define CH_ROOT "/tmp/" 8 | 9 | /* Extension for channel files */ 10 | #define CH_SOCK ".h264s.sock" 11 | #define CH_META ".h264s.meta" 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /http/html.h: -------------------------------------------------------------------------------- 1 | #ifndef HTML_H 2 | #define HTML_H 3 | 4 | #define HTTP_CONTENT_TYPE_H264 "Content-Type: video/h264" 5 | #define HTTP_CHUNKED_TE "Transfer-Encoding: chunked" 6 | 7 | #define HTML_CHANNEL_HEADER \ 8 | "H264 Streamer" \ 9 | "

Channel List

" 10 | 11 | #define HTML_CHANNEL_FOOTER "" 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /http/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | H264 Streamer 4 | 5 | 6 | 7 | Service is running 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /http/main.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* headers required by common glibc calls */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | /* Duda headers and definition */ 12 | #include "webservice.h" 13 | #include "channels.h" 14 | #include "network.h" 15 | #include "html.h" 16 | 17 | DUDA_REGISTER("Duda HTTP Service", "H264 Streamer"); 18 | 19 | /* List channels available */ 20 | void cb_list(duda_request_t *dr) 21 | { 22 | /* 23 | * Channels are registered into the filesystem under 24 | * the '/tmp' directory, format is the following: 25 | * 26 | * - Each channel must have two files: 27 | * - Metadata file (stream header SPS PSP): channel_name.h264st.meta 28 | * - Unix socket file: channel_name.h264st.sock 29 | */ 30 | 31 | unsigned int offset; 32 | DIR *dir; 33 | struct dirent *ent; 34 | 35 | dir = opendir(CH_ROOT); 36 | if (!dir) { 37 | response->http_status(dr, 404); 38 | response->end(dr, NULL); 39 | } 40 | 41 | response->printf(dr, HTML_CHANNEL_HEADER); 42 | 43 | while ((ent = readdir(dir)) != NULL) { 44 | if (ent->d_name[0] == '.') { 45 | continue; 46 | } 47 | 48 | /* Look just for regular files and socket */ 49 | if (ent->d_type != DT_REG && ent->d_type != DT_SOCK) { 50 | continue; 51 | } 52 | 53 | if (strlen(ent->d_name) <= sizeof(CH_SOCK)) { 54 | continue; 55 | } 56 | 57 | offset = (unsigned int) (strlen(ent->d_name) - sizeof(CH_SOCK) + 1); 58 | if (strncmp(ent->d_name + offset, CH_SOCK, sizeof(CH_SOCK)) != 0) { 59 | continue; 60 | } 61 | 62 | response->printf(dr, "Channel: %s
\n", ent->d_name); 63 | } 64 | 65 | closedir(dir); 66 | 67 | response->printf(dr, HTML_CHANNEL_FOOTER); 68 | response->http_status(dr, 200); 69 | response->http_header(dr, "Content-Type: text/html"); 70 | response->end(dr, NULL); 71 | } 72 | 73 | /* Connect to a streaming */ 74 | void cb_play(duda_request_t *dr) 75 | { 76 | int s; 77 | int fd; 78 | int len; 79 | int size = 64; 80 | int chunk_len; 81 | int raw_size = 65536*4; 82 | char raw[raw_size]; 83 | char *h264_header; 84 | char *channel; 85 | char *file_sock; 86 | char *file_meta; 87 | char chunk[size]; 88 | struct stat st; 89 | const char *base_url = "/h264streamer/play/"; 90 | 91 | /* 92 | * Get channel name 93 | * 94 | */ 95 | s = (dr->sr->uri.len - strlen(base_url)); 96 | 97 | if (s < 1) { 98 | response->http_status(dr, 404); 99 | response->end(dr, NULL); 100 | } 101 | 102 | channel = monkey->mem_alloc(s + 1); 103 | strncpy(channel, dr->sr->uri.data + strlen(base_url), s); 104 | channel[s] = '\0'; 105 | 106 | /* file sock */ 107 | s = strlen(CH_ROOT) + strlen(CH_SOCK) + strlen(channel) + 1; 108 | file_sock = malloc(s); 109 | snprintf(file_sock, s, "%s%s%s", CH_ROOT, channel, CH_SOCK); 110 | 111 | /* file meta */ 112 | file_meta = malloc(s); 113 | snprintf(file_meta, s, "%s%s%s", CH_ROOT, channel, CH_META); 114 | 115 | /* validate meta data file */ 116 | if (stat(file_meta, &st) != 0) { 117 | response->http_status(dr, 400); 118 | response->printf(dr, "Invalid channel"); 119 | response->end(dr, NULL); 120 | } 121 | 122 | /* read meta header */ 123 | h264_header = malloc(st.st_size); 124 | fd = open(file_meta, O_RDONLY); 125 | read(fd, h264_header, st.st_size); 126 | close(fd); 127 | 128 | /* response headers */ 129 | response->http_status(dr, 200); 130 | response->http_content_length(dr, -1); 131 | response->http_header(dr, HTTP_CONTENT_TYPE_H264); 132 | response->http_header(dr, HTTP_CHUNKED_TE); 133 | response->send_headers(dr); 134 | 135 | /* meta */ 136 | len = snprintf(chunk, size, "%X\r\n", (int) st.st_size); 137 | response->print(dr, chunk, len); 138 | response->print(dr, h264_header, st.st_size); 139 | response->flush(dr); 140 | 141 | //send(dr->cs->socket, chunk, len, 0); 142 | //send(dr->cs->socket, h264_header, st.st_size, 0); 143 | 144 | /* Connect to the decoded h264 video stream */ 145 | int stream = -1; 146 | while (stream == -1) { 147 | stream = network_connect(file_sock); 148 | } 149 | 150 | printf("Connected to stream...\n"); 151 | while (1) { 152 | memset(raw, '\0', sizeof(raw)); 153 | len = recv(stream, raw, raw_size, 0); 154 | if (len == -1) { 155 | sleep(0.2); 156 | continue; 157 | } 158 | printf("recv=%i\n", len); 159 | 160 | chunk_len = snprintf(chunk, size, "%X\r\n", len); 161 | 162 | /* 163 | response->print(dr, chunk, chunk_len); 164 | response->print(dr, raw, len); 165 | response->flush(dr); 166 | */ 167 | int r; 168 | 169 | r = send(dr->cs->socket, chunk, chunk_len, 0); 170 | printf("print chunk ret = %i\n", r); 171 | if (r == -1) { 172 | perror("send"); 173 | } 174 | 175 | r = send(dr->cs->socket, raw, len, 0); 176 | printf("print raw ret = %i\n", r); 177 | if (r == -1) { 178 | perror("send"); 179 | } 180 | 181 | continue; 182 | 183 | 184 | r = response->print(dr, chunk, chunk_len); 185 | printf("print chunk ret = %i\n", r); 186 | 187 | 188 | r = response->print(dr, raw, len); 189 | printf("print chunk ret = %i\n", r); 190 | len = response->flush(dr); 191 | printf(" BYTES SENT: %i\n", len); 192 | } 193 | //response->end(dr, NULL); 194 | } 195 | 196 | int duda_main() 197 | { 198 | map->static_add("/channels/", "cb_list"); 199 | map->static_add("/play", "cb_play"); 200 | 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /http/network.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int network_connect(const char *path) 10 | { 11 | struct sockaddr_un address; 12 | int socket_fd; 13 | 14 | socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); 15 | if(socket_fd < 0) { 16 | printf("socket() failed\n"); 17 | return 1; 18 | } 19 | 20 | /* start with a clean address structure */ 21 | memset(&address, 0, sizeof(struct sockaddr_un)); 22 | address.sun_family = AF_UNIX; 23 | snprintf(address.sun_path, sizeof(address.sun_path), "%s", path); 24 | 25 | if(connect(socket_fd, 26 | (struct sockaddr *) &address, 27 | sizeof(struct sockaddr_un)) != 0) 28 | { 29 | printf("connect() failed\n"); 30 | return 1; 31 | } 32 | 33 | return socket_fd; 34 | } 35 | -------------------------------------------------------------------------------- /http/network.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #ifndef NETWORK_H 4 | #define NETWORK_H 5 | 6 | int network_connect(const char *path); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /network.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTSP Client 4 | * ----------- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | /* generic */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* networking */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | /* Connect to a TCP socket server and returns the file descriptor */ 26 | int net_tcp_connect(char *host, unsigned long port) 27 | { 28 | int res; 29 | int sock_fd; 30 | struct sockaddr_in *remote; 31 | 32 | sock_fd = socket(PF_INET, SOCK_STREAM, 0); 33 | if (sock_fd <= 0) { 34 | printf("Error: could not create socket\n"); 35 | return -1; 36 | } 37 | 38 | remote = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in)); 39 | remote->sin_family = AF_INET; 40 | res = inet_pton(AF_INET, host, (void *) (&(remote->sin_addr.s_addr))); 41 | 42 | if (res < 0) { 43 | printf("Error: Can't set remote->sin_addr.s_addr\n"); 44 | free(remote); 45 | return -1; 46 | } 47 | else if (res == 0) { 48 | printf("Error: Invalid address '%s'\n", host); 49 | free(remote); 50 | return -1; 51 | } 52 | 53 | remote->sin_port = htons(port); 54 | if (connect(sock_fd, 55 | (struct sockaddr *) remote, sizeof(struct sockaddr)) == -1) { 56 | close(sock_fd); 57 | printf("Error connecting to %s:%lu\n", host, port); 58 | free(remote); 59 | return -1; 60 | } 61 | 62 | free(remote); 63 | return sock_fd; 64 | } 65 | 66 | 67 | /* Connect to a UDP socket server and returns the file descriptor */ 68 | int net_udp_connect(char *host, unsigned long port) 69 | { 70 | int res; 71 | int sock_fd; 72 | struct sockaddr_in *remote; 73 | 74 | sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 75 | if (sock_fd <= 0) { 76 | printf("Error: could not create socket\n"); 77 | return -1; 78 | } 79 | 80 | remote = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in)); 81 | remote->sin_family = AF_INET; 82 | res = inet_pton(AF_INET, host, (void *) (&(remote->sin_addr.s_addr))); 83 | 84 | if (res < 0) { 85 | printf("Error: Can't set remote->sin_addr.s_addr\n"); 86 | free(remote); 87 | return -1; 88 | } 89 | else if (res == 0) { 90 | printf("Error: Invalid address '%s'\n", host); 91 | free(remote); 92 | return -1; 93 | } 94 | 95 | remote->sin_port = htons(port); 96 | if (connect(sock_fd, 97 | (struct sockaddr *) remote, sizeof(struct sockaddr)) == -1) { 98 | close(sock_fd); 99 | printf("Error connecting to %s:%lu\n", host, port); 100 | free(remote); 101 | return -1; 102 | } 103 | 104 | free(remote); 105 | return sock_fd; 106 | } 107 | 108 | int net_unix_sock(const char *path) 109 | { 110 | int server_fd; 111 | size_t address_length; 112 | struct sockaddr_un address; 113 | 114 | /* Create listening socket */ 115 | server_fd = socket(PF_UNIX, SOCK_STREAM, 0); 116 | if (server_fd < 0) { 117 | perror("socket() failed"); 118 | exit(EXIT_FAILURE); 119 | } 120 | 121 | /* just in case, remove previous sock */ 122 | unlink(path); 123 | 124 | address.sun_family = AF_UNIX; 125 | snprintf(address.sun_path, sizeof(address.sun_path), path); 126 | address_length = sizeof(address.sun_family) + strlen(path); 127 | 128 | if (bind(server_fd, (struct sockaddr *) &address, address_length) != 0) { 129 | perror("bind"); 130 | exit(EXIT_FAILURE); 131 | } 132 | 133 | if (listen(server_fd, 5) != 0) { 134 | perror("listen"); 135 | exit(EXIT_FAILURE); 136 | } 137 | 138 | return server_fd; 139 | } 140 | 141 | int net_sock_nonblock(int sockfd) 142 | { 143 | if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK) == -1) { 144 | perror("fcntl"); 145 | return -1; 146 | } 147 | 148 | return 0; 149 | } 150 | 151 | 152 | int net_sock_cork(int fd, int state) 153 | { 154 | return setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)); 155 | } 156 | 157 | int net_send16(int fd, uint16_t n) 158 | { 159 | uint16_t nbo = htons(n); 160 | return send(fd, &nbo, sizeof(nbo), 0); 161 | } 162 | 163 | int net_send32(int fd, uint32_t n) 164 | { 165 | uint32_t nbo = htonl(n); 166 | return send(fd, &nbo, sizeof(nbo), 0); 167 | } 168 | -------------------------------------------------------------------------------- /network.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #include 4 | 5 | #ifndef NETWORK_H 6 | #define NETWORK_H 7 | 8 | int net_tcp_connect(char *host, unsigned long port); 9 | int net_udp_connect(char *host, unsigned long port); 10 | int net_unix_sock(const char *path); 11 | int net_sock_nonblock(int sockfd); 12 | int net_sock_cork(int fd, int state); 13 | int net_send16(int fd, uint16_t n); 14 | int net_send32(int fd, uint32_t n); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /rtcp.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTCP 4 | * ---- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "rtp.h" 19 | #include "rtcp.h" 20 | #include "rtsp.h" 21 | #include "network.h" 22 | 23 | /* Calculate jitter bassed on stats */ 24 | static uint32_t rtcp_jitter() 25 | { 26 | return rtp_st.jitter; 27 | }; 28 | 29 | uint32_t rtcp_dlsr() 30 | { 31 | struct timeval now; 32 | double delay; 33 | 34 | if (rtp_st.last_rcv_SR_ts == 0) { 35 | return 0; 36 | } 37 | 38 | gettimeofday(&now,NULL); 39 | delay= (now.tv_sec - rtp_st.last_rcv_SR_time.tv_sec) + 40 | ((now.tv_usec - rtp_st.last_rcv_SR_time.tv_usec)*1e-6); 41 | delay= (delay * 65536); 42 | rtp_st.delay_snc_last_SR = (uint32_t) delay; 43 | return rtp_st.delay_snc_last_SR; 44 | } 45 | 46 | static void compute_rtt(const struct timeval *now){ 47 | uint64_t curntp = rtp_timeval_to_ntp(now); 48 | uint32_t approx_ntp = (curntp >> 16) & 0xFFFFFFFF; 49 | uint32_t last_sr_time = rtp_st.last_rcv_SR_ts; 50 | uint32_t sr_delay = rtp_st.delay_snc_last_SR; 51 | 52 | if (last_sr_time !=0 && sr_delay!=0){ 53 | double rtt_frac = approx_ntp-last_sr_time-sr_delay; 54 | rtp_st.rtt_frac /= 65536.0; 55 | } 56 | } 57 | 58 | /* 59 | * Decode a RTSP payload and strip the RTCP frames, it returns an array of 60 | * rtcp_pkg struct and set the number of entries in count variable 61 | */ 62 | struct rtcp_pkg *rtcp_decode(unsigned char *payload, 63 | unsigned long len, int *count) 64 | { 65 | int i = 0; 66 | int start = 0; 67 | int idx = 0; 68 | struct timeval now; 69 | 70 | *count = 0; 71 | gettimeofday(&now, NULL); 72 | 73 | /* 74 | * We define a maximum of 16 RTCP packets to decode, rarely 75 | * we will have more than two. 76 | */ 77 | struct rtcp_pkg *pk = malloc(sizeof(struct rtcp_pkg) * 16); 78 | 79 | while (i < len) { 80 | start = i; 81 | 82 | /* Decode RTCP */ 83 | pk[idx].version = (payload[i] >> 6); 84 | pk[idx].padding = (payload[i] & 0x20) >> 5; 85 | pk[idx].extension = (payload[i] & 0x10) >> 4; 86 | pk[idx].ccrc = (payload[i] & 0xF); 87 | 88 | i++; 89 | pk[idx].type = (payload[i]); 90 | 91 | /* length */ 92 | i++; 93 | pk[idx].length = (payload[i] * 256); 94 | i++; 95 | pk[idx].length += payload[i]; 96 | 97 | if (debug_rtcp) { 98 | printf("RTCP Version : %i\n", pk[idx].version); 99 | printf(" Padding : %i\n", pk[idx].padding); 100 | printf(" Extension: %i\n", pk[idx].extension); 101 | printf(" CCRC : %i\n", pk[idx].ccrc); 102 | printf(" Type : %i\n", pk[idx].type); 103 | printf(" Length : %i (%i bytes)\n", 104 | pk[idx].length, (pk[idx].length + 1) * 4); 105 | } 106 | 107 | /* server report */ 108 | if (pk[idx].type == RTCP_SR) { 109 | pk[idx].ssrc = ( 110 | (payload[i + 4]) | 111 | (payload[i + 3] << 8) | 112 | (payload[i + 2] << 16) | 113 | (payload[i + 1] << 24) 114 | ); 115 | 116 | /* NTP time */ 117 | pk[idx].ts_msw = ( 118 | (payload[i + 8]) | 119 | (payload[i + 7] << 8) | 120 | (payload[i + 6] << 16) | 121 | (payload[i + 5] << 24) 122 | ); 123 | 124 | pk[idx].ts_lsw = ( 125 | (payload[i + 12]) | 126 | (payload[i + 11] << 8) | 127 | (payload[i + 10] << 16) | 128 | (payload[i + 9] << 24) 129 | ); 130 | 131 | /* RTP timestamp */ 132 | pk[idx].ts_rtp = ( 133 | (payload[i + 16]) | 134 | (payload[i + 15] << 8) | 135 | (payload[i + 14] << 16) | 136 | (payload[i + 13] << 24) 137 | ); 138 | 139 | pk[idx].sd_pk_c = ( 140 | (payload[i + 20]) | 141 | (payload[i + 19] << 8) | 142 | (payload[i + 18] << 16) | 143 | (payload[i + 17] << 24) 144 | ); 145 | 146 | pk[idx].sd_pk_c = ( 147 | (payload[i + 24]) | 148 | (payload[i + 23] << 8) | 149 | (payload[i + 22] << 16) | 150 | (payload[i + 21] << 24) 151 | ); 152 | i += 24; 153 | 154 | /* last SR */ 155 | rtp_st.last_rcv_SR_ts = ((pk[idx].ts_msw & 0xFFFF) << 16) | 156 | (pk[idx].ts_lsw >> 16); 157 | 158 | rtp_st.last_rcv_SR_time.tv_sec = now.tv_sec; 159 | rtp_st.last_rcv_SR_time.tv_usec = now.tv_usec; 160 | 161 | rtp_st.rtp_identifier = pk[idx].ssrc; 162 | 163 | if (debug_rtcp) { 164 | printf(" SSRC : 0x%x (%u)\n", 165 | pk[idx].ssrc, pk[idx].ssrc); 166 | printf(" TS MSW : 0x%x (%u)\n", 167 | pk[idx].ts_msw, pk[idx].ts_msw); 168 | printf(" TS LSW : 0x%x (%u)\n", 169 | pk[idx].ts_lsw, pk[idx].ts_lsw); 170 | printf(" TS RTP : 0x%x (%u)\n", 171 | pk[idx].ts_rtp, pk[idx].ts_rtp); 172 | printf(" SD PK CT : %u\n", pk[idx].sd_pk_c); 173 | printf(" SD OC CT : %u\n", pk[idx].sd_oc_c); 174 | } 175 | 176 | 177 | } 178 | /* source definition */ 179 | else if (pk[idx].type == RTCP_SDES) { 180 | pk[idx].identifier = ( 181 | (payload[i + 4]) | 182 | (payload[i + 3] << 8) | 183 | (payload[i + 2] << 16) | 184 | (payload[i + 1] << 24) 185 | ); 186 | i += 5; 187 | pk[idx].sdes_type = payload[i]; 188 | 189 | i++; 190 | pk[idx].sdes_length = payload[i]; 191 | 192 | /* we skip the source name, we dont need it */ 193 | i += pk[idx].sdes_length; 194 | 195 | /* end ? */ 196 | i++; 197 | pk[idx].sdes_type2 = payload[i]; 198 | 199 | i++; 200 | if (debug_rtcp) { 201 | printf(" ID : %u\n", pk[idx].identifier); 202 | printf(" Type : %i\n", pk[idx].sdes_type); 203 | printf(" Length : %i\n", pk[idx].sdes_length); 204 | printf(" Type 2 : %i\n", pk[idx].sdes_type2); 205 | } 206 | } 207 | 208 | if (debug_rtcp) { 209 | printf(" Len Check: "); 210 | if ( (i - start) / 4 != pk[idx].length) { 211 | printf("Error\n"); 212 | 213 | printf("i : %i\nstart : %i\n pk length: %i\n", 214 | i, start, pk[idx].length); 215 | exit(1); 216 | } 217 | else { 218 | printf("OK\n"); 219 | } 220 | } 221 | /* Discard packet */ 222 | else { 223 | i += pk[idx].length; 224 | } 225 | i++; 226 | idx++; 227 | } 228 | 229 | *count = idx; 230 | return pk; 231 | } 232 | 233 | 234 | /* create a receiver report package */ 235 | int rtcp_receiver_report_zero(int fd) 236 | { 237 | uint8_t tmp_8; 238 | uint16_t tmp_16; 239 | uint32_t tmp_32; 240 | 241 | /* RTCP: version, padding, report count = 0; int = 128 ; hex = 0x80 */ 242 | tmp_8 = 0x80; 243 | send(fd, &tmp_8, 1, 0); 244 | 245 | /* RTCP: packet type - receiver report */ 246 | tmp_8 = RTCP_RR; 247 | send(fd, &tmp_8, 1, 0); 248 | 249 | /* RTCP: length */ 250 | tmp_16 = 0x01; 251 | net_send16(fd, tmp_16); 252 | 253 | /* RTCP: sender SSRC */ 254 | tmp_32 = RTCP_SSRC; 255 | net_send32(fd, tmp_32); 256 | 257 | return 0; 258 | } 259 | 260 | /* create a receiver report package */ 261 | int rtcp_receiver_report(int fd) 262 | { 263 | uint8_t tmp_8; 264 | uint16_t tmp_16; 265 | uint32_t tmp_32; 266 | struct timeval now; 267 | gettimeofday(&now, NULL); 268 | 269 | /* RTCP: version, padding, report count; int = 129 ; hex = 0x81 */ 270 | tmp_8 = 0x81; 271 | send(fd, &tmp_8, 1, 0); 272 | 273 | /* RTCP: packet type - receiver report */ 274 | tmp_8 = RTCP_RR; 275 | send(fd, &tmp_8, 1, 0); 276 | 277 | /* RTCP: length */ 278 | tmp_16 = 0x07; 279 | net_send16(fd, tmp_16); 280 | 281 | /* RTCP: sender SSRC */ 282 | tmp_32 = RTCP_SSRC; 283 | net_send32(fd, tmp_32); 284 | 285 | /* RTCP: Source 1: Identifier */ 286 | net_send32(fd, rtp_st.rtp_identifier); 287 | 288 | /* 289 | * Calcs for expected and lost packets 290 | */ 291 | uint32_t extended_max; 292 | uint32_t expected; 293 | extended_max = rtp_st.rtp_received + rtp_st.highest_seq; 294 | expected = extended_max - rtp_st.first_seq + 1; 295 | rtp_st.rtp_cum_lost = expected - rtp_st.rtp_received - 1; 296 | 297 | /* Fraction */ 298 | uint32_t expected_interval; 299 | uint32_t received_interval; 300 | uint32_t lost_interval; 301 | uint8_t fraction; 302 | 303 | expected_interval = expected - rtp_st.rtp_expected_prior; 304 | rtp_st.rtp_expected_prior = expected; 305 | 306 | received_interval = rtp_st.rtp_received - rtp_st.rtp_received_prior; 307 | rtp_st.rtp_received_prior = rtp_st.rtp_received; 308 | lost_interval = expected_interval - received_interval; 309 | if (expected_interval == 0 || lost_interval <= 0) fraction = 0; 310 | else fraction = (lost_interval << 8) / expected_interval; 311 | 312 | /* RTCP: SSRC Contents: Fraction lost */ 313 | //send(fd, &fraction, sizeof(fraction), 0); 314 | 315 | tmp_32 = 0;//(fraction << 24) | ((rtp_st.rtp_cum_lost ) & 0xFFFFFF); 316 | net_send32(fd, tmp_32); 317 | /* RTCP: SSRC Contents: Cumulative packet losts */ 318 | //net_send24(fd, rtp_st.rtp_cum_lost); 319 | 320 | /* RTCP: SSRC Contents: Extended highest sequence */ 321 | tmp_16 = 1;// FIXME!??? rtp_st.rtp_received; 322 | net_send16(fd, tmp_16); 323 | tmp_16 = rtp_st.highest_seq; 324 | net_send16(fd, tmp_16); 325 | 326 | /* RTCP: SSRC Contents: interarrival jitter */ 327 | tmp_32 = rtcp_jitter(); 328 | net_send32(fd, tmp_32); 329 | rtp_stats_print(); 330 | 331 | /* RTCP: SSRC Contents: Last SR timestamp */ 332 | tmp_32 = rtp_st.last_rcv_SR_ts; 333 | net_send32(fd, tmp_32); 334 | 335 | /* RTCP: SSRC Contents: Timestamp delay */ 336 | uint32_t dlsr = rtcp_dlsr(); 337 | net_send32(fd, dlsr); 338 | 339 | return 0; 340 | } 341 | 342 | int rtcp_receiver_desc(int fd) 343 | { 344 | uint8_t tmp_8; 345 | uint16_t tmp_16; 346 | uint32_t tmp_32; 347 | 348 | /* RTCP: version, padding, report count; int = 129 ; hex = 0x81 */ 349 | tmp_8 = 0x81; 350 | send(fd, &tmp_8, 1, 0); 351 | 352 | /* RTCP: packet type - source description */ 353 | tmp_8 = RTCP_SDES; 354 | send(fd, &tmp_8, 1, 0); 355 | 356 | /* RTCP: length */ 357 | tmp_16 = 0x04; /* 11 bytes */ 358 | net_send16(fd, tmp_16); 359 | 360 | /* RTCP: Source 1: Identifier */ 361 | tmp_32 = RTCP_SSRC; 362 | net_send32(fd, tmp_32); 363 | 364 | /* RTCP: SDES: Type CNAME = 1 */ 365 | tmp_8 = 0x1; 366 | send(fd, &tmp_8, 1, 0); 367 | 368 | /* RTCP: SDES: Length */ 369 | tmp_8 = 0x6; 370 | send(fd, &tmp_8, 1, 0); 371 | 372 | /* RTCP: SDES: Text (name string) */ 373 | send(fd, "monkey", 6, 0); 374 | 375 | /* RTCP: SDES: END */ 376 | tmp_8 = 0x0; 377 | send(fd, &tmp_8, 1, 0); 378 | 379 | return 0; 380 | } 381 | 382 | void *_rtcp_worker(void *arg) 383 | { 384 | int fd = (int) arg; 385 | 386 | return; 387 | //sleep(1); 388 | while (1) { 389 | rtsp_rtcp_reports(fd); 390 | sleep(5); 391 | } 392 | } 393 | 394 | int rtcp_worker(int fd) 395 | { 396 | pthread_t tid; 397 | pthread_attr_t attr; 398 | 399 | pthread_attr_init(&attr); 400 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 401 | if (pthread_create(&tid, &attr, _rtcp_worker, 402 | (void *) fd) != 0) { 403 | printf("Could not launch RTCP thread\n"); 404 | exit(1); 405 | } 406 | 407 | return 0; 408 | } 409 | -------------------------------------------------------------------------------- /rtcp.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #ifndef RTCP_H 6 | #define RTCP_H 7 | 8 | /* Payload types */ 9 | #define RTCP_SR 200 /* sender report */ 10 | #define RTCP_RR 201 /* receiver report */ 11 | #define RTCP_SDES 202 /* source description */ 12 | #define RTCP_BYE 203 /* good bye */ 13 | #define RTCP_APP 204 /* application defined */ 14 | 15 | /* Identification */ 16 | #define RTCP_SSRC 0x0c143e07 17 | 18 | struct rtcp_pkg { 19 | /* packet header */ 20 | uint8_t version; 21 | uint8_t padding; 22 | uint8_t extension; 23 | uint8_t ccrc; 24 | uint8_t type; 25 | uint16_t length; 26 | 27 | /* server report */ 28 | uint32_t ssrc; 29 | uint32_t ts_msw; 30 | uint32_t ts_lsw; 31 | uint32_t ts_rtp; 32 | uint32_t sd_pk_c; 33 | uint32_t sd_oc_c; 34 | 35 | /* source definition */ 36 | uint32_t identifier; 37 | uint8_t sdes_type; 38 | uint8_t sdes_length; 39 | uint16_t sdes_text; 40 | uint8_t sdes_type2; 41 | 42 | /* internal / informational */ 43 | }; 44 | 45 | int debug_rtcp; 46 | 47 | uint32_t rtcp_dlsr(); 48 | struct rtcp_pkg *rtcp_decode(unsigned char *payload, 49 | unsigned long len, int *count); 50 | int rtcp_receiver_report(int fd); 51 | int rtcp_receiver_report_zero(int fd); 52 | int rtcp_receiver_desc(int fd); 53 | int rtcp_worker(int fd); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /rtp.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTSP Client 4 | * ----------- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | /* generic headers */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* local headers */ 17 | #include "buffer.h" 18 | #include "rtsp.h" 19 | #include "rtcp.h" 20 | #include "rtp.h" 21 | #include "network.h" 22 | #include "utils.h" 23 | #include "streamer.h" 24 | 25 | int rtp_connect(char *stream) 26 | { 27 | char *host; 28 | char *sep; 29 | int len; 30 | int offset = sizeof(PROTOCOL_PREFIX) - 1; 31 | 32 | /* Lookup the host address */ 33 | if (!(sep = strchr(stream + offset, ':'))) { 34 | sep = strchr(stream + offset, '/'); 35 | } 36 | 37 | if (!sep) { 38 | printf("Error: Invalid stream address '%s'", stream); 39 | exit(EXIT_FAILURE); 40 | } 41 | len = (sep - stream) - offset; 42 | host = malloc(len + 1); 43 | strncpy(host, stream + offset, len); 44 | host[len] = '\0'; 45 | 46 | RTP_INFO("Connecting to host '%s' port %i...\n", host, 0); 47 | 48 | return net_udp_connect(host, 0); 49 | } 50 | 51 | unsigned int rtp_parse(unsigned char *raw, unsigned int size) 52 | { 53 | unsigned int raw_offset = 0; 54 | unsigned int rtp_length = size; 55 | unsigned int paysize; 56 | unsigned char payload[MAX_BUF_SIZE]; 57 | struct rtp_header rtp_h; 58 | 59 | rtp_h.version = raw[raw_offset] >> 6; 60 | rtp_h.padding = CHECK_BIT(raw[raw_offset], 5); 61 | rtp_h.extension = CHECK_BIT(raw[raw_offset], 4); 62 | rtp_h.cc = raw[raw_offset] & 0xFF; 63 | 64 | /* next byte */ 65 | raw_offset++; 66 | 67 | rtp_h.marker = CHECK_BIT(raw[raw_offset], 8); 68 | rtp_h.pt = raw[raw_offset] & 0x7f; 69 | 70 | /* next byte */ 71 | raw_offset++; 72 | 73 | /* Sequence number */ 74 | rtp_h.seq = raw[raw_offset] * 256 + raw[raw_offset + 1]; 75 | raw_offset += 2; 76 | 77 | /* time stamp */ 78 | rtp_h.ts = \ 79 | (raw[raw_offset ] << 24) | 80 | (raw[raw_offset + 1] << 16) | 81 | (raw[raw_offset + 2] << 8) | 82 | (raw[raw_offset + 3]); 83 | raw_offset += 4; 84 | 85 | /* ssrc / source identifier */ 86 | rtp_h.ssrc = \ 87 | (raw[raw_offset ] << 24) | 88 | (raw[raw_offset + 1] << 16) | 89 | (raw[raw_offset + 2] << 8) | 90 | (raw[raw_offset + 3]); 91 | raw_offset += 4; 92 | rtp_st.rtp_identifier = rtp_h.ssrc; 93 | 94 | /* Payload size */ 95 | paysize = (rtp_length - raw_offset); 96 | 97 | memset(payload, '\0', sizeof(payload)); 98 | memcpy(&payload, raw + raw_offset, paysize); 99 | 100 | /* 101 | * A new RTP packet has arrived, we need to pass the rtp_h struct 102 | * to the stats/context updater 103 | */ 104 | rtp_stats_update(&rtp_h); 105 | 106 | /* Display RTP header info */ 107 | printf(" >> RTP\n"); 108 | printf(" Version : %i\n", rtp_h.version); 109 | printf(" Padding : %i\n", rtp_h.padding); 110 | printf(" Extension : %i\n", rtp_h.extension); 111 | printf(" CSRC Count : %i\n", rtp_h.cc); 112 | printf(" Marker : %i\n", rtp_h.marker); 113 | printf(" Payload Type: %i\n", rtp_h.pt); 114 | printf(" Sequence : %i\n", rtp_h.seq); 115 | printf(" Timestamp : %u\n", rtp_h.ts); 116 | printf(" Sync Source : %u\n", rtp_h.ssrc); 117 | 118 | /* 119 | * NAL, first byte header 120 | * 121 | * +---------------+ 122 | * |0|1|2|3|4|5|6|7| 123 | * +-+-+-+-+-+-+-+-+ 124 | * |F|NRI| Type | 125 | * +---------------+ 126 | */ 127 | int nal_forbidden_zero = CHECK_BIT(payload[0], 7); 128 | int nal_nri = (payload[0] & 0x60) >> 5; 129 | int nal_type = (payload[0] & 0x1F); 130 | 131 | printf(" >> NAL\n"); 132 | printf(" Forbidden zero: %i\n", nal_forbidden_zero); 133 | printf(" NRI : %i\n", nal_nri); 134 | printf(" Type : %i\n", nal_type); 135 | 136 | /* Single NAL unit packet */ 137 | if (nal_type >= NAL_TYPE_SINGLE_NAL_MIN && 138 | nal_type <= NAL_TYPE_SINGLE_NAL_MAX) { 139 | 140 | /* Write NAL header */ 141 | streamer_write_nal(); 142 | 143 | /* Write NAL unit */ 144 | streamer_write(payload, sizeof(paysize)); 145 | } 146 | 147 | /* 148 | * Agregation packet - STAP-A 149 | * ------ 150 | * http://www.ietf.org/rfc/rfc3984.txt 151 | * 152 | * 0 1 2 3 153 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 154 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 155 | * | RTP Header | 156 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 157 | * |STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR | 158 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 159 | * | NALU 1 Data | 160 | * : : 161 | * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 162 | * | | NALU 2 Size | NALU 2 HDR | 163 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 164 | * | NALU 2 Data | 165 | * : : 166 | * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 167 | * | :...OPTIONAL RTP padding | 168 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 169 | */ 170 | else if (nal_type == NAL_TYPE_STAP_A) { 171 | uint8_t *q; 172 | uint16_t nalu_size; 173 | 174 | q = payload + 1; 175 | int nidx = 0; 176 | 177 | nidx = 0; 178 | while (nidx < paysize - 1) { 179 | /* write NAL header */ 180 | streamer_write_nal(); 181 | 182 | /* get NALU size */ 183 | nalu_size = (q[nidx] << 8) | (q[nidx + 1]); 184 | printf("nidx = %i ; NAL size = %i ; RAW offset = %i\n", 185 | nidx, nalu_size, raw_offset); 186 | nidx += 2; 187 | 188 | /* write NALU size */ 189 | streamer_write(&nalu_size, 1); 190 | 191 | if (nalu_size == 0) { 192 | nidx++; 193 | continue; 194 | } 195 | 196 | /* write NALU data */ 197 | streamer_write(q + nidx, nalu_size); 198 | nidx += nalu_size; 199 | } 200 | } 201 | else if (nal_type == NAL_TYPE_FU_A) { 202 | printf(" >> Fragmentation Unit\n"); 203 | 204 | uint8_t *q; 205 | q = payload; 206 | 207 | uint8_t h264_start_bit = q[1] & 0x80; 208 | uint8_t h264_end_bit = q[1] & 0x40; 209 | uint8_t h264_type = q[1] & 0x1F; 210 | uint8_t h264_nri = (q[0] & 0x60) >> 5; 211 | uint8_t h264_key = (h264_nri << 5) | h264_type; 212 | 213 | if (h264_start_bit) { 214 | /* write NAL header */ 215 | streamer_write_nal(); 216 | 217 | /* write NAL unit code */ 218 | streamer_write(&h264_key, sizeof(h264_key)); 219 | } 220 | streamer_write(q + 2, paysize - 2); 221 | 222 | if (h264_end_bit) { 223 | /* nothing to do... */ 224 | } 225 | } 226 | else if (nal_type == NAL_TYPE_UNDEFINED) { 227 | 228 | } 229 | else { 230 | printf("OTHER NAL!: %i\n", nal_type); 231 | raw_offset++; 232 | 233 | } 234 | raw_offset += paysize; 235 | 236 | if (rtp_h.seq > rtp_st.highest_seq) { 237 | rtp_st.highest_seq = rtp_h.seq; 238 | } 239 | 240 | rtp_stats_print(); 241 | return raw_offset; 242 | } 243 | 244 | void rtp_rtp2tval(unsigned int ts, struct timeval *tv) 245 | { 246 | tv->tv_sec = (ts * RTP_FREQ); 247 | tv->tv_usec = ((((ts % RTP_FREQ) / (ts / 8000))) * 125); 248 | } 249 | 250 | uint64_t rtp_timeval_to_ntp(const struct timeval *tv) 251 | { 252 | uint64_t msw; 253 | uint64_t lsw; 254 | 255 | /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */ 256 | msw = tv->tv_sec + 0x83AA7E80; 257 | lsw = (uint32_t)((double)tv->tv_usec*(double)(((uint64_t)1)<<32)*1.0e-6); 258 | 259 | return ((msw << 32) | lsw); 260 | } 261 | 262 | /* 263 | uint32_t rtp_now() 264 | { 265 | struct timeval tmp; 266 | 267 | gettimeofday(&tmp, NULL); 268 | tmp.tv_sec -= rtp_st.ts_delta.tv_sec; 269 | tmp.tv_usec -= rtp_st.ts_delta.tv_usec; 270 | 271 | return rtp_tval2RTP(tmp); 272 | return rtp_tval2rtp(tmp.tv_sec, tmp.tv_usec); 273 | } 274 | */ 275 | void rtp_stats_reset() 276 | { 277 | memset(&rtp_st, '\0', sizeof(struct rtp_stats)); 278 | } 279 | 280 | /* Every time a RTP packet arrive, update the stats */ 281 | void rtp_stats_update(struct rtp_header *rtp_h) 282 | { 283 | uint32_t transit; 284 | int delta; 285 | struct timeval now; 286 | 287 | gettimeofday(&now, NULL); 288 | rtp_st.rtp_received++; 289 | 290 | /* Highest sequence */ 291 | if (rtp_h->seq > rtp_st.highest_seq) { 292 | rtp_st.highest_seq = rtp_h->seq; 293 | } 294 | 295 | 296 | /* Update RTP timestamp */ 297 | if (rtp_st.last_rcv_time.tv_sec == 0) { 298 | //rtp_st.rtp_ts = rtp_h->ts; 299 | rtp_st.first_seq = rtp_h->seq; 300 | //rtp_st.jitter = 0; 301 | //rtp_st.last_dlsr = 0; 302 | //rtp_st.rtp_cum_lost = 0; 303 | gettimeofday(&rtp_st.last_rcv_time, NULL); 304 | 305 | /* deltas 306 | int sec = (rtp_h->ts / RTP_FREQ); 307 | int usec = (((rtp_h->ts % RTP_FREQ) / (RTP_FREQ / 8000))) * 125; 308 | rtp_st.ts_delta.tv_sec = now.tv_sec - sec; 309 | rtp_st.ts_delta.tv_usec = now.tv_usec - usec; 310 | 311 | 312 | rtp_st.last_arrival = rtp_tval2rtp(rtp_st.ts_delta.tv_sec, 313 | rtp_st.ts_delta.tv_usec); 314 | rtp_st.last_arrival = rtp_tval2RTP(now); 315 | 316 | } 317 | else {*/ 318 | } 319 | /* Jitter */ 320 | transit = rtp_st.delay_snc_last_SR; 321 | //printf("TRANSIT!: %i\n", transit); exit(1); 322 | delta = transit - rtp_st.transit; 323 | rtp_st.transit = transit; 324 | if (delta < 0) { 325 | delta = -delta; 326 | } 327 | //printf("now = %i ; rtp = %i ; delta = %i\n", 328 | // t, rtp_h->ts, delta); 329 | //rtp_st.jitter += delta - ((rtp_st.jitter + 8) >> 4); 330 | rtp_st.jitter += ((1.0/16.0) * ((double) delta - rtp_st.jitter)); 331 | 332 | rtp_st.rtp_ts = rtp_h->ts; 333 | //} 334 | 335 | /* print the new stats */ 336 | rtp_stats_print(); 337 | } 338 | 339 | void rtp_stats_print() 340 | { 341 | 342 | printf(">> RTP Stats\n"); 343 | printf(" First Sequence : %u\n", rtp_st.first_seq); 344 | printf(" Highest Sequence: %u\n", rtp_st.highest_seq); 345 | printf(" RTP Received : %u\n", rtp_st.rtp_received); 346 | printf(" RTP Identifier : %u\n", rtp_st.rtp_identifier); 347 | printf(" RTP Timestamp : %u\n", rtp_st.rtp_ts); 348 | printf(" Jitter : %u\n", rtp_st.jitter); 349 | printf(" Last DLSR : %i\n", rtp_st.last_dlsr); 350 | } 351 | -------------------------------------------------------------------------------- /rtp.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTSP Client 4 | * ----------- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | #include 9 | 10 | #ifndef RTP_H 11 | #define RTP_H 12 | 13 | /* 14 | * Struct taken from RFC 3550 15 | * -------------------------- 16 | * http://www.ietf.org/rfc/rfc3550.txt 17 | */ 18 | 19 | struct rtp_header { 20 | unsigned int version:2; /* protocol version */ 21 | unsigned int padding:1; /* padding flag */ 22 | unsigned int extension:1; /* header extension flag */ 23 | unsigned int cc:4; /* CSRC count */ 24 | unsigned int marker:1; /* marker bit */ 25 | unsigned int pt:7; /* payload type */ 26 | uint16_t seq:16; /* sequence number */ 27 | uint32_t ts; /* timestamp */ 28 | uint32_t ssrc; /* synchronization source */ 29 | uint32_t csrc[1]; /* optional CSRC list */ 30 | }; 31 | 32 | struct rtp_stats { 33 | uint16_t first_seq; /* first sequence */ 34 | uint16_t highest_seq; /* highest sequence */ 35 | uint16_t rtp_received; /* RTP sequence number received */ 36 | uint32_t rtp_identifier; /* source identifier */ 37 | uint32_t rtp_ts; /* RTP timestamp */ 38 | uint32_t rtp_cum_lost; /* RTP cumulative packet lost */ 39 | uint32_t rtp_expected_prior;/* RTP expected prior */ 40 | uint32_t rtp_received_prior;/* RTP received prior */ 41 | uint32_t transit; /* Transit time. RFC3550 A.8 */ 42 | uint32_t jitter; /* Jitter */ 43 | uint32_t lst; 44 | uint32_t last_dlsr; /* Last DLSR */ 45 | uint32_t last_rcv_SR_ts; /* Last arrival in RTP format */ 46 | uint32_t delay_snc_last_SR; /* Delay sinde last SR */ 47 | struct timeval 48 | last_rcv_SR_time; /* Last SR arrival */ 49 | struct timeval 50 | last_rcv_time; 51 | double rtt_frac; 52 | } rtp_st; 53 | 54 | #define RTP_FREQ 90000 55 | #define RTP_SPROP "sprop-parameter-sets=" 56 | 57 | /* Enumeration of H.264 NAL unit types */ 58 | enum { 59 | NAL_TYPE_UNDEFINED = 0, 60 | NAL_TYPE_SINGLE_NAL_MIN = 1, 61 | NAL_TYPE_SINGLE_NAL_MAX = 23, 62 | NAL_TYPE_STAP_A = 24, 63 | NAL_TYPE_FU_A = 28, 64 | }; 65 | 66 | uint64_t rtp_timeval_to_ntp(const struct timeval *tv); 67 | void rtp_stats_update(struct rtp_header *rtp_h); 68 | void rtp_stats_reset(); 69 | void rtp_stats_print(); 70 | unsigned int rtp_parse(unsigned char *raw, unsigned int size); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /rtsp.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTSP Client 4 | * ----------- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | #define _GNU_SOURCE 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /* networking I/O */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /* local headers */ 31 | #include "network.h" 32 | #include "streamer.h" 33 | #include "base64.h" 34 | #include "rtsp.h" 35 | #include "utils.h" 36 | #include "rtcp.h" 37 | #include "rtp.h" 38 | #include "h264dec.h" 39 | 40 | void rtsp_cseq_inc() 41 | { 42 | rtsp_cseq++; 43 | } 44 | 45 | void rtsp_header(int fd, int channel, uint16_t length) 46 | { 47 | uint8_t tmp_8; 48 | uint16_t tmp_16; 49 | 50 | tmp_8 = 0x24; /* RTSP magic number */ 51 | send(fd, &tmp_8, 1, 0); 52 | 53 | tmp_8 = channel; /* channel */ 54 | send(fd, &tmp_8, 1, 0); 55 | 56 | tmp_16 = length; 57 | net_send16(fd, tmp_16); 58 | } 59 | 60 | void rtsp_rtcp_reports(int fd) 61 | { 62 | // net_sock_cork(fd, 1); 63 | rtsp_header(fd, 1, 74); 64 | 65 | /* report 1 */ 66 | rtcp_receiver_report(fd); /* 32 bytes */ 67 | rtcp_receiver_desc(fd); /* 17 bytes */ 68 | 69 | usleep(200); 70 | /* report 0 */ 71 | //rtcp_receiver_report_zero(fd); /* 8 bytes */ 72 | //rtcp_receiver_desc(fd); /* 17 bytes */ 73 | 74 | //net_sock_cork(fd, 0); 75 | 76 | printf("================RTCP SENT!=================\n"); 77 | } 78 | 79 | 80 | 81 | /* 82 | * Returns the RTSP status code from the response, if an error occurred it 83 | * allocates a memory buffer and store the error message on 'error' variable 84 | */ 85 | int rtsp_response_status(char *response, char **error) 86 | { 87 | int size = 256; 88 | int err_size; 89 | int offset = sizeof(RTSP_RESPONSE) - 1; 90 | char buf[8]; 91 | char *sep; 92 | char *eol; 93 | *error = NULL; 94 | 95 | if (strncmp(response, RTSP_RESPONSE, offset) != 0) { 96 | *error = malloc(size); 97 | snprintf(*error, size, "Invalid RTSP response format"); 98 | return -1; 99 | } 100 | 101 | sep = strchr(response + offset, ' '); 102 | if (!sep) { 103 | *error = malloc(size); 104 | snprintf(*error, size, "Invalid RTSP response format"); 105 | return -1; 106 | } 107 | 108 | memset(buf, '\0', sizeof(buf)); 109 | strncpy(buf, response + offset, sep - response - offset); 110 | 111 | eol = strchr(response, '\r'); 112 | err_size = (eol - response) - offset - 1 - strlen(buf); 113 | *error = malloc(err_size + 1); 114 | strncpy(*error, response + offset + 1 + strlen(buf), err_size); 115 | 116 | return atoi(buf); 117 | } 118 | 119 | int rtsp_cmd_options(int sock, char *stream) 120 | { 121 | int n; 122 | int ret = 0; 123 | int status; 124 | int size = 4096; 125 | char *err; 126 | char buf[size]; 127 | 128 | RTSP_INFO("OPTIONS: command\n"); 129 | 130 | memset(buf, '\0', sizeof(buf)); 131 | n = snprintf(buf, size, CMD_OPTIONS, stream_host, stream_port, rtsp_cseq); 132 | DEBUG_REQ(buf); 133 | n = send(sock, buf, n, 0); 134 | 135 | RTSP_INFO("OPTIONS: request sent\n"); 136 | 137 | memset(buf, '\0', sizeof(buf)); 138 | n = recv(sock, buf, size - 1, 0); 139 | if (n <= 0) { 140 | printf("Error: Server did not respond properly, closing..."); 141 | close(sock); 142 | exit(EXIT_FAILURE); 143 | } 144 | 145 | status = rtsp_response_status(buf, &err); 146 | if (status == 200) { 147 | RTSP_INFO("OPTIONS: response status %i (%i bytes)\n", status, n); 148 | } 149 | else { 150 | RTSP_INFO("OPTIONS: response status %i: %s\n", status, err); 151 | ret = -1; 152 | } 153 | 154 | DEBUG_RES(buf); 155 | rtsp_cseq_inc(); 156 | 157 | return ret; 158 | } 159 | 160 | int rtsp_cmd_describe(int sock, char *stream, char **sprop) 161 | { 162 | int n; 163 | int ret = 0; 164 | int status; 165 | int size = 4096; 166 | char *p, *end; 167 | char *err; 168 | char buf[size]; 169 | 170 | RTSP_INFO("DESCRIBE: command\n"); 171 | 172 | memset(buf, '\0', sizeof(buf)); 173 | n = snprintf(buf, size, CMD_DESCRIBE, stream, rtsp_cseq); 174 | DEBUG_REQ(buf); 175 | n = send(sock, buf, n, 0); 176 | 177 | RTSP_INFO("DESCRIBE: request sent\n"); 178 | 179 | memset(buf, '\0', sizeof(buf)); 180 | n = recv(sock, buf, size - 1, 0); 181 | if (n <= 0) { 182 | printf("Error: Server did not respond properly, closing..."); 183 | close(sock); 184 | exit(EXIT_FAILURE); 185 | } 186 | 187 | status = rtsp_response_status(buf, &err); 188 | if (status == 200) { 189 | RTSP_INFO("DESCRIBE: response status %i (%i bytes)\n", status, n); 190 | } 191 | else { 192 | RTSP_INFO("DESCRIBE: response status %i: %s\n", status, err); 193 | ret = -1; 194 | } 195 | 196 | DEBUG_RES("%s\n", buf); 197 | rtsp_cseq_inc(); 198 | 199 | /* set the DSP information */ 200 | p = strstr(buf, "\r\n\r\n"); 201 | if (!p) { 202 | return -1; 203 | } 204 | 205 | /* Create buffer for DSP */ 206 | dsp = malloc(n + 1); 207 | memset(dsp, '\0', n + 1); 208 | strcpy(dsp, p + 4); 209 | 210 | /* sprop-parameter-sets key */ 211 | p = strstr(dsp, RTP_SPROP); 212 | if (!p) { 213 | return -1; 214 | } 215 | 216 | end = strchr(p, '\r'); 217 | if (!end) { 218 | return -1; 219 | } 220 | 221 | int prop_size = (end - p) - sizeof(RTP_SPROP) + 1; 222 | *sprop = malloc(prop_size + 1); 223 | memcpy(*sprop, p + sizeof(RTP_SPROP) - 1, prop_size); 224 | 225 | return ret; 226 | } 227 | 228 | int rtsp_cmd_setup(int sock, char *stream, struct rtsp_session *session) 229 | { 230 | int n; 231 | int ret = 0; 232 | int status; 233 | int field_size = 16; 234 | int size = 4096; 235 | int client_port_from = -1; 236 | int client_port_to = -1; 237 | int server_port_from = -1; 238 | int server_port_to = -1; 239 | unsigned long session_id; 240 | char *p; 241 | char *sep; 242 | char *err; 243 | char buf[size]; 244 | char field[field_size]; 245 | 246 | RTSP_INFO("SETUP: command\n"); 247 | 248 | memset(buf, '\0', sizeof(buf)); 249 | //n = snprintf(buf, size, CMD_SETUP, stream, rtsp_cseq, client_port, client_port + 1); 250 | n = snprintf(buf, size, CMD_SETUP, stream, rtsp_cseq); 251 | 252 | DEBUG_REQ(buf); 253 | n = send(sock, buf, n, 0); 254 | 255 | RTSP_INFO("SETUP: request sent\n"); 256 | 257 | memset(buf, '\0', sizeof(buf)); 258 | n = recv(sock, buf, size - 1, 0); 259 | if (n <= 0) { 260 | printf("Error: Server did not respond properly, closing..."); 261 | close(sock); 262 | exit(EXIT_FAILURE); 263 | } 264 | 265 | status = rtsp_response_status(buf, &err); 266 | if (status == 200) { 267 | RTSP_INFO("SETUP: response status %i (%i bytes)\n", status, n); 268 | 269 | /* Fill session data */ 270 | p = strstr(buf, "Transport: "); 271 | if (!p) { 272 | RTSP_INFO("SETUP: Error, Transport header not found\n"); 273 | DEBUG_RES(buf); 274 | return -1; 275 | } 276 | 277 | /* 278 | * Commenting out this code, this part was written to support 279 | * RTP connection over UDP socket and determinate the server 280 | * ports to connect. 281 | * 282 | * By now the program is using TCP in Intervealed mode, so no 283 | * extra ports are required. 284 | 285 | buf_client_port = strstr(p, SETUP_TRNS_CLIENT); 286 | buf_server_port = strstr(p, SETUP_TRNS_SERVER); 287 | 288 | if (!buf_client_port || !buf_server_port) { 289 | RTSP_INFO("SETUP: Error, ports not defined in Transport header\n"); 290 | DEBUG_RES(buf); 291 | return -1; 292 | } 293 | 294 | client_port from 295 | sep = strchr(buf_client_port + sizeof(SETUP_TRNS_CLIENT) - 1, '-'); 296 | if (!sep) { 297 | RTSP_INFO("SETUP: client_port have an invalid format\n"); 298 | DEBUG_RES(buf); 299 | return -1; 300 | } 301 | 302 | memset(field, '\0', sizeof(field)); 303 | strncpy(field, 304 | buf_client_port + sizeof(SETUP_TRNS_CLIENT) - 1, 305 | sep - buf_client_port - sizeof(SETUP_TRNS_CLIENT) + 1); 306 | 307 | client_port_from = atoi(field); 308 | 309 | client_port to 310 | p = strchr(sep, ';'); 311 | if (!p) { 312 | p = strchr(sep, '\r'); 313 | if (!p) { 314 | RTSP_INFO("SETUP: client_port have an invalid format\n"); 315 | DEBUG_RES(buf); 316 | return -1; 317 | } 318 | } 319 | 320 | memset(field, '\0', sizeof(field)); 321 | strncpy(field, sep + 1, p - sep - 1); 322 | client_port_to = atoi(field); 323 | 324 | server_port from 325 | sep = strchr(buf_server_port + sizeof(SETUP_TRNS_SERVER) - 1, '-'); 326 | if (!sep) { 327 | RTSP_INFO("SETUP: server_port have an invalid format\n"); 328 | DEBUG_RES(buf); 329 | return -1; 330 | } 331 | 332 | 333 | memset(field, '\0', sizeof(field)); 334 | strncpy(field, 335 | buf_server_port + sizeof(SETUP_TRNS_SERVER) - 1, 336 | sep - buf_server_port - sizeof(SETUP_TRNS_SERVER) + 1); 337 | 338 | server_port_from = atoi(field); 339 | 340 | server_port to 341 | p = strchr(sep, ';'); 342 | if (!p) { 343 | p = strchr(sep, '\r'); 344 | if (!p) { 345 | RTSP_INFO("SETUP: server_port have an invalid format\n"); 346 | DEBUG_RES(buf); 347 | return -1; 348 | } 349 | } 350 | 351 | memset(field, '\0', sizeof(field)); 352 | strncpy(field, sep + 1, p - sep - 1); 353 | server_port_to = atoi(field); 354 | */ 355 | 356 | /* Session ID */ 357 | p = strstr(buf, SETUP_SESSION); 358 | if (!p) { 359 | RTSP_INFO("SETUP: Session header not found\n"); 360 | DEBUG_RES(buf); 361 | return -1; 362 | } 363 | 364 | sep = strchr(p, '\r'); 365 | memset(field, '\0', sizeof(field)); 366 | strncpy(field, p + sizeof(SETUP_SESSION) - 1, sep - p - sizeof(SETUP_SESSION) + 1); 367 | session_id = atol(field); 368 | } 369 | else { 370 | RTSP_INFO("SETUP: response status %i: %s\n", status, err); 371 | DEBUG_RES(buf); 372 | return -1; 373 | } 374 | 375 | /* Fill session data */ 376 | session->packetization = 1; /* FIXME: project specific value */ 377 | session->cport_from = client_port_from; 378 | session->cport_to = client_port_to; 379 | session->sport_from = server_port_from; 380 | session->sport_to = server_port_to; 381 | session->session = session_id; 382 | 383 | DEBUG_RES(buf); 384 | rtsp_cseq_inc(); 385 | return ret; 386 | } 387 | 388 | int rtsp_cmd_play(int sock, char *stream, unsigned long session) 389 | { 390 | int n; 391 | int ret = 0; 392 | int status; 393 | int size = 4096; 394 | char *err; 395 | char buf[size]; 396 | 397 | RTSP_INFO("PLAY: command\n"); 398 | 399 | memset(buf, '\0', sizeof(buf)); 400 | n = snprintf(buf, size, CMD_PLAY, stream, rtsp_cseq, session); 401 | DEBUG_REQ(buf); 402 | n = send(sock, buf, n, 0); 403 | 404 | RTSP_INFO("PLAY: request sent\n"); 405 | 406 | memset(buf, '\0', sizeof(buf)); 407 | 408 | n = recv(sock, buf, size - 1, 0); 409 | if (n <= 0) { 410 | printf("Error: Server did not respond properly, closing..."); 411 | close(sock); 412 | exit(EXIT_FAILURE); 413 | } 414 | 415 | status = rtsp_response_status(buf, &err); 416 | if (status == 200) { 417 | RTSP_INFO("PLAY: response status %i (%i bytes)\n", status, n); 418 | } 419 | else { 420 | RTSP_INFO("PLAY: response status %i: %s\n", status, err); 421 | ret = -1; 422 | } 423 | 424 | DEBUG_RES(buf); 425 | rtsp_cseq_inc(); 426 | return ret; 427 | } 428 | 429 | 430 | int rtsp_connect(char *stream) 431 | { 432 | char *host; 433 | char *sep; 434 | char buf[8]; 435 | int len; 436 | int pos; 437 | int offset = sizeof(PROTOCOL_PREFIX) - 1; 438 | 439 | /* Lookup the host address */ 440 | if (!(sep = strchr(stream + offset, ':'))) { 441 | sep = strchr(stream + offset, '/'); 442 | } 443 | 444 | if (!sep) { 445 | printf("Error: Invalid stream address '%s'", stream); 446 | exit(EXIT_FAILURE); 447 | } 448 | len = (sep - stream) - offset; 449 | host = malloc(len + 1); 450 | strncpy(host, stream + offset, len); 451 | host[len] = '\0'; 452 | 453 | /* Lookup TCP port if specified */ 454 | sep = strchr(stream + offset + 1, ':'); 455 | if (sep) { 456 | pos = (sep - stream) + 1; 457 | sep = strchr(stream + pos, '/'); 458 | if (!sep) { 459 | printf("Error: Invalid stream address '%s'\n", stream); 460 | exit(EXIT_FAILURE); 461 | } 462 | 463 | len = (sep - stream) - pos; 464 | if (len > sizeof(buf) - 1) { 465 | printf("Error: Invalid TCP port in stream address '%s'\n", stream); 466 | exit(EXIT_FAILURE); 467 | } 468 | 469 | memset(buf, '\0', sizeof(buf)); 470 | strncpy(buf, stream + pos, len); 471 | stream_port = atol(buf); 472 | 473 | } 474 | 475 | stream_host = host; 476 | RTSP_INFO("Connecting to host '%s' port %lu...\n", stream_host, stream_port); 477 | return net_tcp_connect(stream_host, stream_port); 478 | } 479 | 480 | int rtsp_loop() 481 | { 482 | int fd; 483 | int ret; 484 | struct rtsp_session rtsp_s; 485 | pid_t streamer_worker; 486 | 487 | /* Connect to server */ 488 | fd = rtsp_connect(opt_stream); 489 | if (fd <= 0) { 490 | return -1; 491 | } 492 | 493 | ret = rtsp_cmd_options(fd, opt_stream); 494 | if (ret != 0) { 495 | return -1; 496 | } 497 | 498 | char *params; 499 | ret = rtsp_cmd_describe(fd, opt_stream, ¶ms); 500 | if (ret != 0) { 501 | printf("Error: Could not send DESCRIBE command to RTSP server\n"); 502 | return -1; 503 | } 504 | 505 | rtsp_s.socket = fd; 506 | rtsp_s.stream = strdup(opt_stream); 507 | 508 | rtsp_cmd_setup(fd, opt_stream, &rtsp_s); 509 | rtsp_cmd_play(fd, opt_stream, rtsp_s.session); 510 | 511 | rtp_stats_reset(); 512 | rtp_st.rtp_identifier = rtsp_s.session; 513 | //rtsp_rtcp_reports(fd); 514 | 515 | /* H264 Parameters, taken from the SDP output */ 516 | int p_size; 517 | char *sep; 518 | char *sps; 519 | char *pps; 520 | unsigned char *sps_dec; 521 | unsigned char *pps_dec; 522 | size_t sps_len; 523 | size_t pps_len; 524 | 525 | /* SPS */ 526 | sep = strchr(params, ','); 527 | p_size = (sep - params); 528 | sps = malloc(p_size + 1); 529 | memset(sps, '\0', p_size + 1); 530 | memcpy(sps, params, p_size); 531 | 532 | /* PPS */ 533 | p_size = (strlen(params) - p_size); 534 | pps = malloc(p_size + 1); 535 | memset(pps, '\0', p_size + 1); 536 | memcpy(pps, sep + 1, p_size); 537 | 538 | /* Decode each parameter */ 539 | sps_dec = base64_decode((const unsigned char *) sps, strlen(sps), &sps_len); 540 | pps_dec = base64_decode((const unsigned char *) pps, strlen(pps), &pps_len); 541 | 542 | free(sps); 543 | free(pps); 544 | 545 | int channel; 546 | int r; 547 | int max_buf_size = 1000000; 548 | 549 | unsigned char raw[max_buf_size]; 550 | unsigned char raw_tmp[max_buf_size]; 551 | unsigned int raw_length; 552 | unsigned int raw_offset = 0; 553 | unsigned int rtp_length; 554 | 555 | /* open debug file */ 556 | if (stream_dump) { 557 | stream_fs_fd = open(stream_dump, O_CREAT|O_WRONLY|O_TRUNC, 0666); 558 | } 559 | else { 560 | stream_fs_fd = -1; 561 | } 562 | 563 | /* write H264 header */ 564 | streamer_write_h264_header(sps_dec, sps_len, pps_dec, pps_len); 565 | 566 | /* Create unix named pipe */ 567 | stream_sock = streamer_prepare(opt_name, sps_dec, sps_len, pps_dec, pps_len); 568 | if (stream_sock <= 0) { 569 | printf("Error: could not create unix socket\n\n"); 570 | exit(EXIT_FAILURE); 571 | } 572 | 573 | /* Local pipe */ 574 | streamer_pipe_init(stream_pipe); 575 | streamer_worker = streamer_loop(stream_sock); 576 | 577 | printf("Streaming will start shortly...\n"); 578 | 579 | /* Set recv timeout for fd */ 580 | struct timeval tv; 581 | tv.tv_sec = 15; /* 30 Secs Timeout */ 582 | tv.tv_usec = 0; // Not init'ing this can cause strange errors 583 | setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); 584 | 585 | int send_rtcp = 0; 586 | rtcp_worker(fd); 587 | while (1) { 588 | raw_offset = 0; 589 | raw_length = 0; 590 | memset(raw, '\0', sizeof(raw)); 591 | 592 | read_pending: 593 | 594 | /* is buffer full ? */ 595 | if (raw_length >= max_buf_size) { 596 | printf(">> RESETTING\n"); 597 | 598 | if (raw_offset < raw_length - 1) { 599 | int bytes = raw_length - raw_offset; 600 | 601 | memset(raw_tmp, '\0', sizeof(raw_tmp)); 602 | memcpy(raw_tmp, raw + raw_offset, bytes); 603 | 604 | memset(raw, '\0', sizeof(raw)); 605 | memcpy(raw, raw_tmp, bytes); 606 | 607 | printf(" Move: %i\n", raw_length - raw_offset); 608 | raw_length = bytes; 609 | raw_offset = 0; 610 | if (raw[raw_offset] != 0x24) { 611 | printf("MASTER CORRUPTION\n"); 612 | } 613 | 614 | } 615 | else { 616 | raw_length = 0; 617 | memset(raw, '\0', sizeof(raw)); 618 | } 619 | } 620 | 621 | /* read incoming data */ 622 | printf(">> RECV: max %i bytes\n", max_buf_size - raw_length); 623 | r = recv(fd, raw + raw_length, max_buf_size - raw_length, 0); 624 | printf(">> READ: %i (up to %i)\n", r, max_buf_size - raw_length); 625 | fflush(stdout); 626 | 627 | if (r <= 0) { 628 | if (errno == EAGAIN) { 629 | //printf("Socket timeout, send out RTCP packets\n"); 630 | //goto read_pending; 631 | } 632 | 633 | rtp_stats_print(); 634 | printf(">> RTSP: Server closed connection!\n"); 635 | 636 | /* cleanup */ 637 | if (stream_fs_fd > 0) { 638 | close(stream_fs_fd); 639 | } 640 | 641 | close(fd); 642 | close(stream_sock); 643 | close(stream_pipe[0]); 644 | close(stream_pipe[1]); 645 | exit(1); 646 | return -1; 647 | } 648 | 649 | raw_length += r; 650 | 651 | /* parse all data in our buffer */ 652 | while (raw_offset < raw_length) { 653 | 654 | /* RTSP Interleaved header */ 655 | if (raw[raw_offset] == 0x24) { 656 | channel = raw[raw_offset + 1]; 657 | rtp_length = (raw[raw_offset + 2] << 8 | raw[raw_offset + 3]); 658 | 659 | printf(">> RTSP Interleaved (offset = %i/%i)\n", 660 | raw_offset, raw_length); 661 | printf(" Magic : 0x24\n"); 662 | printf(" Channel : %i\n", channel); 663 | printf(" Payload Length: %i\n", rtp_length); 664 | 665 | if (raw_length > max_buf_size) { 666 | printf("Error exception: raw_length = %i ; max_buf_size = %i\n", 667 | raw_length, max_buf_size); 668 | exit(EXIT_FAILURE); 669 | } 670 | 671 | /* RTSP header is 4 bytes, update the offset */ 672 | raw_offset += 4; 673 | 674 | /* If no payload exists, continue with next bytes */ 675 | if (rtp_length == 0) { 676 | raw_offset -= 4; 677 | goto read_pending; 678 | continue; 679 | } 680 | 681 | if (rtp_length > (raw_length - raw_offset)) { 682 | raw_offset -= 4; 683 | printf(" ** Pending : %u bytes\n", 684 | rtp_length - (raw_length - raw_offset)); 685 | //[CH %i] PENDING: RTP_LENGTH=%i ; RAW_LENGTH=%i; RAW_OFFSET=%i\n", 686 | // channel, rtp_length, raw_length, raw_offset); 687 | if (send_rtcp == 1){ 688 | rtsp_rtcp_reports(fd); 689 | send_rtcp = 0; 690 | } 691 | 692 | goto read_pending; 693 | } 694 | 695 | if (rtp_st.rtp_received % 20) { 696 | rtsp_rtcp_reports(fd); 697 | } 698 | 699 | /* RTCP data */ 700 | if (channel >= 1) { 701 | int idx; 702 | int rtcp_count; 703 | int size_RTCP_SR = 32; 704 | int size_RTCP_SDES = 17; 705 | int size_RTCP = 0; 706 | 707 | struct rtcp_pkg *rtcp; 708 | 709 | /* Decode RTCP packet(s) */ 710 | rtcp = rtcp_decode(raw + raw_offset, rtp_length, &rtcp_count); 711 | 712 | if (rtcp_count >= 1 && rtcp[0].type == RTCP_SR) { 713 | send_rtcp = 1; 714 | //rtsp_rtcp_reports(fd); 715 | } 716 | 717 | /* For each RTCP packet, send a reply 718 | for (idx = 0; idx < rtcp_count; idx++) { 719 | if (rtcp[idx].type == RTCP_SR) { 720 | size_RTCP += size_RTCP_SR; 721 | } 722 | else if (rtcp[idx].type == RTCP_SDES) { 723 | size_RTCP += size_RTCP_SDES; 724 | } 725 | } 726 | 727 | net_sock_cork(fd, 1); INVALID 728 | rtsp_header(fd, 1, size_RTCP); 729 | 730 | for (idx = 0; idx < rtcp_count; idx++) { 731 | sender report, send a receiver report 732 | if (rtcp[idx].type == RTCP_SR) { 733 | rtcp_receiver_report(fd); 734 | } 735 | else if (rtcp[idx].type == RTCP_SDES) { 736 | rtcp_receiver_desc(fd); 737 | } 738 | } 739 | net_sock_cork(fd, 0); INVALID 740 | */ 741 | raw_offset += rtp_length; 742 | free(rtcp); 743 | continue; 744 | } 745 | 746 | if (rtp_length == 0) { 747 | continue; 748 | } 749 | 750 | /* 751 | * Channel 0 752 | * --------- 753 | * If the channel is zero, the payload should contain RTP data, 754 | * we need to identify the RTP header fields so later we can 755 | * proceed to extract the H264 information. 756 | */ 757 | int offset; 758 | offset = rtp_parse(raw + raw_offset, rtp_length); 759 | if (offset <= 0) { 760 | raw_offset += rtp_length; 761 | } 762 | else { 763 | raw_offset += offset; 764 | } 765 | 766 | if (send_rtcp == 1) { 767 | //rtsp_rtcp_reports(fd); 768 | send_rtcp = 0; 769 | } 770 | 771 | continue; 772 | } 773 | raw_offset++; 774 | continue; 775 | } 776 | continue; 777 | } 778 | close(stream_fs_fd); 779 | 780 | return 0; 781 | } 782 | -------------------------------------------------------------------------------- /rtsp.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTSP Client 4 | * ----------- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | #include 9 | 10 | #ifndef RTSP_H 11 | #define RTSP_H 12 | 13 | /* 14 | * This struct represents a session established with the RTSP server 15 | * so the data contained is used for later PLAY commands 16 | */ 17 | struct rtsp_session { 18 | int socket; 19 | char *stream; 20 | 21 | /* RTSP protocol stuff */ 22 | unsigned int packetization; /* Packetization mode from SDP data */ 23 | unsigned int cport_from; /* client_port from */ 24 | unsigned int cport_to; /* client port to */ 25 | unsigned int sport_from; /* server port from */ 26 | unsigned int sport_to; /* server port to */ 27 | unsigned long session; /* session ID */ 28 | }; 29 | 30 | /* Last Sender Report timestamp (middle 32 bits) */ 31 | //uint32_t rtcp_last_sr_ts; 32 | 33 | #define VERSION "0.1" 34 | #define PROTOCOL_PREFIX "rtsp://" 35 | #define RTSP_PORT 554 36 | #define RTSP_CLIENT_PORT 9500 37 | #define RTSP_RESPONSE "RTSP/1.0 " 38 | #define CMD_OPTIONS "OPTIONS rtsp://%s:%lu RTSP/1.0\r\nCSeq: %i\r\n\r\n" 39 | #define CMD_DESCRIBE "DESCRIBE %s RTSP/1.0\r\nCSeq: %i\r\nAccept: application/sdp\r\n\r\n" 40 | 41 | //#define CMD_SETUP "SETUP %s/trackID=1 RTSP/1.0\r\nCSeq: %i\r\nTransport: RTP/AVP;unicast;client_port=%i-%i\r\n\r\n" 42 | 43 | #define CMD_SETUP "SETUP %s/trackID=1 RTSP/1.0\r\nCSeq: %i\r\nTransport: RTP/AVP/TCP;interleaved=0-1;\r\n\r\n" 44 | 45 | 46 | 47 | #define CMD_PLAY "PLAY %s RTSP/1.0\r\nCSeq: %i\r\nSession: %lu\r\nRange: npt=0.00-\r\n\r\n" 48 | 49 | /* Transport header constants */ 50 | #define SETUP_SESSION "Session: " 51 | #define SETUP_TRNS_CLIENT "client_port=" 52 | #define SETUP_TRNS_SERVER "server_port=" 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /streamer.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #define _GNU_SOURCE 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "network.h" 19 | #include "streamer.h" 20 | #include "utils.h" 21 | 22 | /* 23 | * Register stream data into local file system, create the unix 24 | * socket and return the file descriptor. 25 | */ 26 | int streamer_prepare(const char *name, 27 | unsigned char *sps, int sps_len, 28 | unsigned char *pps, int pps_len) 29 | { 30 | int fd; 31 | int size = 128; 32 | char path_sock[size]; 33 | char path_meta[size]; 34 | uint8_t nal_header[4] = {0x00, 0x00, 0x00, 0x01}; 35 | 36 | snprintf(path_sock, size, "/tmp/%s.h264s.sock", name); 37 | snprintf(path_meta, size, "/tmp/%s.h264s.meta", name); 38 | 39 | /* write metadata file */ 40 | fd = open(path_meta, O_CREAT|O_WRONLY|O_TRUNC, 0666); 41 | if (fd <= 0) { 42 | ERR(); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | write(fd, &nal_header, sizeof(nal_header)); 47 | write(fd, sps, sps_len); 48 | write(fd, &nal_header, sizeof(nal_header)); 49 | write(fd, pps, pps_len); 50 | close(fd); 51 | 52 | /* create unix sock */ 53 | fd = net_unix_sock(path_sock); 54 | 55 | return fd; 56 | } 57 | 58 | /* 59 | * Create the local Pipe, this pipe is used to transfer the 60 | * extracted H264 data to the unix socket. It also takes care 61 | * to increase the pipe buffer size. 62 | */ 63 | int streamer_pipe_init(int pipefd[2]) 64 | { 65 | int fd; 66 | int ret; 67 | int size = 64; 68 | long pipe_max; 69 | char buf[size]; 70 | 71 | /* create pipe */ 72 | ret = pipe(pipefd); 73 | if (ret != 0) { 74 | printf("Error: could not create streamer pipe\n"); 75 | perror("pipe"); 76 | exit(EXIT_FAILURE); 77 | } 78 | 79 | /* Set non blocking mode */ 80 | net_sock_nonblock(pipefd[0]); 81 | net_sock_nonblock(pipefd[1]); 82 | 83 | /* Get maximum pipe buffer size allowed by the kernel */ 84 | fd = open("/proc/sys/fs/pipe-max-size", O_RDONLY); 85 | if (fd <= 0) { 86 | printf("Warning: could not open pipe-max-size"); 87 | } 88 | else { 89 | ret = read(fd, buf, size); 90 | if (ret <= 0) { 91 | printf("Warning: could not read pipe-max-size value"); 92 | perror("read"); 93 | exit(EXIT_FAILURE); 94 | } 95 | 96 | close(fd); 97 | 98 | pipe_max = atol(buf); 99 | ret = fcntl(pipefd[1], F_SETPIPE_SZ, pipe_max); 100 | 101 | if (ret == -1) { 102 | printf("Warning: could not increase pipe limit to %lu\n", pipe_max); 103 | exit(EXIT_FAILURE); 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | return -1; 110 | } 111 | 112 | void *streamer_worker(void *arg) 113 | { 114 | int bytes; 115 | int remote_fd; 116 | int server_fd; 117 | int size = 650000; 118 | char buf[size]; 119 | struct stream_w *sw; 120 | struct sockaddr_un address; 121 | socklen_t socket_size = sizeof(struct sockaddr_un); 122 | 123 | 124 | sw = arg; 125 | sw->task_id = syscall(__NR_gettid); 126 | server_fd = sw->server_fd; 127 | 128 | while (1) { 129 | remote_fd = accept(server_fd, (struct sockaddr *) &address, &socket_size); 130 | if (remote_fd <= 0) { 131 | ERR(); 132 | printf(">> Streamer: closing worker\n"); 133 | pthread_exit(0); 134 | 135 | } 136 | net_sock_nonblock(remote_fd); 137 | 138 | printf("new connection: %i\n", remote_fd); 139 | 140 | while (1) { 141 | memset(buf, '\0', sizeof(buf)); 142 | bytes = read(stream_pipe[0], buf, sizeof(buf)); 143 | if (bytes > 0) { 144 | send(remote_fd, buf, bytes, 0); 145 | } 146 | else if (bytes == -1) { 147 | sleep(0.5); 148 | continue; 149 | } 150 | send(remote_fd, buf, bytes, 0); 151 | } 152 | 153 | close(remote_fd); 154 | } 155 | } 156 | 157 | pid_t streamer_loop(int server_fd) 158 | { 159 | pthread_t tid; 160 | pthread_attr_t thread_attr; 161 | struct stream_w *sw; 162 | 163 | printf("1 server_fd = %i\n", server_fd); 164 | sw = malloc(sizeof(struct stream_w)); 165 | sw->server_fd = server_fd; 166 | sw->task_id = 0; 167 | 168 | pthread_attr_init(&thread_attr); 169 | pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); 170 | if (pthread_create(&tid, &thread_attr, 171 | streamer_worker, (void *) sw) < 0) { 172 | perror("pthread_create"); 173 | exit(EXIT_FAILURE); 174 | } 175 | 176 | return sw->task_id; 177 | } 178 | 179 | int streamer_write_h264_header(unsigned char *sps_dec, size_t sps_len, 180 | unsigned char *pps_dec, size_t pps_len) 181 | { 182 | uint8_t nal_header[4] = {0x00, 0x00, 0x00, 0x01}; 183 | 184 | /* [00 00 00 01] [SPS] */ 185 | write(stream_fs_fd, &nal_header, sizeof(nal_header)); 186 | write(stream_fs_fd, sps_dec, sps_len); 187 | 188 | /* [00 00 00 01] [PPS] */ 189 | write(stream_fs_fd, &nal_header, sizeof(nal_header)); 190 | write(stream_fs_fd, pps_dec, pps_len); 191 | 192 | return 0; 193 | } 194 | 195 | /* write data to unix socket */ 196 | int streamer_write(const void *buf, size_t count) 197 | { 198 | /* write to file system debug file */ 199 | if (stream_dump) { 200 | write(stream_fs_fd, buf, count); 201 | } 202 | 203 | /* write to pipe */ 204 | return write(stream_pipe[1], buf, count); 205 | } 206 | 207 | int streamer_write_nal() 208 | { 209 | uint8_t nal_header[4] = {0x00, 0x00, 0x00, 0x01}; 210 | 211 | /* write header to file system debug file */ 212 | if (stream_dump) { 213 | write(stream_fs_fd, &nal_header, sizeof(nal_header)); 214 | } 215 | 216 | return write(stream_pipe[1], &nal_header, sizeof(nal_header)); 217 | } 218 | -------------------------------------------------------------------------------- /streamer.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #ifndef STREAMER_H 4 | #define STREAMER_H 5 | 6 | #ifndef F_SETPIPE_SZ 7 | #define F_SETPIPE_SZ 1031 8 | #endif 9 | 10 | int stream_sock; 11 | int stream_pipe[2]; 12 | int stream_fs_fd; 13 | char *stream_dump; 14 | 15 | struct stream_w { 16 | int server_fd; 17 | pid_t task_id; 18 | }; 19 | 20 | pid_t streamer_loop(int server_fd); 21 | int streamer_prepare(const char *name, 22 | unsigned char *sps, int sps_len, 23 | unsigned char *pps, int pps_len); 24 | int streamer_pipe_init(int pipefd[2]); 25 | int streamer_write(const void *buf, size_t count); 26 | int streamer_write_nal(); 27 | int streamer_write_h264_header(unsigned char *sps_dec, size_t sps_len, 28 | unsigned char *pps_dec, size_t pps_len); 29 | #endif 30 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void str_error(int errnum, const char *file, int line, const char *func) 6 | { 7 | int size = 256; 8 | char *err; 9 | char buf[size * 2]; 10 | 11 | err = strerror(errnum); 12 | snprintf(buf, size * 2, 13 | "[ERROR] %s:%i at %s(): %s", file, line, func, err); 14 | 15 | printf("%s\n", buf); 16 | fflush(stdout); 17 | } 18 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | /* RTSP Client 4 | * ----------- 5 | * Written by Eduardo Silva P. 6 | */ 7 | 8 | #include 9 | 10 | #ifndef RTPS_UTILS_H 11 | #define RTPS_UTILS_H 12 | 13 | /* ansi colors */ 14 | #define ANSI_BOLD "\033[1m" 15 | #define ANSI_CYAN "\033[36m" 16 | #define ANSI_MAGENTA "\033[35m" 17 | #define ANSI_RED "\033[31m" 18 | #define ANSI_YELLOW "\033[33m" 19 | #define ANSI_BLUE "\033[34m" 20 | #define ANSI_GREEN "\033[32m" 21 | #define ANSI_WHITE "\033[37m" 22 | #define ANSI_RESET "\033[0m" 23 | 24 | /* request/response debug colors */ 25 | #define RES_HEADER ANSI_BOLD ANSI_GREEN 26 | #define REQ_HEADER ANSI_BOLD ANSI_YELLOW 27 | 28 | /* Debug macros */ 29 | #define DEBUG_REQ(...) if (opt_verbose) { \ 30 | printf(REQ_HEADER); \ 31 | printf(__VA_ARGS__); \ 32 | printf(ANSI_RESET "\n");} 33 | 34 | #define DEBUG_RES(...) if (opt_verbose) { \ 35 | printf(RES_HEADER); \ 36 | printf(__VA_ARGS__); \ 37 | printf(ANSI_RESET "\n");} 38 | 39 | #define RTSP_INFO(...) printf(ANSI_BOLD "[RTSP] " ANSI_RESET); \ 40 | printf(__VA_ARGS__); 41 | #define RTP_INFO(...) printf(ANSI_BOLD "[RTP ] " ANSI_RESET); \ 42 | printf(__VA_ARGS__); 43 | 44 | /* Check if a bit is 1 or 0 */ 45 | #define CHECK_BIT(var, pos) !!((var) & (1 << (pos))) 46 | 47 | /* Error debug */ 48 | #define ERR() str_error(errno, __FILE__, __LINE__, __FUNCTION__) 49 | 50 | /* fcntl pipe value */ 51 | #ifndef F_LINUX_SPECIFIC_BASE 52 | #define F_LINUX_SPECIFIC_BASE 1024 53 | #endif 54 | #ifndef F_SETPIPE_SZ 55 | #define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) 56 | #endif 57 | #ifndef F_GETPIPE_SZ 58 | #define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) 59 | #endif 60 | 61 | void str_error(int errnum, const char *file, int line, const char *func); 62 | 63 | #endif 64 | --------------------------------------------------------------------------------