int sockets_add(int sock, struct sockaddr_in *sa, int sid, int type, socket_action a, socket_action c, socket_action t) { int i; char ra[50]; if (init_sock == 0) { init_sock = 1; for (i = 0; i < MAX_SOCKS; i++) s[i].sock = -1; } for (i = 0; i < MAX_SOCKS; i++) if (s[i].sock < 0) break; if (i == MAX_SOCKS) return -1; s[i].sock = sock; memset(&s[i].sa, 0, sizeof(s[i].sa)); if (sa) memcpy(&s[i].sa, sa, sizeof(s[i].sa)); s[i].action = s[i].close = s[i].timeout = NULL; if (a) s[i].action = a; if (c) s[i].close = c; if (t) s[i].timeout = t; s[i].sid = sid; s[i].type = type & ~TYPE_CONNECT; s[i].rtime = getTick(); s[i].wtime = 0; if (max_sock <= i) max_sock = i + 1; s[i].buf = NULL; s[i].lbuf = 0; s[i].close_sec = 0; s[i].id = i; s[i].read = (read_action) sockets_read; if (s[i].type == TYPE_UDP || s[i].type == TYPE_RTCP) s[i].read = (read_action) sockets_recv; else if (s[i].type == TYPE_SERVER) s[i].read = (read_action) sockets_accept; pf[i].fd = sock; pf[i].events = POLLIN | POLLPRI; if (type & TYPE_CONNECT) pf[i].events |= POLLOUT; pf[i].revents = 0; LOG( "sockets_add: handle %d (type %d) returning socket index %d [%s:%d] read: %p", s[i].sock, s[i].type, i, get_socket_rhost(i, ra, sizeof(ra)), ntohs(s[i].sa.sin_port), s[i].read); return i; }
int sockets_add(int sock, struct sockaddr_in *sa, int sid, int type, socket_action a, socket_action c, socket_action t) { int i; char ra[50]; sockets *ss; i = add_new_lock((void **) s, MAX_SOCKS, sizeof(sockets), &s_mutex); if (i == -1) LOG_AND_RETURN(-1, "sockets_add failed for socks %d", sock); ss = s[i]; ss->enabled = 1; ss->sock = sock; ss->tid = get_tid(); memset(&ss->sa, 0, sizeof(ss->sa)); if (sa) memcpy(&ss->sa, sa, sizeof(ss->sa)); ss->action = ss->close = ss->timeout = NULL; if (a) ss->action = a; if (c) ss->close = c; if (t) ss->timeout = t; ss->sid = sid; ss->type = type & ~TYPE_CONNECT; ss->rtime = getTick(); ss->wtime = 0; if (max_sock <= i) max_sock = i + 1; ss->buf = NULL; ss->lbuf = 0; ss->timeout_ms = 0; ss->id = i; ss->read = (read_action) sockets_read; ss->lock = NULL; if (ss->type == TYPE_UDP || ss->type == TYPE_RTCP) ss->read = (read_action) sockets_recv; else if (ss->type == TYPE_SERVER) ss->read = (read_action) sockets_accept; ss->events = POLLIN | POLLPRI; if (type & TYPE_CONNECT) ss->events |= POLLOUT; LOG( "sockets_add: handle %d (type %d) returning socket index %d [%s:%d] read: %p", ss->sock, ss->type, i, get_socket_rhost(i, ra, sizeof(ra)), ntohs(ss->sa.sin_port), ss->read); mutex_unlock(&ss->mutex); return i; }
void *select_and_execute(void *arg) { fd_set io; int i, rv, rlen, les, es; unsigned char buf[2001]; int err; struct pollfd pf[MAX_SOCKS]; int64_t lt, c_time; int read_ok; char ra[50]; if (arg) thread_name = (char *) arg; else thread_name = "main"; tid = get_tid(); les = 1; es = 0; lt = getTick(); memset(&pf, -1, sizeof(pf)); LOG("Starting select_and_execute on thread ID %x, thread_name %s", tid, thread_name); while (run_loop) { c_time = getTick(); es = 0; clean_mutexes(); for (i = 0; i < max_sock; i++) if (s[i] && s[i]->enabled && s[i]->tid == tid) { pf[i].fd = s[i]->sock; pf[i].events = s[i]->events; pf[i].revents = 0; s[i]->last_poll = c_time; es++; } else { pf[i].fd = -1; pf[i].events = pf[i].revents = 0; } i = -1; if (les == 0 && es == 0 && tid != main_tid) { LOG("No enabled sockets for Thread ID %lx name %s ... exiting ", tid, thread_name); break; } les = es; // LOG("start select"); if ((rv = poll(pf, max_sock, 100)) < 0) { LOG("select_and_execute: select() error %d: %s", errno, strerror(errno)); continue; } // LOG("select returned %d",rv); if (rv > 0) while (++i < max_sock) if ((pf[i].fd >= 0) && pf[i].revents) { sockets *ss = s[i]; if (!ss) continue; c_time = getTick(); LOGL(6, "event on socket index %d handle %d type %d (poll fd:%d, revents=%d)", i, ss->sock, ss->type, pf[i].fd, pf[i].revents); sockets_lock(ss); if (pf[i].revents & POLLOUT) { ss->events &= ~POLLOUT; } if (!ss->buf || ss->buf == buf) { ss->buf = buf; ss->lbuf = sizeof(buf) - 1; ss->rlen = 0; } if (ss->rlen >= ss->lbuf) { LOG( "Socket buffer full, handle %d, sock_id %d, type %d, lbuf %d, rlen %d, ss->buf = %p, buf %p", ss->sock, i, ss->type, ss->lbuf, ss->rlen, ss->buf, buf); ss->rlen = 0; } rlen = 0; if (opts.bw > 0 && bw > opts.bw && ss->type == TYPE_DVR) { int64_t ms = 1000 - c_time + bwtt; if (bwnotify++ == 0) LOG( "capping %d sock %d for the next %jd ms, sleeping for the next %jd ms", i, ss->sock, ms, ms / 50); if (ms > 50) usleep(ms * 20); sockets_unlock(ss); continue; } read_ok = ss->read(ss->sock, &ss->buf[ss->rlen], ss->lbuf - ss->rlen, ss, &rlen); if (opts.log >= 1) { int64_t now = getTick(); if (now - c_time > 100) LOG( "WARNING: read on socket id %d, handle %d, took %jd ms", ss->id, ss->sock, now - c_time); } err = 0; if (rlen < 0) err = errno; if (rlen > 0) ss->rtime = c_time; if (read_ok && rlen >= 0) ss->rlen += rlen; else ss->rlen = 0; //force 0 at the end of the string if (ss->lbuf >= ss->rlen) ss->buf[ss->rlen] = 0; LOGL(6, "Read %s %d (rlen:%d/total:%d) bytes from %d -> %p - iteration %d action %p", read_ok ? "OK" : "NOK", rlen, ss->rlen, ss->lbuf, ss->sock, ss->buf, it++, ss->action); if (((ss->rlen > 0) || err == EWOULDBLOCK) && ss->action && (ss->type != TYPE_SERVER)) ss->action(ss); sockets_unlock(ss); if (!read_ok && ss->type != TYPE_SERVER) { char *err_str; char *types[] = { "udp", "tcp", "server", "http", "rtsp", "dvr" }; if (rlen == 0) { err = 0; err_str = "Close"; } else if (err == EOVERFLOW) err_str = "EOVERFLOW"; else if (err == EWOULDBLOCK) err_str = "Connected"; else err_str = strerror(err); if (ss->type == TYPE_RTCP || ss->sock == SOCK_TIMEOUT) { LOG( "ignoring error on sock_id %d handle %d type %d error %d : %s", ss->id, ss->sock, ss->type, err, err_str); continue; // do not close the RTCP socket, we might get some errors here but ignore them } LOG( "select_and_execute[%d]: %s on socket %d (sid:%d) from %s:%d - type %s errno %d", i, err_str, ss->sock, ss->sid, get_socket_rhost(ss->id, ra, sizeof(ra)), ntohs(ss->sa.sin_port), types[ss->type], err); if (err == EOVERFLOW || err == EWOULDBLOCK) continue; if (err == EAGAIN) { ss->err++; if (ss->err < 10) continue; } sockets_del(i); LOG("Delete socket %d done: sid %d", i, ss->sid); continue; } // ss->err = 0; } // checking every 60seconds for idle connections - or if select times out c_time = getTick(); if (rv == 0 || (c_time - lt >= 200)) { sockets *ss; lt = c_time; i = -1; while (++i < max_sock) if ((ss = get_sockets(i)) && (ss->tid == tid) && ((ss->timeout_ms > 0 && lt - ss->rtime > ss->timeout_ms) || (ss->timeout_ms == 1))) { if (ss->timeout) { int rv; if (ss->sock == SOCK_TIMEOUT) ss->rtime = getTick(); sockets_lock(ss); rv = ss->timeout(ss); sockets_unlock(ss); if (rv) sockets_del(i); } if (!ss->timeout) sockets_del(i); } } } clean_mutexes(); if (tid == main_tid) LOG("The main loop ended, run_loop = %d", run_loop); add_join_thread(tid); return NULL; }
int read_rtsp(sockets * s) { char *arg[50]; int cseq, la, i, rlen; char *transport = NULL; int sess_id = 0; char buf[2000]; char tmp_ra[50]; streams *sid = get_sid(s->sid); if (s->buf[0] == 0x24 && s->buf[1] < 2) { if (sid) sid->rtime = s->rtime; int rtsp_len = s->buf[2] * 256 + s->buf[3]; LOG( "Received RTSP over tcp packet (sock_id %d, stream %d, rlen %d) packet len: %d, type %02X %02X discarding %s...", s->id, s->sid, s->rlen, rtsp_len, s->buf[4], s->buf[5], (s->rlen == rtsp_len + 4) ? "complete" : "fragment"); if (s->rlen == rtsp_len + 4) { // we did not receive the entire packet s->rlen = 0; return 0; } } if (s->rlen < 4 || !end_of_header(s->buf + s->rlen - 4)) { if (s->rlen > RBUF - 10) { LOG( "Discarding %d bytes from the socket buffer, request > %d, consider increasing RBUF", s->rlen, RBUF); s->rlen = 0; } LOG( "read_rtsp: read %d bytes from handle %d, sock_id %d, flags %d not ending with \\r\\n\\r\\n", s->rlen, s->sock, s->id, s->flags); if (s->flags & 1) return 0; unsigned char *new_alloc = malloc1(RBUF); memcpy(new_alloc, s->buf, s->rlen); s->buf = new_alloc; s->flags = s->flags | 1; return 0; } rlen = s->rlen; s->rlen = 0; LOG("read RTSP (from handle %d sock_id %d, len: %d, sid %d):\n%s", s->sock, s->id, s->rlen, s->sid, s->buf); if ((s->type != TYPE_HTTP) && (strncasecmp((const char*) s->buf, "GET", 3) == 0)) { http_response(s, 404, NULL, NULL, 0, 0); return 0; } la = split(arg, (char*) s->buf, 50, ' '); cseq = 0; if (la < 2) LOG_AND_RETURN(0, "Most likely not an RTSP packet sock_id: %d sid: %d rlen: %d, dropping ....", s->id, s->sid, rlen); if (s->sid < 0) for (i = 0; i < la; i++) if (strncasecmp("Session:", arg[i], 8) == 0) { sess_id = map_int(header_parameter(arg, i), NULL); s->sid = find_session_id(sess_id); } /* if (s->sid < 0 && strstr(arg[1], "stream=")) // stream= in URL { char *str_id = strstr(arg[1], "stream=") + 7; sess_id = map_intd(str_id, NULL, 0) - 1; if ((sid = get_sid(sess_id))) { s->sid = sid->sid; LOG("Adopting the session id from the stream %d", s->sid); } } */ if (strstr(arg[1], "freq") || strstr(arg[1], "pids")) { sid = (streams *) setup_stream(arg[1], s); } // if(!get_sid(s->sid) && ((strncasecmp (arg[0], "PLAY", 4) == 0) || (strncasecmp (arg[0], "GET", 3) == 0) || (strncasecmp (arg[0], "SETUP", 5) == 0))) // sid = (streams *) setup_stream (arg[1], s); //setup empty stream sid = get_sid(s->sid); if (sid) sid->rtime = s->rtime; if (sess_id) set_session_id(s->sid, sess_id); for (i = 0; i < la; i++) if (strncasecmp("CSeq:", arg[i], 5) == 0) cseq = map_int(header_parameter(arg, i), NULL); else if (strncasecmp("Transport:", arg[i], 9) == 0) { transport = header_parameter(arg, i); if (-1 == decode_transport(s, transport, opts.rrtp, opts.start_rtp)) { http_response(s, 400, NULL, NULL, cseq, 0); return 0; } } else if (strstr(arg[i], "LIVE555")) { if (sid) sid->timeout = 0; } else if (strstr(arg[i], "Lavf")) { if (sid) sid->timeout = 0; } if ((strncasecmp(arg[0], "PLAY", 4) == 0) || (strncasecmp(arg[0], "GET", 3) == 0) || (strncasecmp(arg[0], "SETUP", 5) == 0)) { char ra[100]; int rv; if (!(sid = get_sid(s->sid))) { http_response(s, 454, NULL, NULL, cseq, 0); return 0; } if ((strncasecmp(arg[0], "PLAY", 3) == 0) || (strncasecmp(arg[0], "GET", 3) == 0)) if ((rv = start_play(sid, s)) < 0) { http_response(s, -rv, NULL, NULL, cseq, 0); return 0; } get_socket_rhost(sid->sid, ra, sizeof(ra)); buf[0] = 0; if (transport) { int s_timeout; if (sid->timeout == 1) sid->timeout = opts.timeout_sec; s_timeout = ( (sid->timeout > 20000) ? sid->timeout : opts.timeout_sec) / 1000; switch (sid->type) { case STREAM_RTSP_UDP: if (atoi(ra) < 224) snprintf(buf, sizeof(buf), "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\nSession: %010d;timeout=%d\r\ncom.ses.streamID: %d", ra, get_sock_shost(s->sock), get_stream_rport(sid->sid), get_stream_rport(sid->sid) + 1, // opts.start_rtp, opts.start_rtp + 1, get_sock_sport(sid->rsock), get_sock_sport(sid->rtcp), get_session_id(s->sid), s_timeout, sid->sid + 1); else snprintf(buf, sizeof(buf), "Transport: RTP/AVP;multicast;destination=%s;port=%d-%d\r\nSession: %010d;timeout=%d\r\ncom.ses.streamID: %d", ra, get_stream_rport(sid->sid), ntohs (sid->sa.sin_port) + 1, get_session_id(s->sid), s_timeout, sid->sid + 1); break; case STREAM_RTSP_TCP: snprintf(buf, sizeof(buf), "Transport: RTP/AVP/TCP;interleaved=0-1\r\nSession: %010d;timeout=%d\r\ncom.ses.streamID: %d", get_session_id(s->sid), s_timeout, sid->sid + 1); break; } } if (strncasecmp(arg[0], "PLAY", 4) == 0) { char *qm = strchr(arg[1], '?'); if (qm) *qm = 0; if (buf[0]) strcat(buf, "\r\n"); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1, "RTP-Info: url=%s;seq=%d;rtptime=%lld\r\nRange: npt=0.000-", arg[1], getTick(), (long long int) (getTickUs() / 1000000)); } if (buf[0] == 0 && sid->type == STREAM_HTTP) snprintf(buf, sizeof(buf), "Content-Type: video/mp2t"); http_response(s, 200, buf, NULL, cseq, 0); } else if (strncmp(arg[0], "TEARDOWN", 8) == 0) { buf[0] = 0; if (get_sid(s->sid)) sprintf(buf, "Session: %010d", get_session_id(s->sid)); close_stream(s->sid); http_response(s, 200, buf, NULL, cseq, 0); } else { if (strncmp(arg[0], "DESCRIBE", 8) == 0) { char sbuf[1000]; char *rv = NULL; rv = describe_streams(s, arg[1], sbuf, sizeof(sbuf)); if (!rv) { http_response(s, 404, NULL, NULL, cseq, 0); return 0; } snprintf(buf, sizeof(buf), "Content-type: application/sdp\r\nContent-Base: rtsp://%s/", get_sock_shost(s->sock)); http_response(s, 200, buf, sbuf, cseq, 0); } else if (strncmp(arg[0], "OPTIONS", 8) == 0) { http_response(s, 200, public, NULL, cseq, 0); } }
int select_and_execute() { fd_set io; int i, rv, rlen; unsigned char buf[2001]; int err; run_loop = 1; int lt, read_ok, c_time; char ra[50]; lt = getTick(); while (run_loop) { FD_ZERO(&io); i = -1; // LOG("start select"); if ((rv = poll(pf, max_sock, 100)) < 0) { perror("select_and_execute: select() error"); continue; } c_time = getTick(); // LOG("select returned %d",rv); if (rv > 0) while (++i < max_sock) if (pf[i].revents) { sockets *ss = &s[i]; c_time = getTick(); LOGL(6, "event on socket index %d handle %d type %d (poll fd:%d, revents=%d)", i, ss->sock, ss->type, pf[i].fd, pf[i].revents); if (pf[i].revents & POLLOUT) { pf[i].events &= ~POLLOUT; } if (!ss->buf || ss->buf == buf) { ss->buf = buf; ss->lbuf = sizeof(buf) - 1; ss->rlen = 0; } if (ss->rlen >= ss->lbuf) { LOG( "Socket buffer full, handle %d, sock_id %d, type %d, lbuf %d, rlen %d, ss->buf = %p, buf %p", ss->sock, i, ss->type, ss->lbuf, ss->rlen, ss->buf, buf); ss->rlen = 0; } rlen = 0; if (c_time - bwtt > 1000) { bwtt = c_time; tbw += bw; if (bw > 2000) LOG( "BW %dKB/s, Total BW: %ld MB, ns/read %lld, r: %d, tt: %lld ms, n: %d (s: %d ms, s_cnt %d)", (int ) bw / 1024, tbw / 1024576, nsecs / reads, reads, nsecs / 1000, bwnotify, sleeping / 1000, sleeping_cnt); bw = 0; bwnotify = 0; nsecs = 0; reads = 0; sleeping = sleeping_cnt = 0; } if (opts.bw > 0 && bw > opts.bw && ss->type == TYPE_DVR) { int ms = 1000 - c_time + bwtt; if (bwnotify++ == 0) LOG( "capping %d sock %d for the next %d ms, sleeping for the next %d ms", i, ss->sock, ms, ms / 50); if (ms > 50) usleep(ms * 20); continue; } read_ok = ss->read(ss->sock, &ss->buf[ss->rlen], ss->lbuf - ss->rlen, ss, &rlen); if (opts.log >= 1) { int now = getTick(); if (now - c_time > 100) LOG( "WARNING: read on socket id %d, handle %d, took %d ms", ss->id, ss->sock, now - c_time); } err = 0; if (rlen < 0) err = errno; if (rlen > 0) ss->rtime = c_time; if (read_ok && rlen > 0) ss->rlen += rlen; else ss->rlen = 0; //force 0 at the end of the string if (ss->lbuf >= ss->rlen) ss->buf[ss->rlen] = 0; LOGL(6, "Read %s %d (rlen:%d/total:%d) bytes from %d -> %p - iteration %d action %p", read_ok ? "OK" : "NOK", rlen, ss->rlen, ss->lbuf, ss->sock, ss->buf, it++, ss->action); if (((ss->rlen > 0) || err == EWOULDBLOCK) && ss->action && (ss->type != TYPE_SERVER)) ss->action(ss); // if(s[i].type==TYPE_DVR && (c_time/1000 % 10 == 0))sockets_del(i); // we do this in stream.c in flush_stream* if (!read_ok && ss->type != TYPE_SERVER) { char *err_str; char *types[] = { "udp", "tcp", "server", "http", "rtsp", "dvr" }; if (rlen == 0) { err = 0; err_str = "Close"; } else if (err == EOVERFLOW) err_str = "EOVERFLOW"; else if (err == EWOULDBLOCK) err_str = "Connected"; else err_str = strerror(err); if (ss->type == TYPE_RTCP) continue; // do not close the RTCP socket, we might get some errors here but ignore them LOG( "select_and_execute[%d]: %s on socket %d (sid:%d) from %s:%d - type %s errno %d", i, err_str, ss->sock, ss->sid, get_socket_rhost(ss->id, ra, sizeof(ra)), ntohs(ss->sa.sin_port), types[ss->type], err); if (err == EOVERFLOW || err == EWOULDBLOCK) continue; if (err == EAGAIN) { ss->err++; if (ss->err < 10) continue; } sockets_del(i); LOG("Delete socket %d done: sid %d", i, ss->sid); continue; } // ss->err = 0; } // checking every 60seconds for idle connections - or if select times out if (rv == 0 || c_time - lt >= 200) { lt = c_time; i = -1; while (++i < max_sock) if (( s[i].sock > 0 ) && ((s[i].close_sec > 0 && lt - s[i].rtime > s[i].close_sec) || (s[i].close_sec == 1))) { if (s[i].timeout && s[i].timeout(&s[i])) sockets_del(i); if (!s[i].timeout) sockets_del(i); } stream_timeouts(); } } LOG("The main loop ended, run_loop = %d", run_loop); return 0; }