/****************************************************************************** Description.: Send a complete HTTP response and a stream of JPG-frames. Input Value.: fildescriptor fd to send the answer to Return Value: - ******************************************************************************/ void send_stream(int fd, int input_number) { unsigned char *frame = NULL, *tmp = NULL; int frame_size = 0, max_frame_size = 0; char buffer[BUFFER_SIZE] = {0}; struct timeval timestamp; DBG("preparing header\n"); sprintf(buffer, "HTTP/1.0 200 OK\r\n" \ STD_HEADER \ "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" \ "\r\n" \ "--" BOUNDARY "\r\n"); if(write(fd, buffer, strlen(buffer)) < 0) { free(frame); return; } DBG("Headers send, sending stream now\n"); while(!pglobal->stop) { /* wait for fresh frames */ pthread_mutex_lock(&pglobal->in[input_number].db); pthread_cond_wait(&pglobal->in[input_number].db_update, &pglobal->in[input_number].db); /* read buffer */ frame_size = pglobal->in[input_number].size; /* check if framebuffer is large enough, increase it if necessary */ if(frame_size > max_frame_size) { DBG("increasing buffer size to %d\n", frame_size); max_frame_size = frame_size + TEN_K; if((tmp = realloc(frame, max_frame_size)) == NULL) { free(frame); pthread_mutex_unlock(&pglobal->in[input_number].db); send_error(fd, 500, "not enough memory"); return; } frame = tmp; } /* copy v4l2_buffer timeval to user space */ timestamp = pglobal->in[input_number].timestamp; memcpy(frame, pglobal->in[input_number].buf, frame_size); DBG("got frame (size: %d kB)\n", frame_size / 1024); pthread_mutex_unlock(&pglobal->in[input_number].db); /* * print the individual mimetype and the length * sending the content-length fixes random stream disruption observed * with firefox */ sprintf(buffer, "Content-Type: image/jpeg\r\n" \ "Content-Length: %d\r\n" \ "X-Timestamp: %d.%06d\r\n" \ "\r\n", frame_size, (int)timestamp.tv_sec, (int)timestamp.tv_usec); DBG("sending intemdiate header\n"); if(write(fd, buffer, strlen(buffer)) < 0) break; DBG("sending frame\n"); if(write(fd, frame, frame_size) < 0) break; DBG("sending boundary\n"); sprintf(buffer, "\r\n--" BOUNDARY "\r\n"); if(write(fd, buffer, strlen(buffer)) < 0) break; } free(frame); } /****************************************************************************** Description.: Open a TCP socket and wait for clients to connect. If clients connect, start a new thread for each accepted connection. Input Value.: arg is a pointer to the globals struct Return Value: always NULL, will only return on exit ******************************************************************************/ void *server_thread(void *arg) { int on; pthread_t client; struct addrinfo *aip, *aip2; struct addrinfo hints; struct sockaddr_storage client_addr; socklen_t addr_len = sizeof(struct sockaddr_storage); fd_set selectfds; int max_fds = 0; char name[NI_MAXHOST]; int err; int i; context *pcontext = arg; pglobal = pcontext->pglobal; /* set cleanup handler to cleanup ressources */ pthread_cleanup_push(server_cleanup, pcontext); bzero(&hints, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; snprintf(name, sizeof(name), "%d", ntohs(pcontext->conf.port)); if((err = getaddrinfo(NULL, name, &hints, &aip)) != 0) { perror(gai_strerror(err)); exit(EXIT_FAILURE); } for(i = 0; i < MAX_SD_LEN; i++) pcontext->sd[i] = -1; /* open sockets for server (1 socket / address family) */ i = 0; for(aip2 = aip; aip2 != NULL; aip2 = aip2->ai_next) { if((pcontext->sd[i] = socket(aip2->ai_family, aip2->ai_socktype, 0)) < 0) { continue; } /* ignore "socket already in use" errors */ on = 1; if(setsockopt(pcontext->sd[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { perror("setsockopt(SO_REUSEADDR) failed"); } #ifdef IPV6_V6ONLY // guard to be able to compile on systems lack of IPV6 support /* IPv6 socket should listen to IPv6 only, otherwise we will get "socket already in use" */ on = 1; if(aip2->ai_family == AF_INET6 && setsockopt(pcontext->sd[i], IPPROTO_IPV6, IPV6_V6ONLY, (const void *)&on , sizeof(on)) < 0) { perror("setsockopt(IPV6_V6ONLY) failed"); } #endif /* perhaps we will use this keep-alive feature oneday */ /* setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); */ if(bind(pcontext->sd[i], aip2->ai_addr, aip2->ai_addrlen) < 0) { perror("bind"); pcontext->sd[i] = -1; continue; } if(listen(pcontext->sd[i], 10) < 0) { perror("listen"); pcontext->sd[i] = -1; } else { i++; if(i >= MAX_SD_LEN) { OPRINT("%s(): maximum number of server sockets exceeded", __FUNCTION__); i--; break; } } } pcontext->sd_len = i; if(pcontext->sd_len < 1) { OPRINT("%s(): bind(%d) failed", __FUNCTION__, htons(pcontext->conf.port)); closelog(); exit(EXIT_FAILURE); } /* create a child for every client that connects */ while(!pglobal->stop) { //int *pfd = (int *)malloc(sizeof(int)); cfd *pcfd = malloc(sizeof(cfd)); if(pcfd == NULL) { fprintf(stderr, "failed to allocate (a very small amount of) memory\n"); exit(EXIT_FAILURE); } DBG("waiting for clients to connect\n"); do { FD_ZERO(&selectfds); for(i = 0; i < MAX_SD_LEN; i++) { if(pcontext->sd[i] != -1) { FD_SET(pcontext->sd[i], &selectfds); if(pcontext->sd[i] > max_fds) max_fds = pcontext->sd[i]; } } err = select(max_fds + 1, &selectfds, NULL, NULL, NULL); if(err < 0 && errno != EINTR) { perror("select"); exit(EXIT_FAILURE); } } while(err <= 0); for(i = 0; i < max_fds + 1; i++) { if(pcontext->sd[i] != -1 && FD_ISSET(pcontext->sd[i], &selectfds)) { pcfd->fd = accept(pcontext->sd[i], (struct sockaddr *)&client_addr, &addr_len); pcfd->pc = pcontext; /* start new thread that will handle this TCP connected client */ DBG("create thread to handle client that just established a connection\n"); #if 0 /* commented out as it fills up syslog with many redundant entries */ if(getnameinfo((struct sockaddr *)&client_addr, addr_len, name, sizeof(name), NULL, 0, NI_NUMERICHOST) == 0) { syslog(LOG_INFO, "serving client: %s\n", name); } #endif if(pthread_create(&client, NULL, &client_thread, pcfd) != 0) { DBG("could not launch another client thread\n"); close(pcfd->fd); free(pcfd); continue; } pthread_detach(client); } } } DBG("leaving server thread, calling cleanup function now\n"); pthread_cleanup_pop(1); return NULL; }
mjpeg源码
猜你喜欢
转载自blog.csdn.net/u011426247/article/details/80552697
今日推荐
周排行