├── .gitignore ├── Makefile ├── README.md ├── demo └── rtspsrv_demo.c ├── doc └── abbreviation.txt ├── inc └── librtspsrv.h └── src ├── delay_task.c ├── delay_task.h ├── librtspsrv.c ├── list.h ├── log.c ├── log.h ├── rtcp.c ├── rtcp.h ├── rtp.c ├── rtp.h ├── rtsp_method.c ├── rtsp_method.h ├── rtsp_parser.c ├── rtsp_parser.h ├── rtsp_sess.c ├── rtsp_sess.h ├── rtsp_srv.c ├── rtsp_srv.h ├── sd_handler.c ├── sd_handler.h ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Git files that we don't want to ignore even it they are dot-files. 2 | demo/rtspsrv_demo 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # File Name : Makefile 4 | # 5 | # Author : Hu Lizhen 6 | # 7 | # Create Date : 2012-03-14 8 | # 9 | ############################################################## 10 | 11 | # Name of current library. 12 | LIBNAME = rtspsrv 13 | 14 | # Libraries needed. 15 | LDLIBS += -l$(LIBNAME) 16 | LDLIBS += -lpthread 17 | 18 | # project directories 19 | ROOTDIR := .. 20 | DEMODIR := demo 21 | SRCDIR := src 22 | LIBDIR := . 23 | INCDIR := inc 24 | TMPDIR := tmp 25 | 26 | # Compile command. 27 | #CROSS ?= arm-hismall-linux- 28 | CROSS ?= 29 | CC = $(CROSS)gcc 30 | LD = $(CROSS)ld 31 | AR = $(CROSS)ar 32 | AS = $(CROSS)as 33 | ARFLAGS = rcsv 34 | STRIP = $(CROSS)strip 35 | CFLAGS += -I$(INCDIR) 36 | CFLAGS += -L$(LIBDIR) 37 | #CFLAGS += -O2 38 | CFLAGS += -Wall -Werror 39 | CFLAGS += -g -rdynamic 40 | CFLAGS += -D_GNU_SOURCE 41 | CFLAGS += -D_REENTRANT -pipe 42 | CFLAGS += -fsigned-char 43 | #CFLAGS += -fno-strict-aliasing 44 | RM = rm -rfv 45 | CP = cp -rfv 46 | 47 | # build directories 48 | DEPDIR := $(TMPDIR)/dep 49 | OBJDIR := $(TMPDIR)/obj 50 | 51 | # project and build files 52 | SRCS := $(wildcard $(SRCDIR)/*.c) 53 | OBJS := $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS)) 54 | DEPS := $(patsubst $(SRCDIR)/%.c,$(DEPDIR)/%.d,$(SRCS)) 55 | 56 | LIB := lib$(LIBNAME).a 57 | DEMO := $(LIBNAME)_demo 58 | 59 | # default target : generate lib & demo 60 | all : $(LIBDIR)/$(LIB) $(DEMODIR)/$(DEMO) 61 | 62 | ifneq ($(MAKECMDGOALS), clean) 63 | -include $(DEPS) 64 | endif 65 | 66 | $(DEMODIR)/$(DEMO) : $(DEMODIR)/*.c $(LIBDIR)/$(LIB) 67 | $(CC) $(CFLAGS) -o $@ $< $(LDLIBS) 68 | $(STRIP) $@ 69 | 70 | $(LIBDIR)/$(LIB) : $(OBJS) 71 | $(AR) $(ARFLAGS) $@ $^ 72 | 73 | # Compile all source files, create directory if it doesn't exist. 74 | $(OBJDIR)/%.o : $(SRCDIR)/%.c 75 | @set -e; \ 76 | if test ! -d $(OBJDIR); then \ 77 | mkdir -p $(OBJDIR); \ 78 | fi 79 | $(CC) $(CFLAGS) -o $@ -c $< 80 | 81 | # Generate depend files, create directory if it doesn't exist. 82 | $(DEPDIR)/%.d : $(SRCDIR)/%.c 83 | @set -e; \ 84 | if test ! -d $(DEPDIR); then \ 85 | mkdir -p $(DEPDIR); \ 86 | fi 87 | $(CC) -MM $(CFLAGS) $< | sed 's,\(.*\)\.o[ :]*,$(OBJDIR)/\1.o $@: ,g' > $@ 88 | 89 | .PHONY : clean 90 | 91 | clean : 92 | @$(RM) \ 93 | $(TMPDIR) \ 94 | $(LIBDIR)/$(LIB) \ 95 | $(DEMODIR)/$(DEMO) 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | An implementation of RTSP server 2 | -------------------------------------------------------------------------------- /demo/rtspsrv_demo.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtspDemo.c 3 | * Description : Demostrate how to use this library. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-13 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "librtspsrv.h" 15 | 16 | #define FILE_HDR_SZ 512 17 | 18 | /* AV file frame flags. */ 19 | #define FRM_FLG_I 0x63643030 /* I frame . */ 20 | #define FRM_FLG_P 0x63643130 /* P frame . */ 21 | #define FRM_FLG_A 0x62773130 /* A frame . */ 22 | #define FRM_FLG_INFO 0x30317762 /* Frmae info. */ 23 | 24 | /* Frame header */ 25 | struct frm_hdr { 26 | unsigned int frm_flg; /* Frame type: <00dc - I>, <01dc - P>, <01wb - A>. */ 27 | unsigned int frm_sz; /* Size of frame. */ 28 | unsigned char hour; 29 | unsigned char min; 30 | unsigned char sec; 31 | unsigned char ext; 32 | unsigned int i_last_offset; 33 | long long pts; /* Timestamp. */ 34 | unsigned int alarm_info; 35 | unsigned int pad; 36 | }; 37 | 38 | int start_strm(struct strm_info *strmp) 39 | { 40 | if (strmp->file_name) { 41 | FILE *fp = fopen(strmp->file_name, "rb"); 42 | if (!fp) { 43 | printf("fopen local file[%s] error: %s\n", 44 | strmp->file_name, strerror(errno)); 45 | return -1; 46 | } 47 | strmp->usr_data = fp; 48 | 49 | /* Skip over the file header. */ 50 | if (fseek(fp, FILE_HDR_SZ, SEEK_SET) < 0) { 51 | perror("fseek av file error"); 52 | return -1; 53 | } 54 | } 55 | return 0; 56 | } 57 | 58 | void stop_strm(struct strm_info *strmp) 59 | { 60 | if (strmp->file_name) { 61 | FILE *fp = (FILE *)strmp->usr_data; 62 | if (strmp->usr_data) { 63 | fclose(fp); 64 | strmp->usr_data = 0; 65 | } 66 | } 67 | return; 68 | } 69 | 70 | int get_frm(struct strm_info *strmp, struct frm_info *frmp) 71 | { 72 | FILE *fp = (FILE *)strmp->usr_data; 73 | size_t nr = 0; /* Bytes read. */ 74 | struct frm_hdr frm_hdr; 75 | 76 | retry: 77 | /* Read frame head. */ 78 | nr = fread(&frm_hdr, 1, sizeof(struct frm_hdr), fp); 79 | if (nr < 0) { 80 | perror("fread frame header error"); 81 | return -1; 82 | } else if (nr == 0) { 83 | fseek(fp, FILE_HDR_SZ, SEEK_SET); 84 | goto retry; 85 | } else if (nr != sizeof(struct frm_hdr)) { 86 | printf("Bytes read[%d] != sizeof(struct frm_hdr) T_T\n", (int)nr); 87 | return -1; 88 | } 89 | 90 | if (frm_hdr.frm_sz >= MAX_FRM_SZ) { 91 | printf("The size of frame is too large: %x!\n", frm_hdr.frm_sz); 92 | return -1; 93 | } 94 | 95 | /* Read frame data. */ 96 | nr = fread(frmp->frm_buf, 1, frm_hdr.frm_sz, fp); 97 | if (nr < 0) { 98 | perror("fread frame data error"); 99 | return -1; 100 | } else if (nr == 0) { 101 | fseek(fp, FILE_HDR_SZ, SEEK_SET); 102 | goto retry; 103 | } else if (nr != frm_hdr.frm_sz) { 104 | printf("Bytes read[%d] != frm_hdr.frm_sz T_T\n", (int)nr); 105 | return -1; 106 | } 107 | 108 | frmp->frm_sz = frm_hdr.frm_sz; 109 | switch (frm_hdr.frm_flg) { 110 | case FRM_FLG_I: 111 | frmp->frm_type = FRM_TYPE_I; 112 | break; 113 | case FRM_FLG_P: 114 | frmp->frm_type = FRM_TYPE_P; 115 | break; 116 | case FRM_FLG_A: 117 | frmp->frm_type = FRM_TYPE_A; 118 | break; 119 | default: 120 | printf("Undefined frame type, flag = %d\n", frm_hdr.frm_flg); 121 | return -1; 122 | } 123 | 124 | return 0; 125 | } 126 | 127 | static void signal_handler(int signo) 128 | { 129 | switch (signo) { 130 | case SIGINT: 131 | exit(0); 132 | break; 133 | case SIGPIPE: 134 | break; 135 | default: 136 | break; 137 | } 138 | return; 139 | } 140 | 141 | static void init_signals(void) 142 | { 143 | signal(SIGINT, signal_handler); 144 | signal(SIGPIPE, signal_handler); 145 | return; 146 | } 147 | 148 | int main(int argc, char *argv[]) 149 | { 150 | init_signals(); 151 | 152 | set_strm_cb(start_strm, stop_strm, get_frm); 153 | rtsp_init_lib(); 154 | 155 | while (1) { 156 | sleep(3); 157 | } 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /doc/abbreviation.txt: -------------------------------------------------------------------------------- 1 | Here list the most abbreviations used in the librtspsrv in alphabetical order. 2 | 3 | When you can't catch on the meaning of some abbreviations, 4 | welcome to come back and check whether it has been listed below. ^_^ 5 | 6 | 7 | ===================================== 8 | Abbreviation | Meaning 9 | ===================================== 10 | av | audio & video 11 | arg | argument 12 | buf | buffer 13 | cb | callback 14 | chn | channel 15 | cli | client 16 | conn | connection / connect 17 | deinit | de-initialize / de-initialization 18 | dfl | default 19 | dst | destination 20 | dt | delay task / delay time 21 | ev | event 22 | fb | frame buffer 23 | frm | frame 24 | hdr | header 25 | info | information 26 | init | initialize / initialization 27 | lsn | listen 28 | nalu | NAL Unit 29 | pl | payload 30 | proc | process 31 | pt | payload type 32 | req | request 33 | res | result 34 | resp | response 35 | ret | return value 36 | rl | resource limit 37 | rtn | return 38 | sa | socket address 39 | sd | socket descriptor 40 | seq | sequence number 41 | sess | session 42 | sock | socket 43 | src | source 44 | srv | server / service 45 | strm | stream 46 | sz | size 47 | thrd | thread 48 | tid | thread id 49 | usr | user 50 | -------------------------------------------------------------------------------- /inc/librtspsrv.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : librtspsrv.h 3 | * Description : 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-15 6 | ********************************************************************/ 7 | 8 | #ifndef __LIBRTSPSRV_H__ 9 | #define __LIBRTSPSRV_H__ 10 | 11 | 12 | #if defined (__cplusplus) || defined (_cplusplus) 13 | extern "C" { 14 | #endif 15 | 16 | #define DFL_RTSP_PORT 10554 /* Default RTSP port. */ 17 | #define DFL_HTTP_PORT 0 /* Not support RTSP over HTTP yet. */ 18 | 19 | #define MAX_CHN_NUM 8 /* Max channel number. */ 20 | #define MAX_FRM_SZ (1 * 1024 * 1024) /* Max frame size bytes. */ 21 | 22 | /* Frame type. */ 23 | enum frm_type { 24 | FRM_TYPE_I = 1, 25 | FRM_TYPE_B, 26 | FRM_TYPE_P, 27 | FRM_TYPE_A, 28 | }; 29 | 30 | enum chn_type { 31 | CHN_TYPE_MAIN, 32 | CHN_TYPE_MINOR, 33 | }; 34 | 35 | /* Stream resource information. */ 36 | struct strm_info { 37 | int chn_no; /* Channel number. */ 38 | enum chn_type chn_type; 39 | char *file_name; /* Local AV file name. */ 40 | void *usr_data; 41 | }; 42 | 43 | /* Frame information used for getting frame. */ 44 | struct frm_info { 45 | unsigned int frm_sz; /* Frame size. */ 46 | enum frm_type frm_type; /* Frame type. */ 47 | char *frm_buf; /* Frame buffer: store pure av data. */ 48 | }; 49 | 50 | /* 51 | * Callback function to start, stop and get media resource. 52 | */ 53 | typedef int (*start_strm_t)(struct strm_info *); 54 | typedef void (*stop_strm_t)(struct strm_info *); 55 | typedef int (*get_frm_t)(struct strm_info *, struct frm_info *); 56 | 57 | void set_strm_cb(start_strm_t start, stop_strm_t stop, get_frm_t get); 58 | void get_strm_cb(start_strm_t *start, stop_strm_t *stop, get_frm_t *get); 59 | int rtsp_init_lib(void); 60 | int rtsp_deinit_lib(void); 61 | 62 | #if defined (__cplusplus) || defined (_cplusplus) 63 | } 64 | #endif 65 | 66 | #endif /* __LIBRTSPSRV_H__ */ 67 | 68 | -------------------------------------------------------------------------------- /src/delay_task.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : delay_task.c 3 | * Description : Implemetation of delay task queue. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-12-04 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include "list.h" 11 | #include "util.h" 12 | #include "log.h" 13 | #include "rtsp_srv.h" 14 | #include "delay_task.h" 15 | 16 | 17 | /* Mean it's time to do the delayed task in queue. */ 18 | #define TIME_UP 0 19 | 20 | 21 | static unsigned long long last_sync_time; 22 | 23 | 24 | static unsigned long long time_now(void) 25 | { 26 | struct timeval tv; 27 | gettimeofday(&tv, NULL); 28 | 29 | /* 1s = (1 * MILLION)us */ 30 | return tv.tv_sec * MILLION + tv.tv_usec; 31 | } 32 | 33 | /* 34 | * Synchronize the delay time in the task queue, 35 | * and check which task is time up. 36 | */ 37 | static void sync_delay_time(void) 38 | { 39 | int elapsed = 0; /* Elapsed time since last sync time. */ 40 | unsigned long long now = 0; 41 | struct delay_task *dtp = 0; /* Used as loop curser. */ 42 | 43 | /* The system clock has gone back in time, just reset sync time. */ 44 | now = time_now(); 45 | if (now < last_sync_time) { 46 | last_sync_time = now; 47 | return; 48 | } 49 | 50 | /* Re-calculate the delay time of any task whose time is up. */ 51 | elapsed = (now - last_sync_time >= INT_MAX) ? 0 : (now - last_sync_time); 52 | list_for_each_entry(dtp, &rtsp_srv.delay_task_queue, entry) { 53 | if (dtp->delta_time <= elapsed) { /* Time up. */ 54 | elapsed -= dtp->delta_time; 55 | dtp->delta_time = TIME_UP; 56 | continue; 57 | } else { 58 | dtp->delta_time -= elapsed; 59 | break; 60 | } 61 | } 62 | 63 | last_sync_time = now; 64 | return; 65 | } 66 | 67 | /* 68 | * Get the minimum delay time in the task queue. 69 | * The delay time of first task must be the minimun one ^_^ 70 | * Usually used in epoll_wait()'s timeout. 71 | */ 72 | int get_min_delay_time(void) 73 | { 74 | struct delay_task *dtp = NULL; 75 | 76 | if (!list_empty(&rtsp_srv.delay_task_queue)) { 77 | dtp = list_first_entry(&rtsp_srv.delay_task_queue, struct delay_task, entry); 78 | sync_delay_time(); 79 | return dtp->delta_time; 80 | } 81 | return -1; 82 | } 83 | 84 | static void add_delay_task(struct delay_task *dtp) 85 | { 86 | struct list_head *pos = NULL; /* The position which new task is added before. */ 87 | struct delay_task *tmp = NULL; /* Used as loop curser. */ 88 | 89 | /* Synchronize the delay time of tasks to improve accuracy. */ 90 | sync_delay_time(); 91 | 92 | /* Find the proper entry of new delay task. */ 93 | pos = &rtsp_srv.delay_task_queue; 94 | list_for_each_entry(tmp, &rtsp_srv.delay_task_queue, entry) { 95 | if (tmp->delta_time > dtp->delta_time) { 96 | tmp->delta_time -= dtp->delta_time; 97 | pos = &tmp->entry; 98 | break; 99 | } else { 100 | dtp->delta_time -= tmp->delta_time; 101 | } 102 | } 103 | 104 | /* Add the new delay task to the queue. */ 105 | list_add_tail(&dtp->entry, pos); 106 | return; 107 | } 108 | 109 | static void del_delay_task(struct delay_task *dtp) 110 | { 111 | struct delay_task *next_dtp = NULL; /* The task just after @dtp in delay queue. */ 112 | 113 | if (!list_empty(&rtsp_srv.delay_task_queue) && 114 | !list_is_singular(&rtsp_srv.delay_task_queue)) { 115 | next_dtp = list_entry(dtp->entry.next, struct delay_task, entry); 116 | next_dtp->delta_time += dtp->delta_time; 117 | } 118 | 119 | list_del(&dtp->entry); 120 | 121 | return; 122 | } 123 | 124 | struct delay_task *create_delay_task(delay_task_proc_t proc, void *arg, int delay) 125 | { 126 | struct delay_task *dtp = NULL; 127 | 128 | dtp = mallocz(sizeof(*dtp)); 129 | if (!dtp) { 130 | printd(ERR "Allocate memory for struct delay_task failed!\n"); 131 | return NULL; 132 | } 133 | dtp->arg = arg; 134 | dtp->proc = proc; 135 | dtp->delta_time = delay; 136 | 137 | /* Add delayed task to queue. */ 138 | add_delay_task(dtp); 139 | 140 | return dtp; 141 | } 142 | 143 | void destroy_delay_task(struct delay_task *dtp) 144 | { 145 | struct delay_task *tmp = NULL; 146 | 147 | /* Delete delayed task from queue. */ 148 | list_for_each_entry(tmp, &rtsp_srv.delay_task_queue, entry) { 149 | if (tmp == dtp) { 150 | del_delay_task(dtp); 151 | freez(dtp); 152 | return; 153 | } 154 | } 155 | return; 156 | } 157 | 158 | void update_delay_time(struct delay_task *dtp, int delay) 159 | { 160 | del_delay_task(dtp); 161 | dtp->delta_time = delay; 162 | add_delay_task(dtp); 163 | return; 164 | } 165 | 166 | void do_delay_task(void) 167 | { 168 | struct delay_task *dtp = NULL; 169 | int next_delay = 0; 170 | 171 | /* Do the task in delay queue. */ 172 | if (!list_empty(&rtsp_srv.delay_task_queue)) { 173 | dtp = list_first_entry(&rtsp_srv.delay_task_queue, struct delay_task, entry); 174 | 175 | if (dtp->delta_time != TIME_UP) { 176 | sync_delay_time(); 177 | } 178 | 179 | if (dtp->delta_time == TIME_UP) { 180 | /* 181 | * The callback function $proc() will return -1 when 182 | * task is finished or failed, >= 0 when need to be 183 | * delayed next time. 184 | * 185 | * We destroy the delayed task when task is finished or failed. 186 | */ 187 | next_delay = dtp->proc(dtp->arg); 188 | if (next_delay >= 0) { 189 | update_delay_time(dtp, next_delay); 190 | } else { 191 | destroy_delay_task(dtp); 192 | } 193 | } 194 | } 195 | return; 196 | } 197 | 198 | /** 199 | * Test whether there's any un-done delayed task 200 | * matched with the @arg in queue. 201 | * */ 202 | void destroy_sess_task(void *arg) 203 | { 204 | struct delay_task *dtp = NULL; 205 | struct delay_task *tmp = NULL; 206 | 207 | list_for_each_entry_safe(dtp, tmp, &rtsp_srv.delay_task_queue, entry) { 208 | if (dtp->arg == arg) { 209 | destroy_delay_task(dtp); 210 | } 211 | } 212 | return; 213 | } 214 | 215 | void init_delay_task_queue(void) 216 | { 217 | INIT_LIST_HEAD(&rtsp_srv.delay_task_queue); 218 | last_sync_time = time_now(); 219 | return; 220 | } 221 | 222 | void deinit_delay_task_queue(void) 223 | { 224 | return; 225 | } 226 | -------------------------------------------------------------------------------- /src/delay_task.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : delay_task.h 3 | * Description : Implemetation of delay task queue. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-12-04 6 | ********************************************************************/ 7 | 8 | #ifndef __DELAY_TASK_H__ 9 | #define __DELAY_TASK_H__ 10 | 11 | 12 | #define THOUSAND 1000 13 | #define MILLION 1000000 14 | 15 | 16 | typedef int (*delay_task_proc_t)(void *); 17 | 18 | 19 | /* 20 | * Delay task to be done in the future. 21 | */ 22 | struct delay_task { 23 | struct list_head entry; /* Entry of task_queue. */ 24 | int delta_time; /* Delay time relative to previous delayed task. */ 25 | void *arg; /* Argument for the callback function. */ 26 | delay_task_proc_t proc; /* Callback of the task. */ 27 | }; 28 | 29 | 30 | void do_delay_task(void); 31 | int get_min_delay_time(void); 32 | void update_delay_time(struct delay_task *dtp, int delay); 33 | struct delay_task *create_delay_task(delay_task_proc_t proc, void *arg, int delay); 34 | void destroy_delay_task(struct delay_task *dtp); 35 | void destroy_sess_task(void *arg); 36 | void init_delay_task_queue(void); 37 | void deinit_delay_task_queue(void); 38 | 39 | 40 | #endif /* __DELAY_TASK_H__ */ 41 | 42 | -------------------------------------------------------------------------------- /src/librtspsrv.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : librtspsrv.c 3 | * Description : 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-15 6 | ********************************************************************/ 7 | 8 | #include 9 | #include "librtspsrv.h" 10 | #include "rtsp_srv.h" 11 | #include "util.h" 12 | #include "log.h" 13 | 14 | 15 | /* 16 | * NOTE: 17 | * If you've made big changes or added new features, 18 | * then bump the MAJOR number up. 19 | * Else if you've fix big bugs or add features based on existed part, 20 | * then bump the MINOR number up. 21 | * Else if you've changed little things or just fixed some small bugs, 22 | * then bump the BUILD number up is enough. 23 | */ 24 | #define LIBRARY_VERSION_MAJOR 0 25 | #define LIBRARY_VERSION_MINOR 1 26 | #define LIBRARY_VERSION_BUILD 0 27 | 28 | 29 | /* RTSP stream relative callback functions. */ 30 | static start_strm_t start_strm; 31 | static stop_strm_t stop_strm; 32 | static get_frm_t get_frm; 33 | 34 | 35 | static void print_lib_info(void) 36 | { 37 | printf("\n \033[1;32m****************************************************\033[0m\n" 38 | " \033[1;32m* RTSP library version: [V%d.%d.%d] *\033[0m\n" 39 | " \033[1;32m* Build date and time: [%s, %s] *\033[0m\n" 40 | " \033[1;32m****************************************************\033[0m\n\n\n", 41 | LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR, LIBRARY_VERSION_BUILD, 42 | __DATE__, __TIME__); 43 | return; 44 | } 45 | 46 | void set_strm_cb(start_strm_t start, stop_strm_t stop, get_frm_t get) 47 | { 48 | start_strm = start; 49 | stop_strm = stop; 50 | get_frm = get; 51 | return; 52 | } 53 | 54 | void get_strm_cb(start_strm_t *start, stop_strm_t *stop, get_frm_t *get) 55 | { 56 | if (start) { 57 | *start = start_strm; 58 | } 59 | if (stop) { 60 | *stop = stop_strm; 61 | } 62 | if (get) { 63 | *get = get_frm; 64 | } 65 | return; 66 | } 67 | 68 | int rtsp_init_lib(void) 69 | { 70 | print_lib_info(); 71 | 72 | if (!start_strm || !stop_strm || !get_frm) { 73 | printf("\033[31m *** RTSP stream callback MUST be set properly! ***\033[0m\n"); 74 | return -1; 75 | } 76 | start_rtsp_srv(); 77 | return 0; 78 | } 79 | 80 | int rtsp_deinit_lib(void) 81 | { 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * File Name : list.h 3 | * Description : Simple doubly linked list implementation. 4 | * Taken from include/linux/list.h 5 | * in linux kernel[V3.6.6] tree. 6 | * Thanks the kernel developers. ^_^ 7 | * Taken Date : 2012-11-16 8 | ***********************************************************************/ 9 | 10 | #ifndef __LIST_H__ 11 | #define __LIST_H__ 12 | 13 | 14 | #define NULL ((void *)0) 15 | 16 | /* 17 | * These are non-NULL pointers that will result in page faults 18 | * under normal circumstances, used to verify that nobody uses 19 | * non-initialized list entries. 20 | */ 21 | #define LIST_POISON1 ((void *) 0x00100100) 22 | #define LIST_POISON2 ((void *) 0x00200200) 23 | 24 | #ifndef offsetof 25 | #define offsetof(type, member) ((size_t) &((type *)0)->member) 26 | #endif 27 | 28 | /** 29 | * container_of - cast a member of a structure out to the containing structure 30 | * @ptr: the pointer to the member. 31 | * @type: the type of the container struct this is embedded in. 32 | * @member: the name of the member within the struct. 33 | * 34 | */ 35 | #ifndef container_of 36 | #define container_of(ptr, type, member) \ 37 | ({ \ 38 | const typeof(((type *)0)->member) *__mptr = (ptr); \ 39 | (type *)((char *)__mptr - offsetof(type, member)); \ 40 | }) 41 | #endif 42 | 43 | /* 44 | * Simple doubly linked list implementation. 45 | * 46 | * Some of the internal functions ("__xxx") are useful when 47 | * manipulating whole lists rather than single entries, as 48 | * sometimes we already know the next/prev entries and we can 49 | * generate better code by using them directly rather than 50 | * using the generic single-entry routines. 51 | */ 52 | struct list_head { 53 | struct list_head *next, *prev; 54 | }; 55 | 56 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 57 | 58 | #define LIST_HEAD(name) \ 59 | struct list_head name = LIST_HEAD_INIT(name) 60 | 61 | static inline void INIT_LIST_HEAD(struct list_head *list) 62 | { 63 | list->next = list; 64 | list->prev = list; 65 | } 66 | 67 | /* 68 | * Insert a new entry between two known consecutive entries. 69 | * 70 | * This is only for internal list manipulation where we know 71 | * the prev/next entries already! 72 | */ 73 | static inline void __list_add(struct list_head *new, 74 | struct list_head *prev, 75 | struct list_head *next) 76 | { 77 | next->prev = new; 78 | new->next = next; 79 | new->prev = prev; 80 | prev->next = new; 81 | } 82 | 83 | /** 84 | * list_add - add a new entry 85 | * @new: new entry to be added 86 | * @head: list head to add it after 87 | * 88 | * Insert a new entry after the specified head. 89 | * This is good for implementing stacks. 90 | */ 91 | static inline void list_add(struct list_head *new, struct list_head *head) 92 | { 93 | __list_add(new, head, head->next); 94 | } 95 | 96 | /** 97 | * list_add_tail - add a new entry 98 | * @new: new entry to be added 99 | * @head: list head to add it before 100 | * 101 | * Insert a new entry before the specified head. 102 | * This is useful for implementing queues. 103 | */ 104 | static inline void list_add_tail(struct list_head *new, struct list_head *head) 105 | { 106 | __list_add(new, head->prev, head); 107 | } 108 | 109 | /* 110 | * Delete a list entry by making the prev/next entries 111 | * point to each other. 112 | * 113 | * This is only for internal list manipulation where we know 114 | * the prev/next entries already! 115 | */ 116 | static inline void __list_del(struct list_head * prev, struct list_head * next) 117 | { 118 | next->prev = prev; 119 | prev->next = next; 120 | } 121 | 122 | /** 123 | * list_del - deletes entry from list. 124 | * @entry: the element to delete from the list. 125 | * Note: list_empty() on entry does not return true after this, the entry is 126 | * in an undefined state. 127 | */ 128 | static inline void __list_del_entry(struct list_head *entry) 129 | { 130 | __list_del(entry->prev, entry->next); 131 | } 132 | 133 | static inline void list_del(struct list_head *entry) 134 | { 135 | __list_del(entry->prev, entry->next); 136 | entry->next = LIST_POISON1; 137 | entry->prev = LIST_POISON2; 138 | } 139 | 140 | /** 141 | * list_replace - replace old entry by new one 142 | * @old : the element to be replaced 143 | * @new : the new element to insert 144 | * 145 | * If @old was empty, it will be overwritten. 146 | */ 147 | static inline void list_replace(struct list_head *old, 148 | struct list_head *new) 149 | { 150 | new->next = old->next; 151 | new->next->prev = new; 152 | new->prev = old->prev; 153 | new->prev->next = new; 154 | } 155 | 156 | static inline void list_replace_init(struct list_head *old, 157 | struct list_head *new) 158 | { 159 | list_replace(old, new); 160 | INIT_LIST_HEAD(old); 161 | } 162 | 163 | /** 164 | * list_del_init - deletes entry from list and reinitialize it. 165 | * @entry: the element to delete from the list. 166 | */ 167 | static inline void list_del_init(struct list_head *entry) 168 | { 169 | __list_del_entry(entry); 170 | INIT_LIST_HEAD(entry); 171 | } 172 | 173 | /** 174 | * list_move - delete from one list and add as another's head 175 | * @list: the entry to move 176 | * @head: the head that will precede our entry 177 | */ 178 | static inline void list_move(struct list_head *list, struct list_head *head) 179 | { 180 | __list_del_entry(list); 181 | list_add(list, head); 182 | } 183 | 184 | /** 185 | * list_move_tail - delete from one list and add as another's tail 186 | * @list: the entry to move 187 | * @head: the head that will follow our entry 188 | */ 189 | static inline void list_move_tail(struct list_head *list, 190 | struct list_head *head) 191 | { 192 | __list_del_entry(list); 193 | list_add_tail(list, head); 194 | } 195 | 196 | /** 197 | * list_is_last - tests whether @list is the last entry in list @head 198 | * @list: the entry to test 199 | * @head: the head of the list 200 | */ 201 | static inline int list_is_last(const struct list_head *list, 202 | const struct list_head *head) 203 | { 204 | return list->next == head; 205 | } 206 | 207 | /** 208 | * list_empty - tests whether a list is empty 209 | * @head: the list to test. 210 | */ 211 | static inline int list_empty(const struct list_head *head) 212 | { 213 | return head->next == head; 214 | } 215 | 216 | /** 217 | * list_empty_careful - tests whether a list is empty and not being modified 218 | * @head: the list to test 219 | * 220 | * Description: 221 | * tests whether a list is empty _and_ checks that no other CPU might be 222 | * in the process of modifying either member (next or prev) 223 | * 224 | * NOTE: using list_empty_careful() without synchronization 225 | * can only be safe if the only activity that can happen 226 | * to the list entry is list_del_init(). Eg. it cannot be used 227 | * if another CPU could re-list_add() it. 228 | */ 229 | static inline int list_empty_careful(const struct list_head *head) 230 | { 231 | struct list_head *next = head->next; 232 | return (next == head) && (next == head->prev); 233 | } 234 | 235 | /** 236 | * list_rotate_left - rotate the list to the left 237 | * @head: the head of the list 238 | */ 239 | static inline void list_rotate_left(struct list_head *head) 240 | { 241 | struct list_head *first; 242 | 243 | if (!list_empty(head)) { 244 | first = head->next; 245 | list_move_tail(first, head); 246 | } 247 | } 248 | 249 | /** 250 | * list_is_singular - tests whether a list has just one entry. 251 | * @head: the list to test. 252 | */ 253 | static inline int list_is_singular(const struct list_head *head) 254 | { 255 | return !list_empty(head) && (head->next == head->prev); 256 | } 257 | 258 | static inline void __list_cut_position(struct list_head *list, 259 | struct list_head *head, struct list_head *entry) 260 | { 261 | struct list_head *new_first = entry->next; 262 | list->next = head->next; 263 | list->next->prev = list; 264 | list->prev = entry; 265 | entry->next = list; 266 | head->next = new_first; 267 | new_first->prev = head; 268 | } 269 | 270 | /** 271 | * list_cut_position - cut a list into two 272 | * @list: a new list to add all removed entries 273 | * @head: a list with entries 274 | * @entry: an entry within head, could be the head itself 275 | * and if so we won't cut the list 276 | * 277 | * This helper moves the initial part of @head, up to and 278 | * including @entry, from @head to @list. You should 279 | * pass on @entry an element you know is on @head. @list 280 | * should be an empty list or a list you do not care about 281 | * losing its data. 282 | * 283 | */ 284 | static inline void list_cut_position(struct list_head *list, 285 | struct list_head *head, struct list_head *entry) 286 | { 287 | if (list_empty(head)) 288 | return; 289 | if (list_is_singular(head) && 290 | (head->next != entry && head != entry)) 291 | return; 292 | if (entry == head) 293 | INIT_LIST_HEAD(list); 294 | else 295 | __list_cut_position(list, head, entry); 296 | } 297 | 298 | static inline void __list_splice(const struct list_head *list, 299 | struct list_head *prev, 300 | struct list_head *next) 301 | { 302 | struct list_head *first = list->next; 303 | struct list_head *last = list->prev; 304 | 305 | first->prev = prev; 306 | prev->next = first; 307 | 308 | last->next = next; 309 | next->prev = last; 310 | } 311 | 312 | /** 313 | * list_splice - join two lists, this is designed for stacks 314 | * @list: the new list to add. 315 | * @head: the place to add it in the first list. 316 | */ 317 | static inline void list_splice(const struct list_head *list, 318 | struct list_head *head) 319 | { 320 | if (!list_empty(list)) 321 | __list_splice(list, head, head->next); 322 | } 323 | 324 | /** 325 | * list_splice_tail - join two lists, each list being a queue 326 | * @list: the new list to add. 327 | * @head: the place to add it in the first list. 328 | */ 329 | static inline void list_splice_tail(struct list_head *list, 330 | struct list_head *head) 331 | { 332 | if (!list_empty(list)) 333 | __list_splice(list, head->prev, head); 334 | } 335 | 336 | /** 337 | * list_splice_init - join two lists and reinitialise the emptied list. 338 | * @list: the new list to add. 339 | * @head: the place to add it in the first list. 340 | * 341 | * The list at @list is reinitialised 342 | */ 343 | static inline void list_splice_init(struct list_head *list, 344 | struct list_head *head) 345 | { 346 | if (!list_empty(list)) { 347 | __list_splice(list, head, head->next); 348 | INIT_LIST_HEAD(list); 349 | } 350 | } 351 | 352 | /** 353 | * list_splice_tail_init - join two lists and reinitialise the emptied list 354 | * @list: the new list to add. 355 | * @head: the place to add it in the first list. 356 | * 357 | * Each of the lists is a queue. 358 | * The list at @list is reinitialised 359 | */ 360 | static inline void list_splice_tail_init(struct list_head *list, 361 | struct list_head *head) 362 | { 363 | if (!list_empty(list)) { 364 | __list_splice(list, head->prev, head); 365 | INIT_LIST_HEAD(list); 366 | } 367 | } 368 | 369 | /** 370 | * list_entry - get the struct for this entry 371 | * @ptr: the &struct list_head pointer. 372 | * @type: the type of the struct this is embedded in. 373 | * @member: the name of the list_struct within the struct. 374 | */ 375 | #define list_entry(ptr, type, member) \ 376 | container_of(ptr, type, member) 377 | 378 | /** 379 | * list_first_entry - get the first element from a list 380 | * @ptr: the list head to take the element from. 381 | * @type: the type of the struct this is embedded in. 382 | * @member: the name of the list_struct within the struct. 383 | * 384 | * Note, that list is expected to be not empty. 385 | */ 386 | #define list_first_entry(ptr, type, member) \ 387 | list_entry((ptr)->next, type, member) 388 | 389 | /** 390 | * list_for_each - iterate over a list 391 | * @pos: the &struct list_head to use as a loop cursor. 392 | * @head: the head for your list. 393 | */ 394 | #define list_for_each(pos, head) \ 395 | for (pos = (head)->next; pos != (head); pos = pos->next) 396 | 397 | /** 398 | * __list_for_each - iterate over a list 399 | * @pos: the &struct list_head to use as a loop cursor. 400 | * @head: the head for your list. 401 | * 402 | * This variant doesn't differ from list_for_each() any more. 403 | * We don't do prefetching in either case. 404 | */ 405 | #define __list_for_each(pos, head) \ 406 | for (pos = (head)->next; pos != (head); pos = pos->next) 407 | 408 | /** 409 | * list_for_each_prev - iterate over a list backwards 410 | * @pos: the &struct list_head to use as a loop cursor. 411 | * @head: the head for your list. 412 | */ 413 | #define list_for_each_prev(pos, head) \ 414 | for (pos = (head)->prev; pos != (head); pos = pos->prev) 415 | 416 | /** 417 | * list_for_each_safe - iterate over a list safe against removal of list entry 418 | * @pos: the &struct list_head to use as a loop cursor. 419 | * @n: another &struct list_head to use as temporary storage 420 | * @head: the head for your list. 421 | */ 422 | #define list_for_each_safe(pos, n, head) \ 423 | for (pos = (head)->next, n = pos->next; pos != (head); \ 424 | pos = n, n = pos->next) 425 | 426 | /** 427 | * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry 428 | * @pos: the &struct list_head to use as a loop cursor. 429 | * @n: another &struct list_head to use as temporary storage 430 | * @head: the head for your list. 431 | */ 432 | #define list_for_each_prev_safe(pos, n, head) \ 433 | for (pos = (head)->prev, n = pos->prev; \ 434 | pos != (head); \ 435 | pos = n, n = pos->prev) 436 | 437 | /** 438 | * list_for_each_entry - iterate over list of given type 439 | * @pos: the type * to use as a loop cursor. 440 | * @head: the head for your list. 441 | * @member: the name of the list_struct within the struct. 442 | */ 443 | #define list_for_each_entry(pos, head, member) \ 444 | for (pos = list_entry((head)->next, typeof(*pos), member); \ 445 | &pos->member != (head); \ 446 | pos = list_entry(pos->member.next, typeof(*pos), member)) 447 | 448 | /** 449 | * list_for_each_entry_reverse - iterate backwards over list of given type. 450 | * @pos: the type * to use as a loop cursor. 451 | * @head: the head for your list. 452 | * @member: the name of the list_struct within the struct. 453 | */ 454 | #define list_for_each_entry_reverse(pos, head, member) \ 455 | for (pos = list_entry((head)->prev, typeof(*pos), member); \ 456 | &pos->member != (head); \ 457 | pos = list_entry(pos->member.prev, typeof(*pos), member)) 458 | 459 | /** 460 | * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() 461 | * @pos: the type * to use as a start point 462 | * @head: the head of the list 463 | * @member: the name of the list_struct within the struct. 464 | * 465 | * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). 466 | */ 467 | #define list_prepare_entry(pos, head, member) \ 468 | ((pos) ? : list_entry(head, typeof(*pos), member)) 469 | 470 | /** 471 | * list_for_each_entry_continue - continue iteration over list of given type 472 | * @pos: the type * to use as a loop cursor. 473 | * @head: the head for your list. 474 | * @member: the name of the list_struct within the struct. 475 | * 476 | * Continue to iterate over list of given type, continuing after 477 | * the current position. 478 | */ 479 | #define list_for_each_entry_continue(pos, head, member) \ 480 | for (pos = list_entry(pos->member.next, typeof(*pos), member); \ 481 | &pos->member != (head); \ 482 | pos = list_entry(pos->member.next, typeof(*pos), member)) 483 | 484 | /** 485 | * list_for_each_entry_continue_reverse - iterate backwards from the given point 486 | * @pos: the type * to use as a loop cursor. 487 | * @head: the head for your list. 488 | * @member: the name of the list_struct within the struct. 489 | * 490 | * Start to iterate over list of given type backwards, continuing after 491 | * the current position. 492 | */ 493 | #define list_for_each_entry_continue_reverse(pos, head, member) \ 494 | for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ 495 | &pos->member != (head); \ 496 | pos = list_entry(pos->member.prev, typeof(*pos), member)) 497 | 498 | /** 499 | * list_for_each_entry_from - iterate over list of given type from the current point 500 | * @pos: the type * to use as a loop cursor. 501 | * @head: the head for your list. 502 | * @member: the name of the list_struct within the struct. 503 | * 504 | * Iterate over list of given type, continuing from current position. 505 | */ 506 | #define list_for_each_entry_from(pos, head, member) \ 507 | for (; &pos->member != (head); \ 508 | pos = list_entry(pos->member.next, typeof(*pos), member)) 509 | 510 | /** 511 | * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry 512 | * @pos: the type * to use as a loop cursor. 513 | * @n: another type * to use as temporary storage 514 | * @head: the head for your list. 515 | * @member: the name of the list_struct within the struct. 516 | */ 517 | #define list_for_each_entry_safe(pos, n, head, member) \ 518 | for (pos = list_entry((head)->next, typeof(*pos), member), \ 519 | n = list_entry(pos->member.next, typeof(*pos), member); \ 520 | &pos->member != (head); \ 521 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 522 | 523 | /** 524 | * list_for_each_entry_safe_continue - continue list iteration safe against removal 525 | * @pos: the type * to use as a loop cursor. 526 | * @n: another type * to use as temporary storage 527 | * @head: the head for your list. 528 | * @member: the name of the list_struct within the struct. 529 | * 530 | * Iterate over list of given type, continuing after current point, 531 | * safe against removal of list entry. 532 | */ 533 | #define list_for_each_entry_safe_continue(pos, n, head, member) \ 534 | for (pos = list_entry(pos->member.next, typeof(*pos), member), \ 535 | n = list_entry(pos->member.next, typeof(*pos), member); \ 536 | &pos->member != (head); \ 537 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 538 | 539 | /** 540 | * list_for_each_entry_safe_from - iterate over list from current point safe against removal 541 | * @pos: the type * to use as a loop cursor. 542 | * @n: another type * to use as temporary storage 543 | * @head: the head for your list. 544 | * @member: the name of the list_struct within the struct. 545 | * 546 | * Iterate over list of given type from current point, safe against 547 | * removal of list entry. 548 | */ 549 | #define list_for_each_entry_safe_from(pos, n, head, member) \ 550 | for (n = list_entry(pos->member.next, typeof(*pos), member); \ 551 | &pos->member != (head); \ 552 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 553 | 554 | /** 555 | * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal 556 | * @pos: the type * to use as a loop cursor. 557 | * @n: another type * to use as temporary storage 558 | * @head: the head for your list. 559 | * @member: the name of the list_struct within the struct. 560 | * 561 | * Iterate backwards over list of given type, safe against removal 562 | * of list entry. 563 | */ 564 | #define list_for_each_entry_safe_reverse(pos, n, head, member) \ 565 | for (pos = list_entry((head)->prev, typeof(*pos), member), \ 566 | n = list_entry(pos->member.prev, typeof(*pos), member); \ 567 | &pos->member != (head); \ 568 | pos = n, n = list_entry(n->member.prev, typeof(*n), member)) 569 | 570 | /** 571 | * list_safe_reset_next - reset a stale list_for_each_entry_safe loop 572 | * @pos: the loop cursor used in the list_for_each_entry_safe loop 573 | * @n: temporary storage used in list_for_each_entry_safe 574 | * @member: the name of the list_struct within the struct. 575 | * 576 | * list_safe_reset_next is not safe to use in general if the list may be 577 | * modified concurrently (eg. the lock is dropped in the loop body). An 578 | * exception to this is if the cursor element (pos) is pinned in the list, 579 | * and list_safe_reset_next is called after re-taking the lock and before 580 | * completing the current iteration of the loop body. 581 | */ 582 | #define list_safe_reset_next(pos, n, member) \ 583 | n = list_entry(pos->member.next, typeof(*pos), member) 584 | 585 | /* 586 | * Double linked lists with a single pointer list head. 587 | * Mostly useful for hash tables where the two pointer list head is 588 | * too wasteful. 589 | * You lose the ability to access the tail in O(1). 590 | */ 591 | struct hlist_head { 592 | struct hlist_node *first; 593 | }; 594 | 595 | struct hlist_node { 596 | struct hlist_node *next, **pprev; 597 | }; 598 | 599 | #define HLIST_HEAD_INIT { .first = NULL } 600 | #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } 601 | #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) 602 | static inline void INIT_HLIST_NODE(struct hlist_node *h) 603 | { 604 | h->next = NULL; 605 | h->pprev = NULL; 606 | } 607 | 608 | static inline int hlist_unhashed(const struct hlist_node *h) 609 | { 610 | return !h->pprev; 611 | } 612 | 613 | static inline int hlist_empty(const struct hlist_head *h) 614 | { 615 | return !h->first; 616 | } 617 | 618 | static inline void __hlist_del(struct hlist_node *n) 619 | { 620 | struct hlist_node *next = n->next; 621 | struct hlist_node **pprev = n->pprev; 622 | *pprev = next; 623 | if (next) 624 | next->pprev = pprev; 625 | } 626 | 627 | static inline void hlist_del(struct hlist_node *n) 628 | { 629 | __hlist_del(n); 630 | n->next = LIST_POISON1; 631 | n->pprev = LIST_POISON2; 632 | } 633 | 634 | static inline void hlist_del_init(struct hlist_node *n) 635 | { 636 | if (!hlist_unhashed(n)) { 637 | __hlist_del(n); 638 | INIT_HLIST_NODE(n); 639 | } 640 | } 641 | 642 | static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) 643 | { 644 | struct hlist_node *first = h->first; 645 | n->next = first; 646 | if (first) 647 | first->pprev = &n->next; 648 | h->first = n; 649 | n->pprev = &h->first; 650 | } 651 | 652 | /* next must be != NULL */ 653 | static inline void hlist_add_before(struct hlist_node *n, 654 | struct hlist_node *next) 655 | { 656 | n->pprev = next->pprev; 657 | n->next = next; 658 | next->pprev = &n->next; 659 | *(n->pprev) = n; 660 | } 661 | 662 | static inline void hlist_add_after(struct hlist_node *n, 663 | struct hlist_node *next) 664 | { 665 | next->next = n->next; 666 | n->next = next; 667 | next->pprev = &n->next; 668 | 669 | if(next->next) 670 | next->next->pprev = &next->next; 671 | } 672 | 673 | /* after that we'll appear to be on some hlist and hlist_del will work */ 674 | static inline void hlist_add_fake(struct hlist_node *n) 675 | { 676 | n->pprev = &n->next; 677 | } 678 | 679 | /* 680 | * Move a list from one list head to another. Fixup the pprev 681 | * reference of the first entry if it exists. 682 | */ 683 | static inline void hlist_move_list(struct hlist_head *old, 684 | struct hlist_head *new) 685 | { 686 | new->first = old->first; 687 | if (new->first) 688 | new->first->pprev = &new->first; 689 | old->first = NULL; 690 | } 691 | 692 | #define hlist_entry(ptr, type, member) container_of(ptr,type,member) 693 | 694 | #define hlist_for_each(pos, head) \ 695 | for (pos = (head)->first; pos ; pos = pos->next) 696 | 697 | #define hlist_for_each_safe(pos, n, head) \ 698 | for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ 699 | pos = n) 700 | 701 | /** 702 | * hlist_for_each_entry - iterate over list of given type 703 | * @tpos: the type * to use as a loop cursor. 704 | * @pos: the &struct hlist_node to use as a loop cursor. 705 | * @head: the head for your list. 706 | * @member: the name of the hlist_node within the struct. 707 | */ 708 | #define hlist_for_each_entry(tpos, pos, head, member) \ 709 | for (pos = (head)->first; \ 710 | pos && \ 711 | ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ 712 | pos = pos->next) 713 | 714 | /** 715 | * hlist_for_each_entry_continue - iterate over a hlist continuing after current point 716 | * @tpos: the type * to use as a loop cursor. 717 | * @pos: the &struct hlist_node to use as a loop cursor. 718 | * @member: the name of the hlist_node within the struct. 719 | */ 720 | #define hlist_for_each_entry_continue(tpos, pos, member) \ 721 | for (pos = (pos)->next; \ 722 | pos && \ 723 | ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ 724 | pos = pos->next) 725 | 726 | /** 727 | * hlist_for_each_entry_from - iterate over a hlist continuing from current point 728 | * @tpos: the type * to use as a loop cursor. 729 | * @pos: the &struct hlist_node to use as a loop cursor. 730 | * @member: the name of the hlist_node within the struct. 731 | */ 732 | #define hlist_for_each_entry_from(tpos, pos, member) \ 733 | for (; pos && \ 734 | ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ 735 | pos = pos->next) 736 | 737 | /** 738 | * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry 739 | * @tpos: the type * to use as a loop cursor. 740 | * @pos: the &struct hlist_node to use as a loop cursor. 741 | * @n: another &struct hlist_node to use as temporary storage 742 | * @head: the head for your list. 743 | * @member: the name of the hlist_node within the struct. 744 | */ 745 | #define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ 746 | for (pos = (head)->first; \ 747 | pos && ({ n = pos->next; 1; }) && \ 748 | ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ 749 | pos = n) 750 | 751 | #endif 752 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : log.c 3 | * Description : 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-15 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "log.h" 14 | 15 | 16 | #if LOG_SWITCH 17 | static char *log_color[] = { 18 | [LOG_EMERG] = COLOR_EMERG, 19 | [LOG_ALERT] = COLOR_ALERT, 20 | [LOG_CRIT] = COLOR_CRIT, 21 | [LOG_ERR] = COLOR_ERR, 22 | [LOG_WARNING] = COLOR_WARNING, 23 | [LOG_NOTICE] = COLOR_NOTICE, 24 | [LOG_INFO] = COLOR_INFO, 25 | [LOG_DEBUG] = COLOR_DEBUG, 26 | }; 27 | #endif 28 | 29 | int log_msg(const char *fmt, ...) 30 | { 31 | int n = 0; 32 | #if LOG_SWITCH 33 | int level = DFL_LOG_LEVEL; 34 | va_list ap = NULL; 35 | const char *ptr = NULL; 36 | const char *log_fmt = NULL; 37 | 38 | /* Find log level. */ 39 | ptr = strstr(fmt, LOG_FMT); 40 | if (ptr) { 41 | log_fmt = LOG_FMT; 42 | ptr += strlen(LOG_FMT); 43 | } else { 44 | ptr = fmt; 45 | } 46 | if (*ptr == '<' && /* Found '<' */ 47 | *(ptr + 2) == '>' && /* Found '>' */ 48 | *(ptr + 1) >= '0' && 49 | *(ptr + 1) <= '7') { 50 | level = *(ptr + 1) - '0'; 51 | ptr += 3; 52 | } 53 | 54 | /* Log the message. */ 55 | if (level <= THRESHOLD_LOG_LEVEL) { 56 | va_start(ap, fmt); 57 | n += fprintf(stderr, "%s", log_color[level]); 58 | if (log_fmt) { 59 | n += vfprintf(stderr, log_fmt, ap); 60 | va_arg(ap, char *); /* Skip __FILE__ */ 61 | va_arg(ap, char *); /* Skip __FUNCTION__ */ 62 | va_arg(ap, int); /* Skip __LINE__ */ 63 | } 64 | n += vfprintf(stderr, ptr, ap); 65 | va_end(ap); 66 | } 67 | #endif 68 | return n; 69 | } 70 | 71 | void test_log_color(void) 72 | { 73 | #if LOG_SWITCH 74 | log_msg("hello world\n"); 75 | printd("Test for log color ^_^\n"); 76 | printd(EMERG"Test for log color ^_^\n"); 77 | printd(ALERT"Test for log color ^_^\n"); 78 | printd(CRIT"Test for log color ^_^\n"); 79 | printd(ERR"Test for log color ^_^\n"); 80 | printd(WARNING"Test for log color ^_^\n"); 81 | printd(NOTICE"Test for log color ^_^\n"); 82 | printd(INFO"Test for log color ^_^\n"); 83 | printd(DEBUG"Test for log color ^_^\n"); 84 | usleep(1); 85 | exit(1); 86 | #endif 87 | return; 88 | } 89 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : log.h 3 | * Description : 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-15 6 | ********************************************************************/ 7 | 8 | #ifndef __LOG_H__ 9 | #define __LOG_H__ 10 | 11 | #include 12 | 13 | 14 | #define LOG_SWITCH 0 /* Log on or off. */ 15 | #define LOG_COLOR 1 /* Log color or non-color. */ 16 | #define DFL_LOG_LEVEL LOG_DEBUG /* If not specify level, use the default one. */ 17 | #define THRESHOLD_LOG_LEVEL LOG_DEBUG /* Only log from [THRESHOLD_LOG_LEVEL, LOG_EMERG. */ 18 | 19 | /** 20 | * priorities/facilities are encoded into a single 32-bit quantity, where the 21 | * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility 22 | * (0-big number). Both the priorities and the facilities map roughly 23 | * one-to-one to strings in the syslogd(8) source code. This mapping is 24 | * included in this file. 25 | * 26 | * priorities (these are ordered) 27 | */ 28 | #define LOG_EMERG 0 /* system is unusable */ 29 | #define LOG_ALERT 1 /* action must be taken immediately */ 30 | #define LOG_CRIT 2 /* critical conditions */ 31 | #define LOG_ERR 3 /* error conditions */ 32 | #define LOG_WARNING 4 /* warning conditions */ 33 | #define LOG_NOTICE 5 /* normal but significant condition */ 34 | #define LOG_INFO 6 /* informational */ 35 | #define LOG_DEBUG 7 /* debug-level messages */ 36 | 37 | #define EMERG "<0>" 38 | #define ALERT "<1>" 39 | #define CRIT "<2>" 40 | #define ERR "<3>" 41 | #define WARNING "<4>" 42 | #define NOTICE "<5>" 43 | #define INFO "<6>" 44 | #define DEBUG "<7>" 45 | 46 | 47 | /* Color print. */ 48 | #if LOG_COLOR 49 | #define COLOR_EMERG "\033[1;31m*EMERG*\033[m" /* Highlight, red foreground */ 50 | #define COLOR_ALERT "\033[1;33m*ALERT*\033[m" /* Highlight, yellow foreground. */ 51 | #define COLOR_CRIT "\033[35m*CRIT*\033[m" /* Purple forground. */ 52 | #define COLOR_ERR "\033[31m*ERR*\033[m" /* Red foreground. */ 53 | #define COLOR_WARNING "\033[33m*WARNING*\033[m" /* Yellow foreground. */ 54 | #define COLOR_NOTICE "\033[36m*NOTICE*\033[m" /* Cyan foreground. */ 55 | #define COLOR_INFO "\033[34m*INFO*\033[m" /* Blue foreground. */ 56 | #define COLOR_DEBUG "\033[32m*DEBUG*\033[m" /* Green foreground. */ 57 | #else 58 | #define COLOR_NONE 59 | #define COLOR_EMERG "*EMERG*" 60 | #define COLOR_ALERT "*ALERT*" 61 | #define COLOR_CRIT "*CRIT*" 62 | #define COLOR_ERR "*ERR*" 63 | #define COLOR_WARNING "*WARNING*" 64 | #define COLOR_NOTICE "*NOTICE*" 65 | #define COLOR_INFO "*INFO*" 66 | #define COLOR_DEBUG "*DEBUG*" 67 | #endif 68 | 69 | 70 | 71 | #if LOG_SWITCH /* ================ OPEN log. ================ */ 72 | 73 | #define LOG_FMT "[%s, %s, %d] " 74 | 75 | /* Never use the function directly, `printd()` instead. */ 76 | int log_msg(const char *fmt, ...); 77 | 78 | /** 79 | * Usage: 80 | * printd(INFO "Hello %s!\n", "world"); 81 | * this is equal to following: 82 | * printd("<6>Hello %s!\n", "world"); 83 | * 84 | * If you haven't specified the log level, 85 | * then it makes the macro `DFL_LOG_LEVEL` as default. 86 | */ 87 | #define printd(fmt, args...) \ 88 | ({ \ 89 | log_msg(LOG_FMT fmt, \ 90 | __FILE__, __FUNCTION__, __LINE__, ##args); \ 91 | }) 92 | 93 | #define perrord(fmt) \ 94 | ({ \ 95 | log_msg(LOG_FMT fmt ": %s.\n", \ 96 | __FILE__, __FUNCTION__, __LINE__, strerror(errno)); \ 97 | }) \ 98 | 99 | /* Print pretty RTSP request message. */ 100 | #define print_req_msg(msg) \ 101 | ({ \ 102 | log_msg(INFO "\n\033[1;33;41m[Begin]===================== Request Message ========================[Begin]\033[0m\n" \ 103 | "\033[32m%s\033[0m" \ 104 | "\033[33m[End]==================================================================[End]\033[0m\n\n\n", msg); \ 105 | }) 106 | 107 | /* Notify when entering or leaving a thread. */ 108 | #define entering_thread() \ 109 | printd(INFO"Entering thread %s[pid: %u] ...\n", __FUNCTION__, getpid()) 110 | #define leaving_thread() \ 111 | printd(INFO"Leaving thread %s[pid: %u] ...\n", __FUNCTION__, getpid()) 112 | 113 | #else /* ================ CLOSE log. ================ */ 114 | #define printd(fmt, args...) 115 | #define perrord(fmt) 116 | #define print_req_msg(msg) 117 | #define leaving_thread() 118 | #define entering_thread() 119 | #endif 120 | 121 | void test_log_color(void); 122 | 123 | 124 | #endif /* __LOG_H__ */ 125 | 126 | -------------------------------------------------------------------------------- /src/rtcp.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtcp.c 3 | * Description : 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-12-14 6 | ********************************************************************/ 7 | 8 | #include "rtcp.h" 9 | 10 | 11 | int handle_rtcp_pkt(struct rtsp_sess *sessp) 12 | { 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /src/rtcp.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtcp.h 3 | * Description : 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-12-14 6 | ********************************************************************/ 7 | 8 | #ifndef __RTCP_H__ 9 | #define __RTCP_H__ 10 | 11 | 12 | struct rtsp_sess; 13 | int handle_rtcp_pkt(struct rtsp_sess *sessp); 14 | 15 | 16 | #endif /* __RTCP_H__ */ 17 | 18 | -------------------------------------------------------------------------------- /src/rtp.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtp.c 3 | * Description : Implementation of RTP transmission. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-12-07 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "rtsp_sess.h" 16 | #include "rtp.h" 17 | #include "delay_task.h" 18 | #include "sd_handler.h" 19 | #include "log.h" 20 | 21 | 22 | /* Update sequence number in RTP header. */ 23 | #define update_seq(seq) do { \ 24 | seq = htons(ntohs(seq) + 1); \ 25 | } while (0) 26 | 27 | /* Update timestamp in RTP header. */ 28 | #define update_ts(ts, media) do { \ 29 | if (media == MEDIA_TYPE_VIDEO) { \ 30 | ts = htonl(ntohl(ts) + 90000 / 25); \ 31 | } else if (media == MEDIA_TYPE_AUDIO) { \ 32 | } \ 33 | } while (0) 34 | 35 | static void init_rtp_hdr(struct rtp_hdr *hdrp, int media) 36 | { 37 | hdrp->v = 2; 38 | hdrp->p = 0; 39 | hdrp->x = 0; 40 | hdrp->cc = 0; 41 | hdrp->m = 0; 42 | hdrp->pt = (media == MEDIA_TYPE_VIDEO) ? 43 | RTP_PT_H264 : RTP_PT_PCMU; 44 | hdrp->seq = random(); 45 | hdrp->ts = random(); 46 | hdrp->ssrc = random(); 47 | return; 48 | } 49 | 50 | /** 51 | * Depacketize the NALU into small enough slices. 52 | * And packetize them into RTP packets. 53 | */ 54 | static int produce_rtp_pkt(struct rtsp_sess *sessp, enum media_type media, 55 | struct rtp_hdr *rtp_hdrp, struct rtp_pl *rtp_plp) 56 | { 57 | struct send_buf *sendp = NULL; 58 | char *ptr = NULL; 59 | char nalu_pt = 0; /* Payload type. */ 60 | int send_sz = 0; 61 | enum data_type data_type; 62 | struct intlvd intlvd = { 63 | .dollar = '$', 64 | .chn = sessp->rtp_rtcp[media].tcp.rtp_chn, 65 | }; 66 | 67 | data_type = (media == MEDIA_TYPE_VIDEO) ? DATA_TYPE_RTP_V_PKT : DATA_TYPE_RTP_A_PKT; 68 | sendp = create_send_buf(data_type, MAX_RTP_PKT_SZ); 69 | if (!sendp) { 70 | return -1; 71 | } 72 | 73 | ptr = sessp->intlvd_mode ? (sendp->buf + sizeof(intlvd)) : sendp->buf; 74 | 75 | memcpy(ptr, rtp_hdrp, sizeof(*rtp_hdrp)); 76 | 77 | ptr += sizeof(*rtp_hdrp); 78 | switch (rtp_hdrp->pt) { 79 | case RTP_PT_H264: 80 | nalu_pt = *((char *)rtp_plp) & 0x1F; /* First byte in struct rtp_pl & 0x1F */ 81 | switch (nalu_pt) { 82 | case 1 ... 23: /* Single NALU Packet. */ 83 | *ptr++ = rtp_plp->single.hdr; 84 | memcpy(ptr, rtp_plp->single.pl, rtp_plp->single.pl_sz); 85 | send_sz += rtp_plp->single.pl_sz + 86 | sizeof(rtp_plp->single.hdr); 87 | break; 88 | case NALU_PT_FU_A: 89 | *ptr++ = rtp_plp->fu_a.idr; 90 | *ptr++ = rtp_plp->fu_a.hdr; 91 | memcpy(ptr, rtp_plp->fu_a.pl, rtp_plp->fu_a.pl_sz); 92 | send_sz += rtp_plp->fu_a.pl_sz + 93 | sizeof(rtp_plp->fu_a.idr) + 94 | sizeof(rtp_plp->fu_a.hdr); 95 | break; 96 | case NALU_PT_FU_B: 97 | *ptr++ = rtp_plp->fu_b.idr; 98 | *ptr++ = rtp_plp->fu_b.hdr; 99 | *ptr++ = (char)(rtp_plp->fu_b.don >> 8); 100 | *ptr++ = (char)rtp_plp->fu_b.don; 101 | memcpy(ptr, rtp_plp->fu_b.pl, rtp_plp->fu_b.pl_sz); 102 | send_sz += rtp_plp->fu_b.pl_sz + 103 | sizeof(rtp_plp->fu_b.idr) + 104 | sizeof(rtp_plp->fu_b.hdr); 105 | break; 106 | default: 107 | printd("Unsupported or undefined NALU payload type[%d]\n", nalu_pt); 108 | return -1; 109 | } 110 | break; 111 | case RTP_PT_PCMA: 112 | case RTP_PT_PCMU: 113 | memcpy(ptr, rtp_plp->audio.pl, rtp_plp->audio.pl_sz); 114 | send_sz += rtp_plp->audio.pl_sz; 115 | break; 116 | default: 117 | printd("Unsupported or undefined RTP payload type[%d]\n", nalu_pt); 118 | return -1; 119 | } 120 | 121 | send_sz += sizeof(*rtp_hdrp); 122 | if (sessp->intlvd_mode) { 123 | intlvd.sz = htons(send_sz); 124 | send_sz += sizeof(intlvd); 125 | memcpy(sendp->buf, &intlvd, sizeof(intlvd)); 126 | } 127 | sendp->sz = send_sz; 128 | 129 | /* sendto(sessp->rtp_rtcp[media].udp.rtp_sd, sendp->buf, sendp->sz, 0, */ 130 | /* &sessp->rtp_rtcp[media].udp.rtp_sa, sizeof(struct sockaddr)); */ 131 | add_send_buf(sessp, sendp); 132 | return 0; 133 | } 134 | 135 | static int send_h264_nalu(struct rtsp_sess *sessp, 136 | char *nalu, unsigned int sz, int last) 137 | { 138 | struct rtp_hdr *rtp_hdrp = &sessp->rtp_rtcp[MEDIA_TYPE_VIDEO].rtp_hdr; 139 | struct rtp_pl rtp_pl; 140 | unsigned int left = sz; 141 | unsigned int num = 0; /* RTP packet number. */ 142 | int max_fu_sz = MAX_RTP_PL_SZ - 2; /* 2 = size of FU-A indicator and header. */ 143 | int pl_sz = 0; /* Payload size. */ 144 | char *ptr = nalu; 145 | int end = 0; /* Last FU fragment. */ 146 | int i = 0; 147 | 148 | if (sz <= MAX_RTP_PL_SZ) { 149 | rtp_hdrp->m = last; 150 | rtp_pl.single.hdr = nalu[0]; 151 | rtp_pl.single.pl = nalu + 1; 152 | rtp_pl.single.pl_sz = sz - 1; 153 | produce_rtp_pkt(sessp, MEDIA_TYPE_VIDEO, rtp_hdrp, &rtp_pl); 154 | update_seq(rtp_hdrp->seq); 155 | } else { 156 | /* Skip header of NALU. */ 157 | left--; 158 | ptr++; 159 | 160 | /* Calculate RTP packet number. */ 161 | num = (left / max_fu_sz) + ((left % max_fu_sz) != 0); 162 | 163 | rtp_hdrp->m = 0; 164 | for (i = 0; i < num; i++) { 165 | end = (i == num - 1); 166 | /* RTP header */ 167 | rtp_hdrp->m = end ? last : 0; 168 | 169 | /* FU indicator: data[0] = F | NRI | TYPE. */ 170 | rtp_pl.fu_a.idr = (nalu[0] & 0xF0) | (nalu[0] & 0x60) | NALU_PT_FU_A; 171 | 172 | /* FU header: data[1] = S | E | R | TYPE, R = 0. */ 173 | rtp_pl.fu_a.hdr = (i ? 0 : 0x80) | (end ? 0x40 : 0) | (nalu[0] & 0x1F); 174 | 175 | pl_sz = end ? left : max_fu_sz; 176 | rtp_pl.fu_a.pl = ptr; 177 | rtp_pl.fu_a.pl_sz = pl_sz; 178 | produce_rtp_pkt(sessp, MEDIA_TYPE_VIDEO, rtp_hdrp, &rtp_pl); 179 | update_seq(rtp_hdrp->seq); 180 | 181 | ptr += pl_sz; 182 | left -= pl_sz; 183 | } 184 | } 185 | 186 | if (last) { 187 | update_ts(rtp_hdrp->ts, MEDIA_TYPE_VIDEO); 188 | } 189 | 190 | return 0; 191 | } 192 | 193 | static int send_video_frm(struct rtsp_sess *sessp, 194 | char *fb, unsigned int sz) 195 | { 196 | char *end = fb + sz; 197 | char *ptr = fb; 198 | unsigned int nalu_sz = 0; 199 | int last = 0; /* Last NALU of the frame. */ 200 | 201 | while (ptr != end) { 202 | ptr = skip_nalu_start_code(ptr, end); 203 | if (!ptr) { 204 | break; 205 | } 206 | 207 | #if 0 208 | /* We've send sps & pps NALU to RTSP client 209 | * in sdp when responsing RTSP method 'DESCRIBE', 210 | * needn't repeat this again. */ 211 | if ((ptr[0] & 0x1F) == NALU_TYPE_SPS || 212 | (ptr[0] & 0x1F) == NALU_TYPE_PPS) { 213 | continue; 214 | } 215 | #endif 216 | 217 | nalu_sz = get_nalu_sz(ptr, end); 218 | last = (ptr + nalu_sz == end) ? 1 : 0; 219 | send_h264_nalu(sessp, ptr, nalu_sz, last); 220 | ptr += nalu_sz; 221 | } 222 | 223 | return 0; 224 | } 225 | 226 | static int send_audio_frm(struct rtsp_sess *sessp, 227 | char *fb, unsigned int sz) 228 | { 229 | printd("Send audio frame hasn't been implemented yet!\n"); 230 | return 0; 231 | } 232 | 233 | /** 234 | * Prefetch one frame which is pure data encoded in H.264, 235 | * with NALU start code. 236 | */ 237 | int prefetch_frm(struct rtsp_sess *sessp) 238 | { 239 | int next_delay = 0; 240 | struct frm_info *frmp = &sessp->strm.frm_info; 241 | 242 | // elapsed_time(); 243 | 244 | /* Wait until RTSP state is RTSP_STATE_PLAYING, 245 | * and last response message has been add to send buffer queue. */ 246 | if (sessp->rtsp_state != RTSP_STATE_PLAYING || 247 | sessp->handling_state != HANDLING_STATE_INIT) { 248 | return -1; 249 | } 250 | 251 | /* Get frame and send it out. (Actually, just add to the send queue.) */ 252 | if (sessp->strm.get_frm(&sessp->strm.strm_info, &sessp->strm.frm_info) < 0) { 253 | /* Retry 5ms later. */ 254 | next_delay = 5 * 1000; 255 | } else { 256 | switch (frmp->frm_type) { 257 | case FRM_TYPE_I: 258 | case FRM_TYPE_B: 259 | case FRM_TYPE_P: 260 | send_video_frm(sessp, frmp->frm_buf, frmp->frm_sz); 261 | break; 262 | case FRM_TYPE_A: 263 | send_audio_frm(sessp, frmp->frm_buf, frmp->frm_sz); 264 | break; 265 | default: 266 | printd("Unsupport frame type[%d]!\n"); 267 | return -1; 268 | } 269 | /* Do this again after 30ms later. */ 270 | next_delay = 10 * 1000; 271 | } 272 | 273 | return next_delay; 274 | } 275 | 276 | /** 277 | * Setup interleaved mode. 278 | * Just initialize the intlvd struct in the beginning of RTP packet. 279 | */ 280 | static int setup_intlvd_mode(struct rtsp_sess *sessp) 281 | { 282 | struct rtsp_req *req = sessp->req; 283 | struct rtsp_resp *resp = sessp->resp; 284 | int media = req->media; 285 | 286 | resp->resp_hdr.transport.rtp_chn = req->req_hdr.transport.rtp_chn; 287 | resp->resp_hdr.transport.rtcp_chn = req->req_hdr.transport.rtcp_chn; 288 | 289 | sessp->rtp_rtcp[media].tcp.rtp_chn = resp->resp_hdr.transport.rtp_chn; 290 | sessp->rtp_rtcp[media].tcp.rtcp_chn = resp->resp_hdr.transport.rtcp_chn; 291 | 292 | return 0; 293 | } 294 | 295 | /** 296 | * Setup non-interleaved mode. 297 | * Create two UDP sockets for RTSP server side, 298 | * then bind them to the local ports. 299 | * Generally, the two local ports are continuous number pair, 300 | * first port for RTP, 301 | * and second port for RTCP. 302 | */ 303 | static int setup_non_intlvd_mode(struct rtsp_sess *sessp) 304 | { 305 | int first_sd = -1; 306 | int second_sd = -1; 307 | unsigned short first_port = 0; 308 | unsigned short second_port = 0; 309 | struct sockaddr_in rtsp_sa; 310 | struct sockaddr_in sa; /* Use for creating RTP & RTCP socket. */ 311 | socklen_t salen = 0; 312 | struct rtsp_req *req = sessp->req; 313 | struct rtsp_resp *resp = sessp->resp; 314 | int media = req->media; 315 | struct rtp_rtcp *rtp_rtcp = &sessp->rtp_rtcp[media]; 316 | 317 | memset(&sa, 0, sizeof(sa)); 318 | sa.sin_family = AF_INET; 319 | sa.sin_addr.s_addr = htonl(INADDR_ANY); 320 | 321 | /* Create first socket and get first port. */ 322 | if ((first_sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 323 | perrord(ERR "Create first UDP socket error"); 324 | goto err; 325 | } 326 | if (bind(first_sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 327 | perrord(ERR "Bind first UDP port error"); 328 | goto err; 329 | } 330 | salen = sizeof(sa); 331 | if (getsockname(first_sd, &sa, &salen) < 0) { 332 | perrord(ERR "Getsockname of first UDP socket error"); 333 | goto err; 334 | } 335 | first_port = ntohs(sa.sin_port); 336 | second_port = (first_port % 2) ? (first_port - 1) : (first_port + 1); 337 | 338 | /* Create second socket and get second port. */ 339 | sa.sin_port = htons(second_port); 340 | if ((second_sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 341 | perrord(ERR "Create second UDP socket error"); 342 | goto err; 343 | } 344 | if (bind(second_sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 345 | if (errno == EADDRINUSE) { /* Address already in use. */ 346 | sa.sin_port = 0; /* Give up to use continuous number. */ 347 | if (bind(second_sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 348 | perrord(ERR "Bind second UDP sockaddr error"); 349 | goto err; 350 | } 351 | } else { 352 | perrord(ERR "Bind second UDP sockaddr error"); 353 | goto err; 354 | } 355 | } 356 | if (getsockname(second_sd, &sa, &salen) < 0 ) { 357 | perrord(ERR "Getsockname of second UDP socket error"); 358 | goto err; 359 | } 360 | second_port = ntohs(sa.sin_port); 361 | 362 | /* Assign UDP socket to server for RTP & RTCP. */ 363 | if (first_port % 2) { /* Odd port. */ 364 | rtp_rtcp->udp.rtp_sd = second_sd; 365 | rtp_rtcp->udp.rtcp_sd = first_sd; 366 | } else { /* Even port. */ 367 | rtp_rtcp->udp.rtp_sd = first_sd; 368 | rtp_rtcp->udp.rtcp_sd = second_sd; 369 | } 370 | 371 | /* Add rtp_sd & rtcp_sd to epoll events. */ 372 | if ((monitor_sd_event(rtp_rtcp->udp.rtp_sd, RTP_SD_DFL_EV) < 0) || 373 | (monitor_sd_event(rtp_rtcp->udp.rtcp_sd, RTCP_SD_DFL_EV) < 0)) { 374 | printd(ERR "monitor rtp_sd or rtcp_sd handler failed!\n"); 375 | goto err; 376 | } 377 | 378 | /* Register RTP & RTCP sd handler. */ 379 | if ((register_sd_handler(rtp_rtcp->udp.rtp_sd, handle_rtp_sd, sessp) < 0) || 380 | (register_sd_handler(rtp_rtcp->udp.rtcp_sd, handle_rtcp_sd, sessp) < 0)) { 381 | printd(ERR "register rtp_sd or rtcp_sd handler failed!\n"); 382 | goto err; 383 | } 384 | 385 | /* Get UDP socket address information of client. */ 386 | salen = sizeof(sa); 387 | if (getpeername(sessp->rtsp_sd, &rtsp_sa, &salen) < 0) { 388 | perrord(ERR "Getpeername of RTSP client socket"); 389 | goto err; 390 | } 391 | memcpy(&rtp_rtcp->udp.rtp_sa, &rtsp_sa, sizeof(sa)); 392 | memcpy(&rtp_rtcp->udp.rtcp_sa, &rtsp_sa, sizeof(sa)); 393 | ((struct sockaddr_in *)&rtp_rtcp->udp.rtp_sa)->sin_port = 394 | htons(req->req_hdr.transport.rtp_cli_port); 395 | ((struct sockaddr_in *)&rtp_rtcp->udp.rtcp_sa)->sin_port = 396 | htons(req->req_hdr.transport.rtcp_cli_port); 397 | 398 | /* Prepare RTSP response. */ 399 | resp->resp_hdr.transport.rtp_cli_port = req->req_hdr.transport.rtp_cli_port; 400 | resp->resp_hdr.transport.rtcp_cli_port = req->req_hdr.transport.rtcp_cli_port; 401 | resp->resp_hdr.transport.rtp_srv_port = (first_port % 2) ? second_port : first_port; 402 | resp->resp_hdr.transport.rtcp_srv_port = (first_port % 2) ? first_port : second_port; 403 | 404 | return 0; 405 | 406 | err: 407 | close(first_sd); 408 | close(second_sd); 409 | return -1; 410 | } 411 | 412 | int create_rtp_sess(struct rtsp_sess *sessp) 413 | { 414 | int media = sessp->req->media; /* Media type: video, audio. */ 415 | struct rtp_rtcp *rtp_rtcp = &sessp->rtp_rtcp[media]; 416 | 417 | if (sessp->rtsp_state == RTSP_STATE_INIT) { 418 | sessp->sess_id = random64(); 419 | sessp->resp->resp_hdr.sess_id = sessp->sess_id; 420 | } 421 | 422 | init_rtp_hdr(&rtp_rtcp->rtp_hdr, media); 423 | 424 | if (sessp->intlvd_mode) { 425 | if (setup_intlvd_mode(sessp) < 0) { 426 | return -1; 427 | } 428 | } else { 429 | if (setup_non_intlvd_mode(sessp) < 0) { 430 | return -1; 431 | } 432 | } 433 | 434 | if (sessp->strm.start_strm(&sessp->strm.strm_info) < 0) { 435 | return -1; 436 | } 437 | 438 | return 0; 439 | } 440 | 441 | void destroy_rtp_sess(struct rtsp_sess *sessp) 442 | { 443 | int i = 0; 444 | 445 | sessp->strm.stop_strm(&sessp->strm.strm_info); 446 | 447 | if (sessp->intlvd_mode) { 448 | /* TODO: Is there anything to be done? */ 449 | } else { 450 | for (i = 0; i < 2; i++) { 451 | deregister_sd_handler(sessp->rtp_rtcp[i].udp.rtp_sd); 452 | close(sessp->rtp_rtcp[i].udp.rtp_sd); 453 | sessp->rtp_rtcp[i].udp.rtp_sd = -1; 454 | 455 | deregister_sd_handler(sessp->rtp_rtcp[i].udp.rtcp_sd); 456 | close(sessp->rtp_rtcp[i].udp.rtcp_sd); 457 | sessp->rtp_rtcp[i].udp.rtcp_sd = -1; 458 | } 459 | } 460 | return; 461 | } 462 | -------------------------------------------------------------------------------- /src/rtp.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtp.h 3 | * Description : Implementation of RTP transmission. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-12-07 6 | ********************************************************************/ 7 | 8 | #ifndef __RTP_H__ 9 | #define __RTP_H__ 10 | 11 | 12 | /* NALU types */ 13 | enum { 14 | NALU_TYPE_SPS = 7, /* sequence parameter set */ 15 | NALU_TYPE_PPS = 8, /* picture parameter set */ 16 | }; 17 | 18 | /* Media type. */ 19 | enum media_type { 20 | MEDIA_TYPE_VIDEO, 21 | MEDIA_TYPE_AUDIO, 22 | }; 23 | 24 | /* RTP payload type. */ 25 | enum { 26 | RTP_PT_H264 = 96, 27 | RTP_PT_PCMU = 0, 28 | RTP_PT_PCMA = 8, 29 | }; 30 | 31 | /* NALU payload type. */ 32 | enum { 33 | // NALU_PT_SINGLE = 1...23, 34 | NALU_PT_FU_A = 28, 35 | NALU_PT_FU_B = 29, 36 | }; 37 | 38 | /* Interleaved header for transport(RTP over RTSP). */ 39 | struct intlvd { 40 | unsigned char dollar; 41 | unsigned char chn; 42 | unsigned short sz; 43 | }; 44 | 45 | /* RTP header. */ 46 | struct rtp_hdr { 47 | #ifdef BIGENDIAN 48 | unsigned char v:2; /* protocol version */ 49 | unsigned char p:1; /* padding flag */ 50 | unsigned char x:1; /* header extension flag */ 51 | unsigned char cc:4; /* CSRC count */ 52 | unsigned char m:1; /* marker bit */ 53 | unsigned char pt:7; /* payload type */ 54 | #else 55 | unsigned char cc:4; 56 | unsigned char x:1; 57 | unsigned char p:1; 58 | unsigned char v:2; 59 | unsigned char pt:7; 60 | unsigned char m:1; 61 | #endif 62 | unsigned short seq; /* sequence number */ 63 | unsigned int ts; /* timestamp */ 64 | unsigned int ssrc; /* synchronization source */ 65 | #if 0 66 | unsigned int csrc[2]; /* optional CSRC list */ 67 | #endif 68 | }; 69 | 70 | /* RTP payload format for single NALU. */ 71 | struct single { 72 | char hdr; 73 | char *pl; 74 | unsigned int pl_sz; 75 | }; 76 | 77 | /* RTP payload format for FU-A. */ 78 | struct fu_a { 79 | char idr; 80 | char hdr; 81 | char *pl; /* FU payload. */ 82 | unsigned int pl_sz; 83 | }; 84 | 85 | /* RTP payload format for FU-B. */ 86 | struct fu_b { 87 | char idr; 88 | char hdr; 89 | unsigned short don; /* Decoding order number(in network byte order). */ 90 | char *pl; /* FU payload. */ 91 | unsigned int pl_sz; 92 | }; 93 | 94 | /* RTP payload format for Audio. */ 95 | struct audio { 96 | char *pl; 97 | char pl_sz; 98 | }; 99 | 100 | /* RTP payload. */ 101 | struct rtp_pl { 102 | union { 103 | struct single single; 104 | struct fu_a fu_a; 105 | struct fu_b fu_b; 106 | struct audio audio; 107 | }; 108 | }; 109 | 110 | #define MAX_RTP_PKT_SZ 1440 111 | #define MAX_RTP_PL_SZ (MAX_RTP_PKT_SZ - sizeof(struct rtp_hdr) - sizeof(struct intlvd)) 112 | 113 | 114 | 115 | struct rtsp_sess; 116 | int create_rtp_sess(struct rtsp_sess *sessp); 117 | void destroy_rtp_sess(struct rtsp_sess *sessp); 118 | int prefetch_frm(struct rtsp_sess *sessp); 119 | 120 | 121 | #endif /* __RTP_H__ */ 122 | -------------------------------------------------------------------------------- /src/rtsp_method.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtsp_method.c 3 | * Description : Implementation of RTSP method handlers. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-27 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "list.h" 14 | #include "rtsp_srv.h" 15 | #include "rtsp_sess.h" 16 | #include "rtsp_parser.h" 17 | #include "delay_task.h" 18 | #include "util.h" 19 | #include "rtp.h" 20 | #include "log.h" 21 | 22 | 23 | #define CRLF "\r\n" 24 | 25 | /* RTSP methods we support now. */ 26 | static char *const supported_methods = 27 | "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER"; 28 | 29 | static char *const rtsp_method_token[] = { 30 | [RTSP_METHOD_OPTIONS] = "OPTIONS", 31 | [RTSP_METHOD_DESCRIBE] = "DESCRIBE", 32 | [RTSP_METHOD_SETUP] = "SETUP", 33 | [RTSP_METHOD_PLAY] = "PLAY", 34 | [RTSP_METHOD_PAUSE] = "PAUSE", 35 | [RTSP_METHOD_GET_PARAMETER] = "GET_PARAMETER", 36 | [RTSP_METHOD_SET_PARAMETER] = "SET_PARAMETER", 37 | [RTSP_METHOD_TEARDOWN] = "TEARDOWN", 38 | }; 39 | 40 | 41 | int get_rtsp_method(const char *str) 42 | { 43 | int i = 0; 44 | int method = -1; 45 | int num = sizeof(rtsp_method_token) / sizeof(*rtsp_method_token); 46 | 47 | for (i = 0; i < num; i++) { 48 | if (!strncmp(str, rtsp_method_token[i], strlen(rtsp_method_token[i]))) { 49 | method = i; 50 | break; 51 | } 52 | } 53 | return method; 54 | } 55 | 56 | char *rtsp_encodeBase64(char *buf, int buf_len, 57 | const unsigned char *src, int src_len) 58 | { 59 | #ifndef UINT_MAX 60 | #define UINT_MAX 0xFFFFFFFF 61 | #endif 62 | static const char b64[] = 63 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 64 | char *ret, *dst; 65 | unsigned i_bits = 0; 66 | int i_shift = 0; 67 | int bytes_remaining = src_len; 68 | 69 | if (src_len >= UINT_MAX / 4 || 70 | buf_len < src_len * 4 / 3 + 12) { 71 | return NULL; 72 | } 73 | ret = dst = buf; 74 | while (bytes_remaining) { 75 | i_bits = (i_bits << 8) + *src++; 76 | bytes_remaining--; 77 | i_shift += 8; 78 | 79 | do { 80 | *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; 81 | i_shift -= 6; 82 | } while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0)); 83 | } 84 | while ((dst - ret) & 3) { 85 | *dst++ = '='; 86 | } 87 | *dst = '\0'; 88 | 89 | return ret; 90 | } 91 | 92 | static char *make_sdp_info(struct rtsp_sess *sessp, struct frm_info *frmp) 93 | { 94 | /* Use this MACRO to move forward while filling the response message. */ 95 | #define MOVE_FORWARD() do { \ 96 | left -= strlen(line); \ 97 | if (left < 0) { \ 98 | printd("MOVE_FORWARD failed when parsing request.\n"); \ 99 | return NULL; \ 100 | } \ 101 | line += strlen(line); \ 102 | } while (0) 103 | 104 | static char sdp[2048]; 105 | char *line = NULL; 106 | unsigned int left = 0; 107 | char *ptr = NULL; 108 | char *end = NULL; 109 | char *b64_sps = NULL; 110 | char *b64_pps = NULL; 111 | char *sps = NULL; /* Begin of sequence parameter sets. */ 112 | char *pps = NULL; /* Begin of picture parameter sets. */ 113 | int nalu_type = 0; 114 | struct sockaddr_in sa; 115 | socklen_t salen = sizeof(sa); 116 | 117 | /* Get sps & pps information. */ 118 | ptr = frmp->frm_buf; 119 | end = ptr + frmp->frm_sz; 120 | while (!sps || !pps) { 121 | if (!(ptr = skip_nalu_start_code(ptr, end))) { 122 | printd("skip_nalu_start_code failed!\n"); 123 | return NULL; 124 | } 125 | nalu_type = ptr[0] & 0x1F; 126 | if (nalu_type == NALU_TYPE_SPS) { 127 | sps = ptr; 128 | continue; 129 | } 130 | if (nalu_type == NALU_TYPE_PPS) { 131 | pps = ptr; 132 | continue; 133 | } 134 | } 135 | 136 | /* Get IP address. */ 137 | memset(&sa, 0, sizeof(sa)); 138 | if (getsockname(sessp->rtsp_sd, (struct sockaddr *)&sa, &salen) < 0) { 139 | perrord(ERR "getsockname of rtsp_sd error"); 140 | return NULL; 141 | } 142 | 143 | /* Make up sdp information. */ 144 | memset(sdp, 0, sizeof(sdp)); 145 | line = sdp; 146 | left = sizeof(sdp); 147 | 148 | MOVE_FORWARD(); 149 | snprintf(line, left, CRLF); 150 | MOVE_FORWARD(); 151 | snprintf(line, left, "v=0"CRLF); 152 | MOVE_FORWARD(); 153 | snprintf(line, left, "o=- %ld %ld IN IP4 %s"CRLF, 154 | time(NULL), time(NULL) + 1, inet_ntoa(sa.sin_addr)); 155 | MOVE_FORWARD(); 156 | snprintf(line, left, "s=streamed by the ZMODO RTSP server"CRLF); 157 | MOVE_FORWARD(); 158 | snprintf(line, left, "t=0 0"CRLF); 159 | 160 | MOVE_FORWARD(); 161 | snprintf(line, left, "m=video 0 RTP/AVP %d"CRLF, RTP_PT_H264); 162 | MOVE_FORWARD(); 163 | snprintf(line, left, "a=rtpmap:%d H264/90000"CRLF, RTP_PT_H264); 164 | MOVE_FORWARD(); 165 | snprintf(line, left, "a=control:track%d"CRLF, MEDIA_TYPE_VIDEO); 166 | 167 | #if 0 168 | MOVE_FORWARD(); 169 | snprintf(line, left, "m=audio 0 RTP/AVP %d"CRLF, 0); 170 | MOVE_FORWARD(); 171 | snprintf(line, left, "a=rtpmap:%d %s/8000/1"CRLF, RTP_PT_PCMU, "PCMU"); 172 | MOVE_FORWARD(); 173 | snprintf(line, left, "a=control:track%d"CRLF, MEDIA_TYPE_AUDIO); 174 | #endif 175 | 176 | MOVE_FORWARD(); 177 | snprintf(line, left, "a=range:npt=0-"CRLF); 178 | 179 | MOVE_FORWARD(); 180 | if ((b64_sps = encode_base64(sps, get_nalu_sz(sps, end))) < 0) { 181 | return NULL; 182 | } 183 | if ((b64_pps = encode_base64(pps, get_nalu_sz(pps, end))) < 0) { 184 | freez(b64_sps); 185 | return NULL; 186 | } 187 | snprintf(line, left, "a=fmtp:%d packetization-mode=1;" 188 | "profile-level-id=%02X%02X%02X;" 189 | "sprop-parameter-sets=%s,%s"CRLF, 190 | RTP_PT_H264, 191 | (unsigned char)sps[1], (unsigned char)sps[2], (unsigned char)sps[3], 192 | b64_sps, b64_pps); 193 | 194 | freez(b64_sps); 195 | freez(b64_pps); 196 | return sdp; 197 | 198 | #undef MOVE_FORWARD 199 | } 200 | 201 | void handle_method_options(struct rtsp_sess *sessp) 202 | { 203 | sessp->resp->resp_hdr.public = strndup(supported_methods, strlen(supported_methods)); 204 | sessp->handling_state = HANDLING_STATE_HANDLED; 205 | return; 206 | } 207 | 208 | void handle_method_describe(struct rtsp_sess *sessp) 209 | { 210 | struct rtsp_resp *resp = sessp->resp; 211 | if (sessp->handling_state != HANDLING_STATE_RETRY) { 212 | if (sessp->strm.start_strm(&sessp->strm.strm_info) < 0) { 213 | sessp->handling_state = HANDLING_STATE_FAILED; 214 | goto rtn; 215 | } 216 | } 217 | 218 | if (sessp->strm.get_frm(&sessp->strm.strm_info, &sessp->strm.frm_info) < 0) { 219 | sessp->handling_state = HANDLING_STATE_FAILED; 220 | goto rtn; 221 | } 222 | 223 | /* Retry to get frame, until I frame appears. */ 224 | if (sessp->strm.frm_info.frm_type != FRM_TYPE_I) { 225 | sessp->handling_state = HANDLING_STATE_RETRY; 226 | goto rtn; 227 | } 228 | 229 | /* Make up SDP information. */ 230 | resp->resp_body.sdp_info = make_sdp_info(sessp, &sessp->strm.frm_info); 231 | if (!resp->resp_body.sdp_info) { 232 | sessp->handling_state = HANDLING_STATE_FAILED; 233 | goto rtn; 234 | } 235 | 236 | sessp->handling_state = HANDLING_STATE_HANDLED; 237 | 238 | rtn: 239 | if (sessp->handling_state != HANDLING_STATE_RETRY) { 240 | sessp->strm.stop_strm(&sessp->strm.strm_info); 241 | } 242 | return; 243 | } 244 | 245 | void handle_method_setup(struct rtsp_sess *sessp) 246 | { 247 | if (create_rtp_sess(sessp) < 0) { 248 | sessp->handling_state = HANDLING_STATE_FAILED; 249 | return; 250 | } 251 | 252 | if (sessp->rtsp_state != RTSP_STATE_PLAYING) { 253 | sessp->rtsp_state = RTSP_STATE_READY; 254 | } 255 | sessp->handling_state = HANDLING_STATE_HANDLED; 256 | return; 257 | } 258 | 259 | void handle_method_play(struct rtsp_sess *sessp) 260 | { 261 | struct rtsp_resp *resp = sessp->resp; 262 | if (sessp->rtsp_state == RTSP_STATE_INIT) { 263 | resp->resp_line.status_code = METHOD_NOT_VALID_IN_THIS_STATE; 264 | sessp->handling_state = HANDLING_STATE_ERROR; 265 | return; 266 | } else { 267 | sessp->rtsp_state = RTSP_STATE_PLAYING; 268 | } 269 | create_delay_task((delay_task_proc_t)prefetch_frm, sessp, 0); 270 | sessp->handling_state = HANDLING_STATE_HANDLED; 271 | return; 272 | } 273 | 274 | void handle_method_pause(struct rtsp_sess *sessp) 275 | { 276 | if (sessp->rtsp_state == RTSP_STATE_PLAYING) { 277 | sessp->rtsp_state = RTSP_STATE_READY; 278 | } 279 | 280 | sessp->handling_state = HANDLING_STATE_HANDLED; 281 | return; 282 | } 283 | 284 | void handle_method_get_parameter(struct rtsp_sess *sessp) 285 | { 286 | sessp->handling_state = HANDLING_STATE_HANDLED; 287 | return; 288 | } 289 | 290 | void handle_method_set_parameter(struct rtsp_sess *sessp) 291 | { 292 | sessp->handling_state = HANDLING_STATE_HANDLED; 293 | return; 294 | } 295 | 296 | void handle_method_teardown(struct rtsp_sess *sessp) 297 | { 298 | sessp->rtsp_state = RTSP_STATE_INIT; 299 | 300 | /* Destroy RTSP session 10ms later, after handling current method. */ 301 | create_delay_task((delay_task_proc_t)destroy_rtsp_sess, sessp, 10 * 1000); 302 | 303 | sessp->handling_state = HANDLING_STATE_HANDLED; 304 | return; 305 | } 306 | 307 | /** 308 | * Allocate resource to prepare handling RTSP method. 309 | */ 310 | static void handling_method_init(struct rtsp_sess *sessp) 311 | { 312 | struct rtsp_req *req = NULL; 313 | struct rtsp_resp *resp = NULL; 314 | 315 | if (sessp->handling_state == HANDLING_STATE_RETRY) { 316 | return; 317 | } 318 | 319 | /* Request. */ 320 | req = mallocz(sizeof(*req)); 321 | if (!req) { 322 | printd("Allocate memory for RTSP request failed!\n"); 323 | goto err; 324 | } 325 | 326 | /* Response. */ 327 | resp = mallocz(sizeof(*resp)); 328 | if (!resp) { 329 | printd("Allocate memory for RTSP response failed!\n"); 330 | goto err; 331 | } 332 | /* Initialize the response struct. */ 333 | resp->resp_line.status_code = OK; 334 | 335 | sessp->req = req; 336 | sessp->resp = resp; 337 | sessp->handling_state = HANDLING_STATE_READY; 338 | return; 339 | 340 | err: 341 | freez(req); 342 | freez(resp); 343 | sessp->handling_state = HANDLING_STATE_FAILED; 344 | return; 345 | } 346 | 347 | /** 348 | * Free resource using in handling RTSP method. 349 | */ 350 | static void handling_method_done(struct rtsp_sess *sessp) 351 | { 352 | struct rtsp_req *req = sessp->req; 353 | struct rtsp_resp *resp = sessp->resp; 354 | 355 | if (sessp->handling_state == HANDLING_STATE_RETRY) { 356 | return; 357 | } 358 | 359 | /* Request. */ 360 | freez(req->req_line.uri); 361 | freez(req->req_line.ver); 362 | freez(req); 363 | 364 | /* Response. */ 365 | freez(resp->resp_line.ver); 366 | freez(resp->resp_hdr.public); 367 | freez(resp->resp_hdr.server); 368 | freez(resp); 369 | 370 | sessp->handling_state = HANDLING_STATE_INIT; 371 | return; 372 | } 373 | 374 | int handle_rtsp_req(struct rtsp_sess *sessp) 375 | { 376 | handling_method_init(sessp); 377 | 378 | /* Parse request message. */ 379 | parse_rtsp_req(sessp); 380 | 381 | /* Run the RTSP state machine. */ 382 | run_rtsp_state_machine(sessp); 383 | 384 | /* Produce RTSP response message and put it into send buffer queue. */ 385 | produce_rtsp_resp(sessp); 386 | 387 | handling_method_done(sessp); 388 | 389 | if (sessp->handling_state == HANDLING_STATE_RETRY) { 390 | /* Usually generate one frame per 40ms, when frame rate = 25f/s. */ 391 | return 40 * 1000; 392 | } 393 | 394 | /* Task finished! */ 395 | return -1; 396 | } 397 | -------------------------------------------------------------------------------- /src/rtsp_method.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : method handlers.h 3 | * Description : Implementation of RTSP method handlers. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-27 6 | ********************************************************************/ 7 | 8 | #ifndef __RTSP_METHOD_H__ 9 | #define __RTSP_METHOD_H__ 10 | 11 | 12 | #include "list.h" 13 | 14 | 15 | /* SDP information. */ 16 | struct sdp_info { 17 | struct list_head entry; /* Entry of sdp list. */ 18 | char type; /* Ascii code, type of sdp info. */ 19 | char *value; /* String, value of sdp info. */ 20 | }; 21 | 22 | 23 | void handle_method_options(struct rtsp_sess *sessp); 24 | void handle_method_describe(struct rtsp_sess *sessp); 25 | void handle_method_setup(struct rtsp_sess *sessp); 26 | void handle_method_play(struct rtsp_sess *sessp); 27 | void handle_method_pause(struct rtsp_sess *sessp); 28 | void handle_method_get_parameter(struct rtsp_sess *sessp); 29 | void handle_method_set_parameter(struct rtsp_sess *sessp); 30 | void handle_method_teardown(struct rtsp_sess *sessp); 31 | 32 | int handle_rtsp_req(struct rtsp_sess *sessp); 33 | 34 | 35 | 36 | #endif /* __RTSP_METHOD_H__ */ 37 | -------------------------------------------------------------------------------- /src/rtsp_parser.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : parser.c 3 | * Description : Parse the RTSP requeset message. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-23 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "list.h" 13 | #include "util.h" 14 | #include "log.h" 15 | #include "rtsp_parser.h" 16 | #include "rtsp_sess.h" 17 | #include "rtsp_srv.h" 18 | 19 | 20 | #define MAX_URI_SZ 256 21 | 22 | static const char *get_next_word(const char *str) 23 | { 24 | const char *word = NULL; 25 | if (!str) { 26 | return NULL; 27 | } 28 | 29 | while (*str) { 30 | if (*str == ' ' && *(str + 1) != ' ') { 31 | word = str + 1; 32 | break; 33 | } else if (*str == '\r' || *str == '\n') { 34 | break; 35 | } 36 | str++; 37 | } 38 | return word; 39 | } 40 | 41 | /** 42 | * Return the next pointer. 43 | * 44 | * Refer to RFC2326 section 4: 45 | * Lines are terminated by CRLF, but we should be prepared to 46 | * also interpret CR and LF by themselves as line terminators. 47 | */ 48 | static const char *get_next_line(const char *str) 49 | { 50 | const char *line = NULL; 51 | if (!str) { 52 | return NULL; 53 | } 54 | 55 | while (*str) { 56 | if ((*str == '\r' && *(str + 1) == '\n' && *(str + 2) == '\r' && *(str + 3) == '\n') || 57 | (*str == '\r' && *(str + 1) == '\r') || 58 | (*str == '\n' && *(str + 1) == '\n')) { 59 | break; 60 | } else if (*str == '\r' && *(str + 1) == '\n') { 61 | line = str + 2; 62 | break; 63 | } else if (*str == '\r' || *str == '\n') { 64 | line = str + 1; 65 | break; 66 | } 67 | str++; 68 | } 69 | return line; 70 | } 71 | 72 | /** 73 | * Count until a white space appears. 74 | */ 75 | static int get_word_sz(const char *word) 76 | { 77 | int sz = 0; 78 | 79 | /* Count util hitting ' ' || '\r' || '\n'. */ 80 | for (; *word && *word != ' ' && *word != '\r' && *word != '\n'; word++, sz++); 81 | return sz; 82 | } 83 | 84 | static void parse_rtsp_uri(struct rtsp_sess *sessp, struct rtsp_req *req, struct rtsp_resp *resp) 85 | { 86 | struct strm_info *strmp = &sessp->strm.strm_info; 87 | const char *uri = req->req_line.uri; 88 | char file_name[MAX_URI_SZ] = {0}; 89 | int chn_no = 0; 90 | int chn_type = 0; 91 | int media_type = 0; 92 | 93 | /* URI check. */ 94 | if (strncasecmp(uri, "rtsp://", strlen("rtsp://"))) { 95 | printd("Illegal RTSP uri: [%s]\n", req->req_line.uri); 96 | resp->resp_line.status_code = NOT_FOUND; 97 | return; 98 | } 99 | if (strlen(uri) >= MAX_URI_SZ) { 100 | resp->resp_line.status_code = REQUEST_URI_TOO_LARGE; 101 | return; 102 | } 103 | 104 | /* Lookup stream resource */ 105 | printd("uri: %s\n", uri); 106 | if (sscanf(uri, "rtsp://%*[^/]/av%d_%d", &chn_no, &chn_type) == 2) { 107 | printd(INFO "chn_no = %d, chn_type = %d\n", chn_no, chn_type); 108 | /* Sanity check. */ 109 | if (chn_no < 0 || chn_no >= MAX_CHN_NUM || 110 | chn_type < 0 || chn_type > 1) { 111 | printd(WARNING "Invalid range.\n"); 112 | resp->resp_line.status_code = INVALID_RANGE; 113 | return; 114 | } 115 | strmp->chn_no = chn_no; 116 | strmp->chn_type = chn_type ? CHN_TYPE_MAIN : CHN_TYPE_MINOR; 117 | } else if (sscanf(uri, "rtsp://%*[^/]/%*[^/]/track%d", &media_type) == 1) { 118 | if (media_type < 0 || media_type > 1) { 119 | printd(WARNING "Invalid range.\n"); 120 | resp->resp_line.status_code = INVALID_RANGE; 121 | return; 122 | } 123 | req->media = media_type ? MEDIA_TYPE_AUDIO : MEDIA_TYPE_VIDEO; 124 | } else if (sscanf(uri, "rtsp://%*[^/]/%s", file_name) == 1) { 125 | /* Check whether the request file exist. */ 126 | if (access(file_name, F_OK) < 0) { 127 | perrord(ERR "Access file error"); 128 | resp->resp_line.status_code = INVALID_RANGE; 129 | return; 130 | } 131 | strmp->file_name = strndup(file_name, strlen(file_name)); 132 | } else if (sscanf(uri, "rtsp://%*[^/]") == 0) { 133 | printd(INFO "Default resource.\n"); 134 | /* Default resource. */ 135 | strmp->chn_no = 0; 136 | strmp->chn_type = 0; 137 | } else { 138 | printd(WARNING "Invalid range.\n"); 139 | resp->resp_line.status_code = INVALID_RANGE; 140 | return; 141 | } 142 | 143 | return; 144 | } 145 | 146 | /* 147 | * Parse RTSP request line. 148 | */ 149 | static void parse_req_line(struct rtsp_sess *sessp, const char *line) 150 | { 151 | struct rtsp_req *req = sessp->req; 152 | struct rtsp_resp *resp = sessp->resp; 153 | const char *ptr = line; 154 | 155 | /* Get method ID. */ 156 | req->req_line.method = get_rtsp_method(ptr); 157 | if (req->req_line.method == -1) { 158 | printd("RTSP method is not implemented!\n"); 159 | resp->resp_line.status_code = NOT_IMPLEMENTED; 160 | return; 161 | } 162 | 163 | /* Get and check RTSP uri. */ 164 | ptr = get_next_word(ptr); 165 | if (!ptr) { 166 | sessp->handling_state = HANDLING_STATE_FAILED; 167 | printd("get_next_word failed!\n"); 168 | return; 169 | } 170 | req->req_line.uri = strndup(ptr, get_word_sz(ptr)); 171 | parse_rtsp_uri(sessp, req, resp); 172 | 173 | /* Check RTSP version. */ 174 | ptr = get_next_word(ptr); 175 | if (!ptr) { 176 | sessp->handling_state = HANDLING_STATE_FAILED; 177 | printd("get_next_word failed!\n"); 178 | return; 179 | } 180 | req->req_line.ver = strndup(ptr, get_word_sz(ptr)); 181 | if (strcmp(req->req_line.ver, RTSP_VERSION)) { 182 | printd("Unsupported RTSP version: [%s]!\n", req->req_line.ver); 183 | resp->resp_line.status_code = VERSION_NOT_SUPPORTED; 184 | return; 185 | } 186 | 187 | return; 188 | } 189 | 190 | static int parse_hdr_transport(struct rtsp_sess *sessp, const char *line) 191 | { 192 | const char *ptr = line; 193 | struct rtsp_req *req = sessp->req; 194 | 195 | if (strncmp(ptr, "Transport: ", strlen("Transport: "))) { 196 | return -1; /* Not a transport field! */ 197 | } 198 | ptr += strlen("Transport: "); 199 | 200 | while (*ptr) { 201 | if (!strncmp(ptr, "RTP/AVP/TCP", strlen("RTP/AVP/TCP"))) { 202 | sessp->intlvd_mode = 1; 203 | } else if (!strncmp(ptr, "RTP/AVP/UDP", strlen("RTP/AVP/UDP")) || 204 | !strncmp(ptr, "RTP/AVP", strlen("RTP/AVP"))) { 205 | sessp->intlvd_mode = 0; 206 | } else if (!strncmp(ptr, "unicast", strlen("unicast"))) { 207 | } else if (!strncmp(ptr, "mode", strlen("mode"))) { 208 | } else if (!strncmp(ptr, "client_port", strlen("client_port"))) { 209 | if (sscanf(ptr, "client_port=%hu-%hu", 210 | &req->req_hdr.transport.rtp_cli_port, 211 | &req->req_hdr.transport.rtcp_cli_port) != 2) { 212 | return -1; 213 | } 214 | } else if (!strncmp(ptr, "interleaved", strlen("interleaved"))) { 215 | if (sscanf(ptr, "interleaved=%hhd-%hhd", 216 | &req->req_hdr.transport.rtp_chn, 217 | &req->req_hdr.transport.rtcp_chn) != 2) { 218 | return -1; 219 | } 220 | } 221 | 222 | ptr = strpbrk(ptr, ";\r\n"); 223 | while (*ptr == ';') { /* Skip over separating ';' chars. */ 224 | ptr++; 225 | } 226 | if (*ptr == '\0' || *ptr == '\r' || *ptr == '\n') { 227 | break; 228 | } 229 | } 230 | return 0; 231 | } 232 | 233 | /** 234 | * Parse RTSP request header fields. 235 | */ 236 | static void parse_req_hdrs(struct rtsp_sess *sessp, const char *line) 237 | { 238 | struct rtsp_req *req = sessp->req; 239 | struct rtsp_resp *resp = sessp->resp; 240 | int cseq_found = 0; 241 | 242 | do { 243 | if (!strncmp(line, "CSeq", strlen("CSeq"))) { 244 | if (sscanf(line, "CSeq: %d", &req->req_hdr.cseq) != 1) { 245 | resp->resp_line.status_code = INTERNAL_SERVER_ERROR; 246 | printd(ERR "Parse RTSP header failed!\n"); 247 | break; 248 | } 249 | cseq_found = 1; 250 | resp->resp_hdr.cseq = req->req_hdr.cseq; 251 | } else if (!strncmp(line, "Session", strlen("Session"))) { 252 | if (sscanf(line, "Session: %llu", &req->req_hdr.sess_id) == 1) { 253 | if (rtsp_sess_id_exist(req->req_hdr.sess_id)) { 254 | resp->resp_hdr.sess_id = req->req_hdr.sess_id; 255 | } else { 256 | printd(WARNING "Session not found!\n"); 257 | resp->resp_hdr.sess_id = 0; 258 | resp->resp_line.status_code = SESSION_NOT_FOUND; 259 | break; 260 | } 261 | } else { 262 | resp->resp_line.status_code = INTERNAL_SERVER_ERROR; 263 | printd(ERR "Parse RTSP header failed!\n"); 264 | break; 265 | } 266 | } else if (!strncmp(line, "User-Agent", strlen("User-Agent"))) { 267 | } else if (!strncmp(line, "Transport", strlen("Transport"))) { 268 | if (parse_hdr_transport(sessp, line) < 0) { 269 | resp->resp_line.status_code = INTERNAL_SERVER_ERROR; 270 | printd(ERR "Parse RTSP header failed!\n"); 271 | break; 272 | } 273 | } 274 | 275 | line = get_next_line(line); 276 | } while (line); 277 | 278 | if (!cseq_found) { 279 | resp->resp_line.status_code = INTERNAL_SERVER_ERROR; 280 | printd(WARNING "CSeq not found!\n"); 281 | } 282 | return; 283 | } 284 | 285 | /** 286 | * We assume that the request message is complete, 287 | * and do not handle the un-complete request message. 288 | */ 289 | void parse_rtsp_req(struct rtsp_sess *sessp) 290 | { 291 | const char *line = sessp->recv_buf; 292 | struct rtsp_req *req = sessp->req; 293 | struct rtsp_resp *resp = sessp->resp; 294 | 295 | if (sessp->handling_state != HANDLING_STATE_READY) { 296 | return; 297 | } 298 | 299 | /* Prepare RTSP response message. */ 300 | resp->resp_line.ver = strndup(RTSP_VERSION, strlen(RTSP_VERSION)); 301 | resp->resp_hdr.server = strndup(RTSP_SERVER, strlen(RTSP_SERVER)); 302 | resp->resp_hdr.cseq = req->req_hdr.cseq; 303 | resp->resp_hdr.sess_id = req->req_hdr.sess_id; 304 | 305 | print_req_msg(sessp->recv_buf); 306 | 307 | /* Parse RTSP request line. */ 308 | parse_req_line(sessp, line); 309 | 310 | /* Parse RTSP request header fields. */ 311 | line = get_next_line(line); 312 | if (line) { 313 | parse_req_hdrs(sessp, line); 314 | } 315 | 316 | if (sessp->handling_state != HANDLING_STATE_FAILED) { 317 | sessp->handling_state = (resp->resp_line.status_code == OK) ? 318 | HANDLING_STATE_PARSED : HANDLING_STATE_ERROR; 319 | } 320 | return; 321 | } 322 | -------------------------------------------------------------------------------- /src/rtsp_parser.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : parser.h 3 | * Description : Parse the RTSP requeset message. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-23 6 | ********************************************************************/ 7 | 8 | #ifndef __PARSER_H__ 9 | #define __PARSER_H__ 10 | 11 | 12 | #include "rtsp_sess.h" 13 | 14 | 15 | void parse_rtsp_req(struct rtsp_sess *sessp); 16 | 17 | 18 | #endif /* __PARSER_H__ */ 19 | -------------------------------------------------------------------------------- /src/rtsp_sess.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtsp_sess.c 3 | * Description : RTSP session management. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-27 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "list.h" 15 | #include "util.h" 16 | #include "log.h" 17 | #include "rtsp_srv.h" 18 | #include "rtsp_parser.h" 19 | #include "delay_task.h" 20 | #include "rtsp_method.h" 21 | #include "sd_handler.h" 22 | #include "rtp.h" 23 | 24 | 25 | #define CRLF "\r\n" 26 | 27 | 28 | struct send_buf *create_send_buf(enum data_type type, unsigned int sz) 29 | { 30 | struct send_buf *sendp = NULL; 31 | 32 | sendp = mallocz(sizeof(*sendp)); 33 | if (!sendp) { 34 | printd("Allocate memory for send buffer failed!\n"); 35 | return NULL; 36 | } 37 | sendp->sz = sz; 38 | sendp->type = type; 39 | sendp->buf = mallocz(sz); 40 | if (!sendp->buf) { 41 | printd("Allocate memory for sendp->buf failed!\n"); 42 | freez(sendp); 43 | return NULL; 44 | } 45 | return sendp; 46 | } 47 | 48 | void destroy_send_buf(struct send_buf *sendp) 49 | { 50 | freez(sendp->buf); 51 | freez(sendp); 52 | return; 53 | } 54 | 55 | void add_send_buf(struct rtsp_sess *sessp, struct send_buf *sendp) 56 | { 57 | /* Put it into send buffer queue. */ 58 | list_add_tail(&sendp->entry, &sessp->send_queue); 59 | return; 60 | } 61 | 62 | void del_send_buf(struct send_buf *sendp) 63 | { 64 | /* Delete from send buffer queue. */ 65 | list_del(&sendp->entry); 66 | return; 67 | } 68 | 69 | static char *make_date_hdr(void) 70 | { 71 | static char date[256]; 72 | time_t tm; 73 | 74 | tm = time(NULL); 75 | strftime(date, sizeof(date), 76 | "Date: %a, %b %d %Y %H:%M:%S GMT"CRLF, gmtime(&tm)); 77 | return date; 78 | } 79 | 80 | /** 81 | * Produce RTSP response message. 82 | * The buffer data produced here will be consumed 83 | * by function consume_send_buf(). 84 | */ 85 | void produce_rtsp_resp(struct rtsp_sess *sessp) 86 | { 87 | /* Use this MACRO to move forward while filling the response message. */ 88 | #define MOVE_FORWARD() do { \ 89 | left -= strlen(line); \ 90 | if (left < 0) { \ 91 | sessp->handling_state = HANDLING_STATE_FAILED; \ 92 | printd("MOVE_FORWARD failed when parsing request.\n"); \ 93 | return; \ 94 | } \ 95 | line += strlen(line); \ 96 | } while (0) 97 | 98 | struct send_buf *sendp = NULL; 99 | char *line = NULL; 100 | unsigned int left = 0; 101 | struct rtsp_req *req = sessp->req; 102 | struct rtsp_resp *resp = sessp->resp; 103 | 104 | if (!rtsp_sess_exist(sessp)) { 105 | return; 106 | } 107 | 108 | if (sessp->handling_state != HANDLING_STATE_ERROR && 109 | sessp->handling_state != HANDLING_STATE_HANDLED) { 110 | return; 111 | } 112 | 113 | sendp = create_send_buf(DATA_TYPE_RTSP_RESP, RTSP_RESP_SZ); 114 | if (!sendp) { 115 | sessp->handling_state = HANDLING_STATE_FAILED; 116 | return; 117 | } 118 | line = sendp->buf; 119 | left = sendp->sz; 120 | 121 | /* Status line of response message. */ 122 | MOVE_FORWARD(); 123 | snprintf(line, left, "%s %d %s"CRLF, resp->resp_line.ver, 124 | resp->resp_line.status_code, 125 | get_status_reason(resp->resp_line.status_code)); 126 | 127 | /* Common response header fields. */ 128 | MOVE_FORWARD(); 129 | snprintf(line, left, "CSeq: %u"CRLF, resp->resp_hdr.cseq); 130 | MOVE_FORWARD(); 131 | snprintf(line, left, "%s", make_date_hdr()); 132 | MOVE_FORWARD(); 133 | snprintf(line, left, "Server: %s"CRLF, resp->resp_hdr.server); 134 | 135 | /* If we are at state HANDLING_STATE_FAILED, don't do following stuff. */ 136 | if (sessp->handling_state == HANDLING_STATE_HANDLED) { 137 | if (resp->resp_hdr.sess_id) { 138 | MOVE_FORWARD(); 139 | snprintf(line, left, "Session: %llu"CRLF, resp->resp_hdr.sess_id); 140 | } 141 | 142 | /* Response header fields of different method. */ 143 | switch (req->req_line.method) { 144 | case RTSP_METHOD_OPTIONS: 145 | MOVE_FORWARD(); 146 | snprintf(line, left, "Public: %s"CRLF, resp->resp_hdr.public); 147 | break; 148 | case RTSP_METHOD_DESCRIBE: 149 | MOVE_FORWARD(); 150 | snprintf(line, left, "Content-type: application/sdp"CRLF); 151 | MOVE_FORWARD(); 152 | snprintf(line, left, "Content-length: %d"CRLF, 153 | (int)strlen(resp->resp_body.sdp_info)); 154 | MOVE_FORWARD(); 155 | snprintf(line, left, "%s", resp->resp_body.sdp_info); 156 | break; 157 | case RTSP_METHOD_SETUP: 158 | MOVE_FORWARD(); 159 | if (sessp->intlvd_mode) { 160 | snprintf(line, left, "Transport: RTP/AVP/TCP;unicast;" 161 | "interleaved=%hhd-%hhd"CRLF, 162 | resp->resp_hdr.transport.rtp_chn, 163 | resp->resp_hdr.transport.rtcp_chn); 164 | } else { 165 | snprintf(line, left, "Transport: RTP/AVP/UDP;unicast;" 166 | "client_port=%hu-%hu;server_port=%hu-%hu"CRLF, 167 | resp->resp_hdr.transport.rtp_cli_port, 168 | resp->resp_hdr.transport.rtcp_cli_port, 169 | resp->resp_hdr.transport.rtp_srv_port, 170 | resp->resp_hdr.transport.rtcp_srv_port); 171 | } 172 | break; 173 | case RTSP_METHOD_PLAY: 174 | break; 175 | case RTSP_METHOD_PAUSE: 176 | break; 177 | case RTSP_METHOD_GET_PARAMETER: 178 | break; 179 | case RTSP_METHOD_SET_PARAMETER: 180 | break; 181 | case RTSP_METHOD_TEARDOWN: 182 | break; 183 | } 184 | } 185 | 186 | /* End of response message. */ 187 | MOVE_FORWARD(); 188 | snprintf(line, left, CRLF); 189 | 190 | /* Get the actual message size which will be sent. */ 191 | MOVE_FORWARD(); 192 | sendp->sz -= left; 193 | 194 | add_send_buf(sessp, sendp); 195 | 196 | sessp->handling_state = HANDLING_STATE_DONE; 197 | return; 198 | 199 | #undef MOVE_FORWARD 200 | } 201 | 202 | void run_rtsp_state_machine(struct rtsp_sess *sessp) 203 | { 204 | static void (*handle_method[])(struct rtsp_sess *) = { 205 | [RTSP_METHOD_OPTIONS] = handle_method_options, 206 | [RTSP_METHOD_DESCRIBE] = handle_method_describe, 207 | [RTSP_METHOD_SETUP] = handle_method_setup, 208 | [RTSP_METHOD_PLAY] = handle_method_play, 209 | [RTSP_METHOD_PAUSE] = handle_method_pause, 210 | [RTSP_METHOD_GET_PARAMETER] = handle_method_get_parameter, 211 | [RTSP_METHOD_SET_PARAMETER] = handle_method_set_parameter, 212 | [RTSP_METHOD_TEARDOWN] = handle_method_teardown, 213 | }; 214 | 215 | if (sessp->handling_state != HANDLING_STATE_PARSED && 216 | sessp->handling_state != HANDLING_STATE_RETRY) { 217 | return; 218 | } 219 | 220 | /* Handle the RTSP method of requested. */ 221 | handle_method[sessp->req->req_line.method](sessp); 222 | 223 | return; 224 | } 225 | 226 | static ssize_t send_buf_data(struct rtsp_sess *sessp, struct send_buf *sendp) 227 | { 228 | ssize_t ns = 0; /* Bytes sent. */ 229 | int sd = -1; 230 | struct sockaddr *sap = NULL; 231 | int next_ev = 0; 232 | 233 | if (sessp->intlvd_mode || sendp->type == DATA_TYPE_RTSP_RESP) { 234 | sd = sessp->rtsp_sd; 235 | next_ev = RTSP_SD_DFL_EV; 236 | ns = send(sd, sendp->buf, sendp->sz, 0); 237 | if (ns < 0 || ns != sendp->sz) { 238 | printd(WARNING "send_buf_data ns = %d\n", ns); 239 | } 240 | } else { 241 | switch (sendp->type) { 242 | case DATA_TYPE_RTP_V_PKT: 243 | sd = sessp->rtp_rtcp[MEDIA_TYPE_VIDEO].udp.rtp_sd; 244 | sap = &sessp->rtp_rtcp[MEDIA_TYPE_VIDEO].udp.rtp_sa; 245 | next_ev = RTP_SD_DFL_EV; 246 | break; 247 | case DATA_TYPE_RTP_A_PKT: 248 | sd = sessp->rtp_rtcp[MEDIA_TYPE_AUDIO].udp.rtp_sd; 249 | sap = &sessp->rtp_rtcp[MEDIA_TYPE_AUDIO].udp.rtp_sa; 250 | next_ev = RTP_SD_DFL_EV; 251 | break; 252 | case DATA_TYPE_RTCP_V_PKT: 253 | sd = sessp->rtp_rtcp[MEDIA_TYPE_VIDEO].udp.rtcp_sd; 254 | next_ev = RTCP_SD_DFL_EV; 255 | sap = &sessp->rtp_rtcp[MEDIA_TYPE_VIDEO].udp.rtcp_sa; 256 | break; 257 | case DATA_TYPE_RTCP_A_PKT: 258 | sd = sessp->rtp_rtcp[MEDIA_TYPE_AUDIO].udp.rtcp_sd; 259 | next_ev = RTCP_SD_DFL_EV; 260 | sap = &sessp->rtp_rtcp[MEDIA_TYPE_AUDIO].udp.rtcp_sa; 261 | break; 262 | default: 263 | return -1; 264 | } 265 | ns = sendto(sd, sendp->buf, sendp->sz, 0, 266 | sap, sizeof(struct sockaddr)); 267 | } 268 | 269 | update_sd_event(sd, next_ev); 270 | return ns; 271 | } 272 | 273 | /** 274 | * @brief: Find the specified type buffer in send queue, and send it out. 275 | * Then remove the buffer from the queue. 276 | * 277 | * @type: The buffer type wanted to be consumed. 278 | * 279 | * NOTE: 280 | * To guarantee the order of sending RT(C)P packet(including video & audio), 281 | * we stop iterating the queue when found one kind of RT(C)P packet, no matter 282 | * it's kind of video or audio. 283 | * Maybe it's hard to understand, just reading the code. 284 | */ 285 | int consume_send_buf(struct rtsp_sess *sessp, enum data_type type) 286 | { 287 | struct send_buf *sendp = NULL; /* Send buffer pointer. */ 288 | 289 | if (!list_empty(&sessp->send_queue)) { 290 | sendp = list_first_entry(&sessp->send_queue, struct send_buf, entry); 291 | if (sendp->type & type) { 292 | if (send_buf_data(sessp, sendp) < 0) { 293 | return -1; 294 | } 295 | del_send_buf(sendp); 296 | destroy_send_buf(sendp); 297 | } 298 | } 299 | 300 | return 0; 301 | } 302 | 303 | /** 304 | * Create one session for it 305 | * and add it to the session list. 306 | */ 307 | int create_rtsp_sess(int rtsp_sd) 308 | { 309 | struct rtsp_sess *sessp = NULL; /* RTSP session information. */ 310 | 311 | /* Allocate a rtsp_sess struct for this session. */ 312 | sessp = mallocz(sizeof(*sessp)); 313 | if (!sessp) { 314 | printd(ERR "Allocate memory for RTSP session failed!\n"); 315 | goto err; 316 | } 317 | 318 | /* Register sd handler. */ 319 | if (register_sd_handler(rtsp_sd, handle_rtsp_sd, sessp) < 0) { 320 | printd(ERR "register rtsp_sd handler failed!\n"); 321 | goto err; 322 | } 323 | sessp->rtsp_sd = rtsp_sd; 324 | 325 | /* Allocate memory for receive buffer. */ 326 | sessp->recv_buf = mallocz(RECV_BUF_SZ); 327 | if (!sessp->recv_buf) { 328 | printd("ERR Allocate memory for receive buffer failed!\n"); 329 | goto err; 330 | } 331 | 332 | /* Allocate memory for frame buffer. */ 333 | sessp->strm.frm_info.frm_buf = mallocz(MAX_FRM_SZ); 334 | if (!sessp->strm.frm_info.frm_buf) { 335 | printd("ERR Allocate memory for frame buffer failed!\n"); 336 | goto err; 337 | } 338 | 339 | /* Initialize the stream information. */ 340 | get_strm_cb(&sessp->strm.start_strm, 341 | &sessp->strm.stop_strm, 342 | &sessp->strm.get_frm); 343 | 344 | /* Other initializations. */ 345 | sessp->rtsp_state = RTSP_STATE_INIT; 346 | sessp->handling_state = HANDLING_STATE_INIT; 347 | INIT_LIST_HEAD(&sessp->send_queue); 348 | 349 | /* Add to session list. */ 350 | list_add(&sessp->entry, &rtsp_srv.rtsp_sess_list); 351 | return 0; 352 | 353 | err: 354 | deregister_sd_handler(rtsp_sd); 355 | freez(sessp->strm.frm_info.frm_buf); 356 | freez(sessp->recv_buf); 357 | freez(sessp); 358 | return -1; 359 | } 360 | 361 | /** 362 | * Delete the session from session list, 363 | * and destroy its relevant resource. 364 | * 365 | * Return -1, means task finished. 366 | * Return >= 0, means task will be delay, and perform it later. 367 | */ 368 | int destroy_rtsp_sess(struct rtsp_sess *sessp) 369 | { 370 | struct send_buf *sendp = NULL; 371 | struct send_buf *tmp_sendp = NULL; 372 | 373 | if (sessp->handling_state != HANDLING_STATE_INIT) { 374 | return 10 * 1000; /* Retry 10ms later. */ 375 | } 376 | sessp->rtsp_state = RTSP_STATE_INIT; 377 | 378 | /* Loopup RTSP session in list, maybe it has been deleted. */ 379 | if (!rtsp_sess_exist(sessp)) { 380 | return -1; 381 | } 382 | 383 | list_del(&sessp->entry); 384 | 385 | freez(sessp->strm.strm_info.file_name); 386 | 387 | /* Close and deregister socket descriptors. */ 388 | deregister_sd_handler(sessp->rtsp_sd); 389 | close(sessp->rtsp_sd); 390 | 391 | /* Free send buffers in queue. */ 392 | list_for_each_entry_safe(sendp, tmp_sendp, &sessp->send_queue, entry) { 393 | del_send_buf(sendp); 394 | destroy_send_buf(sendp); 395 | } 396 | 397 | /* Destroy delayed task in queue. */ 398 | destroy_sess_task(sessp); 399 | 400 | /* Destroy RTP session. */ 401 | destroy_rtp_sess(sessp); 402 | 403 | /* Free frame buffer. */ 404 | freez(sessp->strm.frm_info.frm_buf); 405 | 406 | /* Free receive buffer. */ 407 | freez(sessp->recv_buf); 408 | freez(sessp); 409 | 410 | /* Task finished. */ 411 | return -1; 412 | } 413 | 414 | void destroy_rtsp_sess_contain_sd(int sd) 415 | { 416 | struct rtsp_sess *sessp = NULL; /* Used as loop curser. */ 417 | 418 | list_for_each_entry(sessp, &rtsp_srv.rtsp_sess_list, entry) { 419 | if (sessp->rtsp_sd == sd || 420 | sessp->rtp_rtcp[0].udp.rtp_sd == sd || 421 | sessp->rtp_rtcp[0].udp.rtcp_sd == sd || 422 | sessp->rtp_rtcp[1].udp.rtp_sd == sd || 423 | sessp->rtp_rtcp[1].udp.rtcp_sd == sd) { 424 | destroy_rtsp_sess(sessp); 425 | return; 426 | } 427 | } 428 | return; 429 | } 430 | 431 | int rtsp_sess_id_exist(unsigned long long sess_id) 432 | { 433 | struct rtsp_sess *sessp = NULL; /* Used as loop curser. */ 434 | 435 | list_for_each_entry(sessp, &rtsp_srv.rtsp_sess_list, entry) { 436 | if (sessp->sess_id == sess_id) { 437 | return 1; 438 | } 439 | } 440 | return 0; 441 | } 442 | 443 | int rtsp_sess_exist(struct rtsp_sess *sessp) 444 | { 445 | struct rtsp_sess *tmp = NULL; /* Used as loop curser. */ 446 | 447 | list_for_each_entry(tmp, &rtsp_srv.rtsp_sess_list, entry) { 448 | if (tmp == sessp) { 449 | return 1; 450 | } 451 | } 452 | return 0; 453 | } 454 | 455 | /** 456 | * Check send buffer queue in each RTSP session in list. 457 | * If there's any data to be sent, we add EPOLLOUT event to 458 | * corresponding socket descriptor. 459 | * 460 | * NOTE: The added EPOLLOUT event will be removed after the 461 | * data is sent out. 462 | */ 463 | int check_send_queue(void) 464 | { 465 | struct rtsp_sess *sessp = NULL; 466 | struct send_buf *sendp = NULL; 467 | int sd = -1; 468 | int ev = EPOLLOUT; 469 | 470 | list_for_each_entry(sessp, &rtsp_srv.rtsp_sess_list, entry) { 471 | if (!list_empty(&sessp->send_queue)) { 472 | sendp = list_first_entry(&sessp->send_queue, struct send_buf, entry); 473 | if (sessp->intlvd_mode || sendp->type == DATA_TYPE_RTSP_RESP) { 474 | sd = sessp->rtsp_sd; 475 | ev |= RTSP_SD_DFL_EV; 476 | } else { 477 | switch (sendp->type) { 478 | case DATA_TYPE_RTP_V_PKT: 479 | sd = sessp->rtp_rtcp[MEDIA_TYPE_VIDEO].udp.rtp_sd; 480 | ev |= RTP_SD_DFL_EV; 481 | break; 482 | case DATA_TYPE_RTP_A_PKT: 483 | sd = sessp->rtp_rtcp[MEDIA_TYPE_AUDIO].udp.rtp_sd; 484 | ev |= RTP_SD_DFL_EV; 485 | break; 486 | case DATA_TYPE_RTCP_V_PKT: 487 | sd = sessp->rtp_rtcp[MEDIA_TYPE_VIDEO].udp.rtcp_sd; 488 | ev |= RTCP_SD_DFL_EV; 489 | break; 490 | case DATA_TYPE_RTCP_A_PKT: 491 | sd = sessp->rtp_rtcp[MEDIA_TYPE_AUDIO].udp.rtcp_sd; 492 | ev |= RTCP_SD_DFL_EV; 493 | break; 494 | default: 495 | break; 496 | } 497 | } 498 | if (update_sd_event(sd, ev) < 0) { 499 | return -1; 500 | } 501 | } 502 | } 503 | return 0; 504 | } 505 | 506 | void init_rtsp_sess_list(void) 507 | { 508 | INIT_LIST_HEAD(&rtsp_srv.rtsp_sess_list); 509 | return; 510 | } 511 | 512 | void deinit_rtsp_sess_list(void) 513 | { 514 | struct rtsp_sess *sessp = NULL; 515 | struct rtsp_sess *tmp = NULL; 516 | 517 | list_for_each_entry_safe(sessp, tmp, &rtsp_srv.rtsp_sess_list, entry) { 518 | destroy_rtsp_sess(sessp); 519 | } 520 | return; 521 | } 522 | -------------------------------------------------------------------------------- /src/rtsp_sess.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtsp_sess.h 3 | * Description : RTSP session management. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-27 6 | ********************************************************************/ 7 | 8 | #ifndef __RTSP_SESS_H__ 9 | #define __RTSP_SESS_H__ 10 | 11 | 12 | #include "util.h" 13 | #include "list.h" 14 | #include "rtsp_srv.h" 15 | #include "librtspsrv.h" 16 | #include "rtp.h" 17 | 18 | 19 | /* RTSP method handling state. */ 20 | enum handling_state { 21 | HANDLING_STATE_INIT, /* When created. */ 22 | HANDLING_STATE_READY, /* It's ready to parse the request. */ 23 | HANDLING_STATE_PARSED, /* Request has been parsed. */ 24 | HANDLING_STATE_HANDLED, /* RTSP method has been performed. */ 25 | HANDLING_STATE_RETRY, /* Retry to handle RTSP method. */ 26 | HANDLING_STATE_DONE, /* Everything done, RTSP response has been sent out. */ 27 | HANDLING_STATE_ERROR, /* RTSP Error occured while handling. */ 28 | HANDLING_STATE_FAILED, /* Handling RTSP method failed, can't continue... */ 29 | }; 30 | 31 | /* Transport in request or response header fields. */ 32 | struct transport { 33 | char rtp_chn; 34 | char rtcp_chn; 35 | unsigned short rtp_cli_port; 36 | unsigned short rtcp_cli_port; 37 | unsigned short rtp_srv_port; 38 | unsigned short rtcp_srv_port; 39 | } transport; 40 | 41 | /* RTSP request. */ 42 | struct rtsp_req { 43 | struct req_line { /* Request line. */ 44 | enum rtsp_method method; /* RTSP method ID. */ 45 | char *uri; /* RTSP uri. */ 46 | char *ver; /* RTSP version. */ 47 | } req_line; 48 | struct req_hdr { /* Response header fields. */ 49 | unsigned long long sess_id; /* RTSP session ID. */ 50 | unsigned int cseq; /* RTSP sequence number. */ 51 | struct transport transport; 52 | } req_hdr; 53 | enum media_type media; 54 | }; 55 | 56 | /* RTSP response. */ 57 | struct rtsp_resp { 58 | struct resp_line { /* Status line. */ 59 | char *ver; /* RTSP version. */ 60 | enum status_code status_code; /* RTSP status code. */ 61 | } resp_line; 62 | struct resp_hdr { /* Response header fields. */ 63 | unsigned long long sess_id; /* Session. */ 64 | unsigned int cseq; /* CSeq. */ 65 | char *server; /* Server. */ 66 | char *public; /* Public. */ 67 | struct transport transport; 68 | } resp_hdr; 69 | struct resp_body { 70 | char *sdp_info; /* List hold all sdp info. */ 71 | } resp_body; 72 | }; 73 | 74 | /* RTSP session stream resource: video or audio stream. */ 75 | struct strm { 76 | struct strm_info strm_info; /* Stream information. */ 77 | struct frm_info frm_info; /* Frame information. */ 78 | start_strm_t start_strm; /* Start the stream resource. */ 79 | stop_strm_t stop_strm; /* Stop the stream resource. */ 80 | get_frm_t get_frm; /* Callback function to get one frame. */ 81 | }; 82 | 83 | /* RTP information structure.. */ 84 | struct rtp_rtcp { 85 | struct rtp_hdr rtp_hdr; 86 | union { 87 | struct tcp { /* Used in interleaved mode. */ 88 | char rtp_chn; 89 | char rtcp_chn; 90 | } tcp; 91 | struct udp { 92 | int rtp_sd; /* Server RTP socket descriptor. */ 93 | int rtcp_sd; /* Server RTCP socket descriptor. */ 94 | struct sockaddr rtp_sa; /* Client RTP socket address. */ 95 | struct sockaddr rtcp_sa; /* Client RTCP socket address. */ 96 | } udp; 97 | }; 98 | }; 99 | 100 | /* RTSP session struct. */ 101 | struct rtsp_sess { 102 | struct list_head entry; /* Entry of session list. */ 103 | unsigned long long sess_id; /* Session ID. */ 104 | unsigned int cseq; /* RTSP sequence number. */ 105 | enum rtsp_state rtsp_state; /* Current session state. */ 106 | enum handling_state handling_state; /* RTSP method handling state. */ 107 | int rtsp_sd; /* RTSP socket discriptor. */ 108 | int intlvd_mode; /* Interleaved mode: [0, 1]. */ 109 | struct list_head send_queue; /* A queue keeps send buffers. */ 110 | char *recv_buf; /* Buffer for receiving client data. */ 111 | struct rtsp_req *req; /* RTSP request. */ 112 | struct rtsp_resp *resp; /* RTSP response. */ 113 | struct strm strm; /* Stream resource */ 114 | struct rtp_rtcp rtp_rtcp[2]; /* RTP & RTCP information of video[0] & audio[1]. */ 115 | }; 116 | 117 | 118 | void init_rtsp_sess_list(void); 119 | void deinit_rtsp_sess_list(void); 120 | 121 | int create_rtsp_sess(int rtsp_sd); 122 | int destroy_rtsp_sess(struct rtsp_sess *sessp); 123 | 124 | int get_rtsp_method(const char *str); 125 | 126 | struct send_buf *create_send_buf(enum data_type type, unsigned int sz); 127 | void destroy_send_buf(struct send_buf *sendp); 128 | void add_send_buf(struct rtsp_sess *sessp, struct send_buf *sendp); 129 | void del_send_buf(struct send_buf *sendp); 130 | void produce_rtsp_resp(struct rtsp_sess *sessp); 131 | int consume_send_buf(struct rtsp_sess *sessp, enum data_type type); 132 | 133 | void run_rtsp_state_machine(struct rtsp_sess *sessp); 134 | 135 | void destroy_rtsp_sess_contain_sd(int sd); 136 | int rtsp_sess_id_exist(unsigned long long sess_id); 137 | int rtsp_sess_exist(struct rtsp_sess *sessp); 138 | int check_send_queue(void); 139 | 140 | 141 | #endif /* __RTSP_SESS_H__ */ 142 | -------------------------------------------------------------------------------- /src/rtsp_srv.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtsp_srv.c 3 | * Description : Implementaion of a RTSP server. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-16 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "list.h" 19 | #include "log.h" 20 | #include "librtspsrv.h" 21 | #include "rtsp_srv.h" 22 | #include "util.h" 23 | #include "rtsp_parser.h" 24 | #include "rtsp_sess.h" 25 | #include "rtp.h" 26 | #include "sd_handler.h" 27 | #include "delay_task.h" 28 | 29 | 30 | struct rtsp_srv rtsp_srv; 31 | 32 | /** 33 | * This will add specified @ev to the epoll events. 34 | * 35 | * @ev: Generally, we provide EPOLLIN, EPOLLOUT, EPOLLET for the caller. 36 | */ 37 | int monitor_sd_event(int fd, unsigned int ev) 38 | { 39 | struct epoll_event ee; 40 | 41 | ee.events = ev; 42 | ee.data.fd = fd; 43 | if (epoll_ctl(rtsp_srv.epoll_fd, EPOLL_CTL_ADD, fd, &ee) < 0) { 44 | perrord(ERR "Add fd to epoll events error"); 45 | return -1; 46 | } 47 | return 0; 48 | } 49 | 50 | /** 51 | * This will modify specified @ev to the epoll events. 52 | * 53 | * @ev: Generally, we provide EPOLLIN, EPOLLOUT, EPOLLET for the caller. 54 | */ 55 | int update_sd_event(int fd, unsigned int ev) 56 | { 57 | struct epoll_event ee; 58 | 59 | ee.events = ev; 60 | ee.data.fd = fd; 61 | if (epoll_ctl(rtsp_srv.epoll_fd, EPOLL_CTL_MOD, fd, &ee) < 0) { 62 | perrord(ERR "Add fd to epoll events error"); 63 | return -1; 64 | } 65 | return 0; 66 | } 67 | 68 | static void *rtsp_srv_thrd(void *arg) 69 | { 70 | pthread_detach(pthread_self()); 71 | entering_thread(); 72 | int i = 0; 73 | struct rtsp_srv *srvp = (struct rtsp_srv *)arg; 74 | int rtsp_lsn_sd = -1; /* RTSP listen socket. */ 75 | int epfd = -1; 76 | struct epoll_event *ees = NULL; 77 | int nfds = 0; 78 | int timeout = 0; 79 | struct rlimit rl; 80 | int maxfd = MAX_FD_NUM; 81 | int min_delay_time = 0; 82 | 83 | /* Set resource limit. */ 84 | rl.rlim_cur = maxfd; 85 | rl.rlim_max = maxfd; 86 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { 87 | if (errno == EPERM || errno == EINVAL) { 88 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { 89 | perrord(WARNING "Get resource limit error"); 90 | goto rtn; 91 | } else { 92 | maxfd = rl.rlim_cur; 93 | rl.rlim_cur = rl.rlim_max; 94 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { 95 | perrord(WARNING "Set resource limit error"); 96 | } else { 97 | maxfd = rl.rlim_cur; 98 | } 99 | } 100 | } else { 101 | perrord(WARNING "Set resource limit error"); 102 | } 103 | return NULL; 104 | } 105 | 106 | /* Create epoll file descriptor. */ 107 | epfd = epoll_create(maxfd); 108 | if (epfd < 0) { 109 | perrord(EMERG "Create epoll file descriptor error"); 110 | goto rtn; 111 | } 112 | srvp->epoll_fd = epfd; 113 | 114 | /* Create listen socket. */ 115 | rtsp_lsn_sd = create_and_bind(srvp->rtsp_port); 116 | if (rtsp_lsn_sd < 0) { 117 | goto rtn; 118 | } 119 | if (listen(rtsp_lsn_sd, BACKLOG) < 0) { 120 | perrord(EMERG "Start listening connections error"); 121 | goto rtn; 122 | } 123 | srvp->rtsp_lsn_sd = rtsp_lsn_sd; 124 | if (monitor_sd_event(srvp->rtsp_lsn_sd, RTSP_LSN_SD_DFL_EV) < 0) { 125 | goto rtn; 126 | } 127 | 128 | /* Register sd handler. */ 129 | if (register_sd_handler(rtsp_lsn_sd, handle_lsn_sd, srvp) < 0) { 130 | printd(ERR "register rtsp_lsn_sd handler failed!\n"); 131 | goto rtn; 132 | } 133 | 134 | ees = calloc(EPOLL_MAX_EVENTS, sizeof(struct epoll_event)); 135 | if (ees == NULL) { 136 | printd("calloc for epoll events failed!\n"); 137 | goto rtn; 138 | } 139 | 140 | /* Accept connections, then create a new thread for it. */ 141 | printd(INFO "Start listening RTSP connections ...\n"); 142 | while (srvp->thrd_running) { 143 | /* Check send buffer queue in each RTSP session, 144 | * then update the event of socket descriptors. */ 145 | if (check_send_queue() < 0) { 146 | continue; 147 | } 148 | /* Reset the value of epoll_wait timeout. */ 149 | min_delay_time = get_min_delay_time(); 150 | timeout = (min_delay_time < 0) ? -1 : (min_delay_time / THOUSAND); 151 | 152 | /* Wait event notifications. */ 153 | do { 154 | nfds = epoll_wait(epfd, ees, EPOLL_MAX_EVENTS, timeout); 155 | } while (nfds < 0 && errno == EINTR); 156 | if (nfds < 0) { 157 | perrord(ERR "epoll_wait for listen socket error"); 158 | goto rtn; 159 | } 160 | 161 | /* Handle the readable sockets. */ 162 | for (i = 0; i < nfds; i++) { 163 | if ((ees[i].events & EPOLLERR) || 164 | #ifdef EPOLLRDHUP 165 | (ees[i].events & EPOLLRDHUP) || 166 | #endif 167 | (ees[i].events & EPOLLHUP)) { 168 | /* Error occured on this descriptor. */ 169 | printd(WARNING "epoll_wait error occured on this fd[%d]\n", 170 | ees[i].events); 171 | destroy_rtsp_sess_contain_sd(ees[i].data.fd); 172 | continue; 173 | } 174 | 175 | do_sd_handler(ees[i].data.fd, ees[i].events); 176 | } 177 | 178 | /* Process the delayed task whose time is up. */ 179 | do_delay_task(); 180 | } 181 | 182 | rtn: 183 | deregister_sd_handler(srvp->rtsp_lsn_sd); 184 | close(srvp->rtsp_lsn_sd); 185 | close(srvp->http_lsn_sd); 186 | close(srvp->epoll_fd); 187 | srvp->rtsp_lsn_sd = -1; 188 | srvp->http_lsn_sd = -1; 189 | freez(ees); 190 | leaving_thread(); 191 | return NULL; 192 | } 193 | 194 | int start_rtsp_srv(void) 195 | { 196 | int ret = 0; 197 | 198 | rtsp_srv.rtsp_lsn_sd = -1; 199 | rtsp_srv.http_lsn_sd = -1; 200 | rtsp_srv.rtsp_port = DFL_RTSP_PORT; 201 | rtsp_srv.http_port = DFL_HTTP_PORT; 202 | rtsp_srv.thrd_running = 1; 203 | init_rtsp_sess_list(); 204 | init_sd_handler_list(); 205 | init_delay_task_queue(); 206 | 207 | if ((ret = pthread_create(&rtsp_srv.rtsp_srv_tid, NULL, 208 | rtsp_srv_thrd, &rtsp_srv)) != 0) { 209 | printd(EMERG "Create thread rtsp_srv_thrd error: %s\n", strerror(ret)); 210 | return -1; 211 | } 212 | usleep(100 * 1000); 213 | return 0; 214 | } 215 | 216 | int stop_rtsp_srv(void) 217 | { 218 | rtsp_srv.thrd_running = 0; 219 | deinit_delay_task_queue(); 220 | deinit_sd_handler_list(); 221 | deinit_rtsp_sess_list(); 222 | 223 | return 0; 224 | } 225 | -------------------------------------------------------------------------------- /src/rtsp_srv.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : rtsp_srv.h 3 | * Description : Implementaion of a RTSP server. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-16 6 | ********************************************************************/ 7 | 8 | #ifndef __RTSP_SRV_H__ 9 | #define __RTSP_SRV_H__ 10 | 11 | #include 12 | #include "list.h" 13 | 14 | 15 | #define MAX_FD_NUM 100000 16 | #define BACKLOG 5 /* Maximum length of pending connections 17 | * for listening socket. */ 18 | 19 | #define RTSP_VERSION "RTSP/1.0" 20 | #define RTSP_SERVER "Zmodo-RTSP-Server" 21 | 22 | #define EPOLL_MAX_EVENTS 128 23 | 24 | /** 25 | * Epoll events of different socket descriptors. 26 | * Here doesn't include EPOLLOUT, we will add EPOLLOUT 27 | * when there's any data in send buffer queue. 28 | */ 29 | #define TIMER_SD_DFL_EV (EPOLLIN | EPOLLET) 30 | #define RTSP_LSN_SD_DFL_EV (EPOLLIN | EPOLLET) 31 | #define RTSP_SD_DFL_EV (EPOLLIN | EPOLLET) 32 | #define RTP_SD_DFL_EV (EPOLLET) 33 | #define RTCP_SD_DFL_EV (EPOLLIN | EPOLLET) 34 | 35 | /* 36 | * Max size of RTSP receiving buffer and response message, 37 | * these should be enough. 38 | */ 39 | #define RECV_BUF_SZ (100 * 1024) 40 | #define RTSP_RESP_SZ (100 * 1024) 41 | 42 | 43 | /* RTSP methods. */ 44 | enum rtsp_method { 45 | RTSP_METHOD_OPTIONS, 46 | RTSP_METHOD_DESCRIBE, 47 | RTSP_METHOD_SETUP, 48 | RTSP_METHOD_PLAY, 49 | RTSP_METHOD_PAUSE, 50 | RTSP_METHOD_GET_PARAMETER, 51 | RTSP_METHOD_SET_PARAMETER, 52 | RTSP_METHOD_TEARDOWN, 53 | }; 54 | 55 | /* 56 | * The RTSP server state machine is defined 57 | * in RFC2326 Appendix A, Subsection 2. 58 | */ 59 | enum rtsp_state { 60 | RTSP_STATE_INIT, 61 | RTSP_STATE_READY, 62 | RTSP_STATE_PLAYING, 63 | }; 64 | 65 | /* 66 | * RTSP server struct. 67 | */ 68 | struct rtsp_srv { 69 | pthread_t rtsp_srv_tid; /* Thread ID of RTSP server thread. */ 70 | int epoll_fd; /* File descriptor for epoll events. */ 71 | int rtsp_lsn_sd; /* Socket for listening RTSP connections. */ 72 | int http_lsn_sd; /* Socket for listening HTTP connections. */ 73 | unsigned short rtsp_port; 74 | unsigned short http_port; 75 | char thrd_running; /* the RTSP server thread is running or not. */ 76 | struct list_head rtsp_sess_list; /* A list holds all RTSP sessions. */ 77 | struct list_head sd_handler_list; /* A queue keeps socket descripotrs' handler. */ 78 | struct list_head delay_task_queue; /* A queue keeps delay tasks todo in the future. */ 79 | }; 80 | 81 | enum data_type { 82 | DATA_TYPE_RTSP_RESP = 0x01, /* RTSP response. */ 83 | DATA_TYPE_RTP_V_PKT = 0x02, /* RTP packet of video. */ 84 | DATA_TYPE_RTP_A_PKT = 0x04, /* RTP packet of audio. */ 85 | DATA_TYPE_RTCP_V_PKT = 0x08, /* RTCP packet of video. */ 86 | DATA_TYPE_RTCP_A_PKT = 0x10, /* RTCP packet of audio. */ 87 | }; 88 | 89 | /* 90 | * Each RTSP session contains one buffer queue. 91 | * And each buffer queue links the nodes below. 92 | */ 93 | struct send_buf { 94 | struct list_head entry; /* Entry of send queue. */ 95 | enum data_type type; /* RTSP, RTCP, RTP(I/P/A frame). */ 96 | unsigned int sz; /* Buffer size. */ 97 | char *buf; /* Buffer for storing raw data. */ 98 | }; 99 | 100 | extern struct rtsp_srv rtsp_srv; 101 | 102 | int start_rtsp_srv(void); 103 | int stop_rtsp_srv(void); 104 | 105 | int monitor_sd_event(int fd, unsigned int ev); 106 | int update_sd_event(int fd, unsigned int ev); 107 | 108 | 109 | #endif /* __RTSP_SRV_H__ */ 110 | -------------------------------------------------------------------------------- /src/sd_handler.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : sd_handler.c 3 | * Description : Handlers of socket descriptors. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-28 6 | ********************************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "list.h" 15 | #include "log.h" 16 | #include "rtsp_parser.h" 17 | #include "util.h" 18 | #include "rtsp_srv.h" 19 | #include "rtsp_sess.h" 20 | #include "delay_task.h" 21 | #include "rtsp_method.h" 22 | #include "sd_handler.h" 23 | #include "rtcp.h" 24 | 25 | 26 | /** 27 | * Handle listening socket. 28 | * @arg: Should be the struct rtsp_srv. 29 | */ 30 | int handle_lsn_sd(struct sd_handler *hp, unsigned int event) 31 | { 32 | struct rtsp_srv *srvp = (struct rtsp_srv *)hp->arg; 33 | struct sockaddr_in rtsp_sa; /* Address of RTSP socket. */ 34 | socklen_t rtsp_salen; /* Address length of RTSP listen socket. */ 35 | int rtsp_sd = -1; /* RTSP socket, accepted from client. */ 36 | socklen_t nodelay = 1; 37 | 38 | memset(&rtsp_sa, 0, sizeof(rtsp_sa)); 39 | rtsp_salen = sizeof(rtsp_sa); 40 | rtsp_sd = accept(srvp->rtsp_lsn_sd, (struct sockaddr *)&rtsp_sa, &rtsp_salen); 41 | if (rtsp_sd < 0) { 42 | perrord(ERR "Accept RTSP connection error"); 43 | return -1; 44 | } 45 | printd(INFO "Accept one connection from [%s:%d] ^_^\n", 46 | inet_ntoa(rtsp_sa.sin_addr), ntohs(rtsp_sa.sin_port)); 47 | 48 | /* Set rtsp_sd non-blocking. */ 49 | if (set_block_mode(rtsp_sd, 0) < 0) { 50 | close(rtsp_sd); 51 | return -1; 52 | } 53 | 54 | if (setsockopt(rtsp_sd, IPPROTO_TCP, TCP_NODELAY, 55 | &nodelay, sizeof(nodelay)) < 0) { 56 | perrord("Set RTSP socket option [TCP_NODELAY] error"); 57 | return -1; 58 | } 59 | 60 | monitor_sd_event(rtsp_sd, RTSP_SD_DFL_EV); 61 | 62 | create_rtsp_sess(rtsp_sd); 63 | return 0; 64 | } 65 | 66 | /** 67 | * Consider about the interleaved mode, we have to 68 | * filter out the RTCP packet. 69 | */ 70 | static int filter_data_from_rtsp_sd(struct rtsp_sess *sessp) 71 | { 72 | ssize_t nr = 0; 73 | int offset = 0; 74 | 75 | /* '$' must be the byte received last time, 76 | * we should continue to receive completed RTCP packet. */ 77 | offset = (sessp->recv_buf[0] == '$') ? 1 : 0; 78 | 79 | nr = recv(sessp->rtsp_sd, 80 | sessp->recv_buf + offset, RECV_BUF_SZ - offset, 0); 81 | if (nr <= 0) { 82 | if (nr < 0) { 83 | perrord(ERR "Receive data from rtsp_sd error"); 84 | } 85 | return -1; 86 | } else if (nr == RECV_BUF_SZ - offset) { 87 | printd(WARNING "Receive buffer seems not enough!\n"); 88 | return 0; 89 | } 90 | 91 | if ((sessp->recv_buf[0] == '$') && (nr == 1)) { 92 | /* Continue to receive completed RTCP packet. */ 93 | return 0; 94 | } else if (sessp->recv_buf[0] == '$') { /* Completed RTCP packet. */ 95 | handle_rtcp_pkt(sessp); 96 | sessp->recv_buf[0] = '\0'; 97 | } else { /* RTSP request message. */ 98 | /* Handle the RTSP method. */ 99 | create_delay_task((delay_task_proc_t)handle_rtsp_req, sessp, 0); 100 | } 101 | 102 | return 0; 103 | } 104 | 105 | /** 106 | * Handle socket of RTSP. 107 | * @arg: Should be the struct rtsp_sess. 108 | * 109 | * NOTE: 110 | * This function will also handle RTSP/RTCP over TCP 111 | * when it's in the RTSP interleaved mode. 112 | */ 113 | int handle_rtsp_sd(struct sd_handler *hp, unsigned int event) 114 | { 115 | struct rtsp_sess *sessp = (struct rtsp_sess *)hp->arg; 116 | enum data_type type = 0; 117 | 118 | if (event & EPOLLIN) { /* Readable. */ 119 | /* Receive data from RTSP client. */ 120 | if (filter_data_from_rtsp_sd(sessp) < 0) { 121 | destroy_rtsp_sess(sessp); 122 | return 0; 123 | } 124 | } 125 | 126 | if (event & EPOLLOUT) { /* Writable. */ 127 | type = sessp->intlvd_mode ? 128 | (DATA_TYPE_RTSP_RESP | 129 | DATA_TYPE_RTP_V_PKT | 130 | DATA_TYPE_RTP_A_PKT | 131 | DATA_TYPE_RTCP_V_PKT | 132 | DATA_TYPE_RTCP_A_PKT) : 133 | DATA_TYPE_RTSP_RESP; 134 | if (consume_send_buf(sessp, type) < 0) { 135 | destroy_rtsp_sess(sessp); 136 | } 137 | } 138 | 139 | return 0; 140 | } 141 | 142 | /** 143 | * Handle socket of RTP. 144 | * @arg: Should be the struct rtsp_sess. 145 | * 146 | * NOTE: 147 | * Strictly speaking, handling non-interleaved mode only. 148 | */ 149 | int handle_rtp_sd(struct sd_handler *hp, unsigned int event) 150 | { 151 | struct rtsp_sess *sessp = (struct rtsp_sess *)hp->arg; 152 | enum data_type type = 0; 153 | 154 | if (hp->sd == sessp->rtp_rtcp[0].udp.rtp_sd) { 155 | type = DATA_TYPE_RTP_V_PKT; 156 | } else { 157 | type = DATA_TYPE_RTP_A_PKT; 158 | } 159 | 160 | if (event & EPOLLOUT) { /* Writable. */ 161 | if (consume_send_buf(sessp, type) < 0) { 162 | destroy_rtsp_sess(sessp); 163 | } 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | /** 170 | * Handle socket of RTCP. 171 | * @arg: Should be the struct rtsp_sess. 172 | * 173 | * NOTE: 174 | * Strictly speaking, handling non-interleaved mode only. 175 | */ 176 | int handle_rtcp_sd(struct sd_handler *hp, unsigned int event) 177 | { 178 | return 0; 179 | } 180 | 181 | void do_sd_handler(int sd, unsigned int event) 182 | { 183 | struct sd_handler *hp = NULL; /* Used as loop curser. */ 184 | 185 | /* Lookup current sd in sd handler list. */ 186 | list_for_each_entry(hp, &rtsp_srv.sd_handler_list, entry) { 187 | if (hp->sd == sd) { /* Matched. */ 188 | hp->proc(hp, event); 189 | break; 190 | } 191 | } 192 | 193 | return; 194 | } 195 | 196 | /** 197 | * Register sd handler and add it to the handler list. 198 | */ 199 | int register_sd_handler(int sd, sd_handler_proc_t proc, void *arg) 200 | { 201 | struct sd_handler *hp = NULL; 202 | 203 | hp = mallocz(sizeof(*hp)); 204 | if (!hp) { 205 | printd(ERR "Allocate memory for struct sd_handler failed!\n"); 206 | return -1; 207 | } 208 | 209 | hp->sd = sd; 210 | hp->arg = arg; 211 | hp->proc = proc; 212 | list_add_tail(&hp->entry, &rtsp_srv.sd_handler_list); 213 | return 0; 214 | } 215 | 216 | /** 217 | * Unregister sd handler and delete it from the handler list. 218 | */ 219 | void deregister_sd_handler(int sd) 220 | { 221 | struct sd_handler *hp = NULL; 222 | 223 | list_for_each_entry(hp, &rtsp_srv.sd_handler_list, entry) { 224 | if (hp->sd == sd) { 225 | list_del(&hp->entry); 226 | freez(hp); 227 | break; 228 | } 229 | } 230 | return; 231 | } 232 | 233 | void init_sd_handler_list(void) 234 | { 235 | INIT_LIST_HEAD(&rtsp_srv.sd_handler_list); 236 | return; 237 | } 238 | 239 | void deinit_sd_handler_list(void) 240 | { 241 | struct sd_handler *hp = NULL; 242 | struct sd_handler *tmp = NULL; 243 | 244 | list_for_each_entry_safe(hp, tmp, &rtsp_srv.sd_handler_list, entry) { 245 | list_del(&hp->entry); 246 | freez(hp); 247 | } 248 | return; 249 | } 250 | -------------------------------------------------------------------------------- /src/sd_handler.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : sd_handler.h 3 | * Description : Handlers of socket descriptors. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-28 6 | ********************************************************************/ 7 | 8 | #ifndef __SD_HANDLER_H__ 9 | #define __SD_HANDLER_H__ 10 | 11 | struct sd_handler; 12 | typedef int (*sd_handler_proc_t)(struct sd_handler *, unsigned int); 13 | 14 | /* 15 | * Handler of socket descriptors. 16 | * Called when there's event occured on socket. 17 | */ 18 | struct sd_handler { 19 | struct list_head entry; /* Entry of handler_list. */ 20 | int sd; /* Can be all kinds of socket descriptors. */ 21 | void *arg; /* Argument for the callback function. */ 22 | sd_handler_proc_t proc; /* Callback of the handler. */ 23 | }; 24 | 25 | 26 | void init_sd_handler_list(void); 27 | void deinit_sd_handler_list(void); 28 | 29 | int handle_lsn_sd(struct sd_handler *hp, unsigned int event); 30 | int handle_rtsp_sd(struct sd_handler *hp, unsigned int event); 31 | int handle_rtp_sd(struct sd_handler *hp, unsigned int event); 32 | int handle_rtcp_sd(struct sd_handler *hp, unsigned int event); 33 | 34 | void do_sd_handler(int sd, unsigned int event); 35 | 36 | int register_sd_handler(int sd, sd_handler_proc_t proc, void *arg); 37 | void deregister_sd_handler(int sd); 38 | 39 | 40 | #endif /* __SD_HANDLER_H__ */ 41 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : util.c 3 | * Description : Provide some utilities. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-27 6 | ********************************************************************/ 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "log.h" 19 | #include "util.h" 20 | 21 | 22 | struct code_and_reason { 23 | enum status_code code; 24 | const char *reason; 25 | }; 26 | 27 | static struct code_and_reason status_code_and_reason[] = { 28 | {CONTINUE, "Continue"}, 29 | {OK, "OK"}, 30 | {CREATED, "Created"}, 31 | {LOW_ON_STORAGE_SPACE, "Low on Storage Space"}, 32 | {MULTIPLE_CHOICES, "Multiple Choices"}, 33 | {MOVED_PERMANENTLY, "Moved Permanently"}, 34 | {MOVED_TEMPORARILY, "Moved Temporarily"}, 35 | {SEE_OTHER, "See Other"}, 36 | {NOT_MODIFIED, "Not Modified"}, 37 | {USE_PROXY, "Use Proxy"}, 38 | {BAD_REQUEST, "Bad Request"}, 39 | {UNAUTHORIZED, "Unauthorized"}, 40 | {PAYMENT_REQUIRED, "Payment Required"}, 41 | {FORBIDDEN, "Forbidden"}, 42 | {NOT_FOUND, "Not Found"}, 43 | {METHOD_NOT_ALLOWED, "Method Not Allowed"}, 44 | {NOT_ACCEPTABLE, "Not Acceptable"}, 45 | {PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required"}, 46 | {REQUEST_TIME_OUT, "Request Time-out"}, 47 | {GONE, "Gone"}, 48 | {LENGTH_REQUIRED, "Length Required"}, 49 | {PRECONDITION_FAILED, "Precondition Failed"}, 50 | {REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large"}, 51 | {REQUEST_URI_TOO_LARGE, "Request-URI Too Large"}, 52 | {UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type"}, 53 | {PARAMETER_NOT_UNDERSTOOD, "Parameter Not Understood"}, 54 | {CONFERENCE_NOT_FOUND, "Conference Not Found"}, 55 | {NOT_ENOUGH_BANDWIDTH, "Not Enough Bandwidth"}, 56 | {SESSION_NOT_FOUND, "Session Not Found"}, 57 | {METHOD_NOT_VALID_IN_THIS_STATE, "Method Not Valid in This State"}, 58 | {HEADER_FIELD_NOT_VALID_FOR_RESOURCE, "Header Field Not Valid for Resource"}, 59 | {INVALID_RANGE, "Invalid Range"}, 60 | {PARAMETER_IS_READ_ONLY, "Parameter Is Read-Only"}, 61 | {AGGREGATE_OPERATION_NOT_ALLOWED, "Aggregate operation not allowed"}, 62 | {ONLY_AGGREGATE_OPERATION_ALLOWED, "Only aggregate operation allowed"}, 63 | {UNSUPPORTED_TRANSPORT, "Unsupported transport"}, 64 | {DESTINATION_UNREACHABLE, "Destination unreachable"}, 65 | {INTERNAL_SERVER_ERROR, "Internal Server Error"}, 66 | {NOT_IMPLEMENTED, "Not Implemented"}, 67 | {BAD_GATEWAY, "Bad Gateway"}, 68 | {SERVICE_UNAVAILABLE, "Service Unavailable"}, 69 | {GATEWAY_TIME_OUT, "Gateway Time-out"}, 70 | {VERSION_NOT_SUPPORTED, "Version not supported"}, 71 | {OPTION_NOT_SUPPORTED, "Option not supported"}, 72 | }; 73 | 74 | /* 75 | * Set socket descriptor block mode: 76 | * block: 1 77 | * nonblock: 0 78 | */ 79 | int set_block_mode(int fd, int mode) 80 | { 81 | int flags = 0; 82 | 83 | if ((flags = fcntl(fd, F_GETFL)) < 0) { 84 | perrord(ERR "Get flags of socket error"); 85 | return -1; 86 | } 87 | 88 | flags = mode ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); 89 | if (fcntl(fd, F_SETFL, flags) < 0) { 90 | perrord(ERR "Set flags of socket error"); 91 | return -1; 92 | } 93 | return 0; 94 | } 95 | 96 | /* 97 | * Create non-blocking socket and bind to the given port. 98 | */ 99 | int create_and_bind(unsigned short port) 100 | { 101 | int sd = -1; /* Listen socket. */ 102 | struct sockaddr_in sa; /* Address of RTSP listen socket. */ 103 | int reuse = 1; 104 | 105 | memset(&sa, 0, sizeof(sa)); 106 | sa.sin_family = AF_INET; 107 | sa.sin_addr.s_addr = htonl(INADDR_ANY); 108 | sa.sin_port = htons(port); 109 | 110 | sd = socket(AF_INET, SOCK_STREAM, 0); 111 | if (sd < 0) { 112 | printd(EMERG "Create rtsp server listening socket error"); 113 | return -1; 114 | } 115 | 116 | /* Set socket in nonblock mode. */ 117 | if (set_block_mode(sd, 0) < 0) { 118 | goto err; 119 | } 120 | 121 | if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, 122 | &reuse, sizeof(reuse)) < 0) { 123 | printd(ERR "Set listening socket option[SO_REUSEADDR] error"); 124 | goto err; 125 | } 126 | if (bind(sd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 127 | printd(EMERG "Bind listening sockaddr to local port[%hd] error: %s\n", 128 | port, strerror(errno)); 129 | goto err; 130 | } 131 | 132 | return sd; 133 | err: 134 | close(sd); 135 | return -1; 136 | } 137 | 138 | const char *get_status_reason(unsigned int code) 139 | { 140 | int i = 0; 141 | const char *reason = "Corresponding Reason Undefined"; 142 | int num = sizeof(status_code_and_reason) / sizeof(*status_code_and_reason); 143 | 144 | for (i = 0; i < num; i++) { 145 | if (status_code_and_reason[i].code == code) { 146 | reason = status_code_and_reason[i].reason; 147 | } 148 | } 149 | return reason; 150 | } 151 | 152 | 153 | /** 154 | * Skip over the first NALU start code found in address [start, end). 155 | * Return position right after the start code. 156 | */ 157 | char *skip_nalu_start_code(char *start, char *end) 158 | { 159 | char *ptr = start; 160 | 161 | while (end - ptr > 3) { 162 | if (*ptr == 0 && *(ptr + 1) == 0 && *(ptr + 2) == 0 && *(ptr + 3) == 1) { 163 | return (ptr + 4); 164 | } else if (*ptr == 0 && *(ptr + 1) == 0 && *(ptr + 2) == 1) { 165 | return (ptr + 3); 166 | } else { 167 | ptr++; 168 | } 169 | } 170 | return NULL; 171 | } 172 | 173 | /** 174 | * @nalu: point to the start of NALU, has skipped the NALU start code. 175 | * Return size of the NALU. 176 | */ 177 | int get_nalu_sz(char *nalu, char *end) 178 | { 179 | #if 1 180 | const char *p = nalu; 181 | while (1) { 182 | if ((p >= end) || 183 | (p[0] == 0x0 && p[1] == 0x0 && p[2] == 0x0 && p[3] == 0x0)) { 184 | p = end; 185 | break; 186 | } else if (p[0] == 0x0 && p[1] == 0x0 && p[2] == 0x0 && p[3] == 0x01) { 187 | if (p + 4 >= end) { 188 | p = end; 189 | } 190 | break; 191 | } else if (p[0] == 0x0 && p[1] == 0x0 && p[2] == 0x01) { 192 | if (p + 3 >= end) { 193 | p = end; 194 | } 195 | break; 196 | } 197 | p++; 198 | } 199 | return p - nalu; 200 | #else 201 | char *ptr = nalu; 202 | 203 | while (ptr < end) { 204 | if (end - ptr > 3) { 205 | if ((*ptr == 0 && *(ptr + 1) == 0 && *(ptr + 2) == 0 && *(ptr + 3) == 1) || 206 | (*ptr == 0 && *(ptr + 1) == 0 && *(ptr + 2) == 1)) { 207 | return (ptr - nalu); 208 | } else if (*ptr == 0 && *(ptr + 1) == 0 && *(ptr + 2) == 0 && *(ptr + 3) == 0) { 209 | return ptr - nalu; 210 | } 211 | } else { 212 | return (end - nalu); 213 | } 214 | ptr++; 215 | } 216 | return 0; 217 | #endif 218 | } 219 | 220 | /** 221 | * We will allocate memory here, remember to free the returned memory. 222 | */ 223 | char *encode_base64(const char *src, unsigned int src_sz) 224 | { 225 | static const char b64[] = 226 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 227 | char *res = NULL; 228 | char *dst = NULL; 229 | unsigned bits = 0; 230 | int shift = 0; 231 | unsigned int left = 0; 232 | int padded = 0; 233 | unsigned int res_sz = 0; 234 | 235 | if (src_sz >= UINT_MAX / 4) { 236 | printd("src_sz >= (UINT_MAX / 4) T_T\n"); 237 | return NULL; 238 | } 239 | 240 | padded = src_sz > (src_sz / 3 * 3); 241 | res_sz = 4 * (src_sz / 3 + padded); 242 | res = mallocz(res_sz + 1); /* Extra 1 size allowing for trailing '\0'. */ 243 | if (!res) { 244 | printd("Allocate memory for encode_base64 failed!\n"); 245 | return NULL; 246 | } 247 | dst = res; 248 | left = src_sz; 249 | while (left) { 250 | bits = (bits << 8) + *src++; 251 | left--; 252 | shift += 8; 253 | 254 | do { 255 | *dst++ = b64[(bits << 6 >> shift) & 0x3f]; 256 | shift -= 6; 257 | } while (shift > 6 || (left == 0 && shift > 0)); 258 | } 259 | while ((dst - res) & 3) { 260 | *dst++ = '='; 261 | } 262 | *dst = '\0'; 263 | 264 | return res; 265 | } 266 | 267 | /** 268 | * Return a 64 bits random number. 269 | */ 270 | unsigned long long random64(void) 271 | { 272 | return ((long long)random() << 32) | random(); 273 | } 274 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * File Name : util.h 3 | * Description : Provide some utilities. 4 | * Author : Hu Lizhen 5 | * Create Date : 2012-11-27 6 | ********************************************************************/ 7 | 8 | 9 | #ifndef __UTIL_H__ 10 | #define __UTIL_H__ 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifndef UINT_MAX 19 | #define UINT_MAX 0xFFFFFFFF 20 | #endif 21 | 22 | #ifndef INT_MAX 23 | #define INT_MAX 0x7FFFFFFF 24 | #endif 25 | 26 | 27 | enum status_code { 28 | CONTINUE = 100, 29 | OK = 200, 30 | CREATED = 201, 31 | LOW_ON_STORAGE_SPACE = 250, 32 | MULTIPLE_CHOICES = 300, 33 | MOVED_PERMANENTLY = 301, 34 | MOVED_TEMPORARILY = 302, 35 | SEE_OTHER = 303, 36 | NOT_MODIFIED = 304, 37 | USE_PROXY = 305, 38 | BAD_REQUEST = 400, 39 | UNAUTHORIZED = 401, 40 | PAYMENT_REQUIRED = 402, 41 | FORBIDDEN = 403, 42 | NOT_FOUND = 404, 43 | METHOD_NOT_ALLOWED = 405, 44 | NOT_ACCEPTABLE = 406, 45 | PROXY_AUTHENTICATION_REQUIRED = 407, 46 | REQUEST_TIME_OUT = 408, 47 | GONE = 410, 48 | LENGTH_REQUIRED = 411, 49 | PRECONDITION_FAILED = 412, 50 | REQUEST_ENTITY_TOO_LARGE = 413, 51 | REQUEST_URI_TOO_LARGE = 414, 52 | UNSUPPORTED_MEDIA_TYPE = 415, 53 | PARAMETER_NOT_UNDERSTOOD = 451, 54 | CONFERENCE_NOT_FOUND = 452, 55 | NOT_ENOUGH_BANDWIDTH = 453, 56 | SESSION_NOT_FOUND = 454, 57 | METHOD_NOT_VALID_IN_THIS_STATE = 455, 58 | HEADER_FIELD_NOT_VALID_FOR_RESOURCE = 456, 59 | INVALID_RANGE = 457, 60 | PARAMETER_IS_READ_ONLY = 458, 61 | AGGREGATE_OPERATION_NOT_ALLOWED = 459, 62 | ONLY_AGGREGATE_OPERATION_ALLOWED = 460, 63 | UNSUPPORTED_TRANSPORT = 461, 64 | DESTINATION_UNREACHABLE = 462, 65 | INTERNAL_SERVER_ERROR = 500, 66 | NOT_IMPLEMENTED = 501, 67 | BAD_GATEWAY = 502, 68 | SERVICE_UNAVAILABLE = 503, 69 | GATEWAY_TIME_OUT = 504, 70 | VERSION_NOT_SUPPORTED = 505, 71 | OPTION_NOT_SUPPORTED = 551, 72 | }; 73 | 74 | /* Wrapped function [malloc() and free()], do the nessary stuff. */ 75 | #define mallocz(sz) \ 76 | ({ \ 77 | void *ptr = malloc(sz); \ 78 | if (ptr) { \ 79 | memset(ptr, 0, sz); \ 80 | } \ 81 | ptr; \ 82 | }) 83 | 84 | #define freez(ptr) \ 85 | ({ \ 86 | if (ptr) { \ 87 | free(ptr); \ 88 | ptr = NULL; \ 89 | } \ 90 | }) 91 | 92 | /** 93 | * Print elapsed time in micro-seconds since last time. 94 | * 95 | * NOTE: 96 | * non-reentrant. 97 | */ 98 | #define elapsed_time() do { \ 99 | struct timeval now_tv; \ 100 | static struct timeval old_tv; \ 101 | gettimeofday(&now_tv, NULL); \ 102 | printd(DEBUG "---------------------------------" \ 103 | "Elapsed micro-seconds since last time: [%d]us\n", \ 104 | (now_tv.tv_sec * MILLION + now_tv.tv_usec) - \ 105 | (old_tv.tv_sec * MILLION + old_tv.tv_usec)); \ 106 | old_tv.tv_sec = now_tv.tv_sec; \ 107 | old_tv.tv_usec = now_tv.tv_usec; \ 108 | } while (0) 109 | 110 | /* 111 | * Set socket descriptor block mode: 112 | * block: 1 113 | * nonblock: 0 114 | */ 115 | int set_block_mode(int sd, int mode); 116 | 117 | int create_and_bind(unsigned short port); 118 | const char *get_status_reason(unsigned int code); 119 | 120 | char *skip_nalu_start_code(char *start, char *end); 121 | int get_nalu_sz(char *nalu, char *end); 122 | 123 | char *encode_base64(const char *src, unsigned int src_sz); 124 | unsigned long long random64(void); 125 | 126 | 127 | #endif /* __UTIL_H__ */ 128 | --------------------------------------------------------------------------------