Exemple #1
0
static void add_client(struct server *s, int sock, struct sockaddr_storage *addr, struct frame_buffers *fbs) {
    struct client *c;
    char cbuf[INET6_ADDRSTRLEN]; // general purpose buffer for various string conversions in this function

    c = malloc(sizeof(struct client));
    memset(c, 0, sizeof(struct client));

    c->sock = sock;
    memcpy(&c->addr, addr, sizeof(struct sockaddr_storage));
    c->last_communication = gettime();
    c->current_frame = 0;
    c->current_frame_pos = 0;
    c->request_header_size = 0;
    c->request = REQUEST_INCOMPLETE;
    c->resp = NULL;
    c->resp_pos = 0;
    c->resp_len = 0;
    c->fb = NULL;
    c->static_file = NULL;

    if (s->ssl_ctx != NULL) {
        c->ssl = SSL_new(s->ssl_ctx);
        SSL_set_fd(c->ssl, c->sock);

        if (SSL_accept(c->ssl) < 1) {
            log_itf(LOG_ERROR, "Error occured doing the SSL handshake: %s", ERR_error_string(ERR_get_error(), NULL));
            free(c);
            return;
        }
    }

    s->clients[c->sock] = c;

    log_itf(LOG_INFO, "Client connected from %s.", ntop(&c->addr, cbuf, sizeof(cbuf)));
}
Exemple #2
0
size_t capture_frame(struct video_device *vd) {
    memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
    vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd->buf.memory = V4L2_MEMORY_MMAP;

    if (xioctl(vd->fd, VIDIOC_DQBUF, &vd->buf) < 0) {
        log_itf(LOG_ERROR, "Unable to dequeue buffer on device %s.", vd->device_filename);
        return -1;
    }

    switch(vd->format_in) {
        case V4L2_PIX_FMT_MJPEG:
            if (vd->buf.bytesused <= MIN_BYTES_USED) {
                // Prevent crash on empty image
                log_itf(LOG_WARNING, "Ignoring empty buffer on device %s.", vd->device_filename);
                return 0;
            }

            memcpy(vd->framebuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
            break;

        case V4L2_PIX_FMT_YUYV:
            if (vd->buf.bytesused > vd->framebuffer_size)
                memcpy(vd->framebuffer, vd->mem[vd->buf.index], vd->framebuffer_size);
            else
                memcpy(vd->framebuffer, vd->mem[vd->buf.index], vd->buf.bytesused);
            break;

        default:
            return -1;
            break;
    }

    return vd->buf.bytesused;
}
Exemple #3
0
static int open_sock(const char *hostname, short port, int family, int socktype) {
    struct addrinfo hints, *res, *ressave;
    int n, sock;
    int sockoptval = 1;
    char cbuf[INET6_ADDRSTRLEN]; // general purpose buffer for various string conversions in this function

    memset(&hints, 0, sizeof(struct addrinfo));

    hints.ai_flags    = AI_PASSIVE;
    hints.ai_family   = family;
    hints.ai_socktype = socktype;

    snprintf(cbuf, sizeof(cbuf), "%d", port);
    n = getaddrinfo(strlen(hostname) ? hostname : NULL, cbuf, &hints, &res);

    if (n < 0) {
        log_itf(LOG_DEBUG, "getaddrinfo error for hostname '%s': %s.", hostname, gai_strerror(n));
        return -1;
    }

    ressave = res;

    // Try open socket with each address getaddrinfo returned,
    // until getting a valid listening socket.
    
    sock = -1;
    while (res) {
        sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int));

        if (sock >= 0) {
            if (bind(sock, res->ai_addr, res->ai_addrlen) == 0) {
                inet_ntop(res->ai_family, (void *) &((struct sockaddr_in6*) res->ai_addr)->sin6_addr, cbuf, sizeof(cbuf));
                log_itf(LOG_INFO, "Listening on %s port %d.", cbuf, port);

                break;
            }

            close(sock);
            sock = -1;
        }
        res = res->ai_next;
    }

    if (sock >= 0) {
        if (listen(sock, MAX_SERVER_SOCKET_BACKLOG) < 0) {
            panic("listen() failed.");
        }
    }

    freeaddrinfo(ressave);

    return sock;
}
Exemple #4
0
static int video_enable(struct video_device *vd) {
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    int ret;

    log_itf(LOG_INFO, "Starting capture on device %s.", vd->device_filename);
    ret = xioctl(vd->fd, VIDIOC_STREAMON, &type);
    if (ret < 0) {
        log_itf(LOG_ERROR, "Unable to start capture on device %s", vd->device_filename);
        return ret;
    }
    vd->streaming_state = STREAMING_ON;
    return 0;
}
Exemple #5
0
static int video_disable(struct video_device *vd, streaming_state disabledState) {
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    int ret;

    log_itf(LOG_INFO, "Stopping capture on device %s.", vd->device_filename);
    ret = xioctl(vd->fd, VIDIOC_STREAMOFF, &type);
    if (ret != 0) {
        log_itf(LOG_ERROR, "Unable to stop capture on device %s.", vd->device_filename);
        return ret;
    }
    log_itf(LOG_INFO, "Stopping capture done on device %s.", vd->device_filename);
    vd->streaming_state = disabledState;
    return 0;
}
Exemple #6
0
void destroy_video_device(struct video_device *vd) {
    if (vd->streaming_state == STREAMING_ON) {
        video_disable(vd, STREAMING_OFF);
    }

    if (CLOSE_VIDEO(vd->fd) != 0) {
        log_itf(LOG_ERROR, "Failed to close device %s.", vd->device_filename);
    }

    free(vd->framebuffer);
    vd->framebuffer = NULL;

    free(vd->device_filename);
    vd->device_filename = NULL;

    free(vd->formats);
    vd->formats = NULL;
    vd->format_count = 0;

    free(vd->resolutions);
    vd->resolutions = NULL;
    vd->resolution_count = 0;

    free(vd);
}
Exemple #7
0
int requeue_device_buffer(struct video_device *vd) {
    if (xioctl(vd->fd, VIDIOC_QBUF, &vd->buf) < 0) {
        log_itf(LOG_ERROR, "Unable to requeue buffer on device %s.", vd->device_filename);
        return -1;
    }

    return 0;
}
Exemple #8
0
void* __wrap_realloc(void *ptr, size_t size) {
	void *tmp = __real_realloc(ptr, size);
	char error[512];
	if (tmp == NULL) {
		strerror_r(errno, (char *) &error, sizeof(error));
		log_itf(LOG_ERROR, "realloc() failed: (%d) %s", errno, error);
		exit(EXIT_FAILURE);
	}
	return tmp;
}
Exemple #9
0
char* __wrap_strdup(const char *s) {
	char *ptr = __real_strdup(s);
	char error[512];
	if (ptr == NULL) {
		strerror_r(errno, (char *) &error, sizeof(error));
		log_itf(LOG_ERROR, "strdup() failed: (%d) %s", errno, error);
		exit(EXIT_FAILURE);
	}

	return ptr;
}
Exemple #10
0
static int xioctl(int fd, int IOCTL_X, void *arg) {
    int ret = 0;
    int tries = IOCTL_RETRY;

    do {
        ret = IOCTL_VIDEO(fd, IOCTL_X, arg);
    } while(ret && tries-- &&
            ((errno == EINTR) || (errno == EAGAIN) || (errno == ETIMEDOUT)));

    if (ret && tries <= 0) {
        log_itf(LOG_ERROR, "ioctl (%x) retried %i times - giving up: %s.", IOCTL_X, IOCTL_RETRY, strerror(errno));
        user_panic("ioctl error.");
    }

    return (ret);
}
Exemple #11
0
struct server *create_server(char *host, unsigned short port, struct frame_buffers *fbs, char *static_root, char *auth, char *ssl_cert_file, char *ssl_key_file) {
    struct server *s = malloc(sizeof(struct server));
    size_t stream_info_buf_size;
    
    memset(s->clients, 0, sizeof(s->clients));

    s->sock6 = open_sock(host, port, AF_INET6, SOCK_STREAM);
    s->sock4 = open_sock(host, port, AF_INET, SOCK_STREAM);
    
    if (s->sock4 < 0 && s->sock6 < 0) {
        panic("Could not bind to socket.");
    }

    stream_info_buf_size = sizeof(HTTP_STREAM_INFO_TEMPLATE) + 64; // Should be enough room for the data
    s->stream_info = malloc(stream_info_buf_size); 
    snprintf(s->stream_info,
            stream_info_buf_size,
            HTTP_STREAM_INFO_TEMPLATE,
            (int) fbs->count,
            fbs->buffers[0].vd->width,
            fbs->buffers[0].vd->height
    );

    s->auth = NULL;
    if (strlen(auth)) {
        s->auth = base64_encode((unsigned char *) auth);
    }

    s->static_root = strdup(static_root);

    s->ssl_ctx = NULL;

    if (strlen(ssl_cert_file) && strlen(ssl_key_file)) {
        s->ssl_ctx = ssl_create_ctx();
        ssl_load_certs(s->ssl_ctx, ssl_cert_file, ssl_key_file);
    }
    else if (strlen(auth)) {
        log_itf(LOG_WARNING, "You are password protecting your streams, but not using encryption.\nAnyone can see your password!");
    }

    return s;
}
Exemple #12
0
static void remove_client(struct server *s, struct client *c) {
    char cbuf[INET6_ADDRSTRLEN];
    log_itf(LOG_INFO, "Disconneting client from %s.", ntop(&c->addr, cbuf, sizeof(cbuf)));
    
    s->clients[c->sock] = NULL;
    close(c->sock);
    
    if (c->static_file != NULL) {
        fclose(c->static_file);
    }

    if (c->resp != NULL) {
        free(c->resp);
    }
    
    if (c->ssl) {
        SSL_free(c->ssl);
    }

    free(c);
}
Exemple #13
0
static void handle_request(struct server *s, struct client *c, struct frame_buffers *fbs) {
    int index;
    char cbuf[INET6_ADDRSTRLEN]; // general purpose buffer for various string conversions in this function
    char tmp_filename[PATH_MAX + 1], filename[PATH_MAX + 1]; // Need 2 of these for realpath()
    char resp_head[sizeof(HTTP_STATIC_FILE_HEADERS_TMPL) + 256];
    struct http_request req;

    parse_request(c->request_headers, &req);
    if (req.protocol_version == NULL) {
        set_client_response(c, REQUEST_BAD, HTTP_BAD_REQUEST);
        return;
    }

    log_itf(LOG_INFO, "Client at %s requested %s %s%s%s.", 
        ntop(&c->addr, cbuf, sizeof(cbuf)),
        req.method,
        req.path,
        *req.query_string != '\0' ? "?" : "",
        req.query_string
    );
    
    if (strcmp(req.method, "GET") != 0 || strcmp(req.protocol, "HTTP") != 0) {
        return set_client_response(c, REQUEST_BAD, HTTP_BAD_REQUEST);
    }

    if (strncmp(req.path, "/img/", 5) != 0) {
        if (!check_http_auth(req.authorization, s->auth)) {
            return set_client_response(c, REQUEST_AUTH_REQUIRED, HTTP_AUTH_REQUIRED);
        }
    }

    // /stream/
    if (strncmp(req.path, "/stream/", strlen("/stream/")) == 0) {
        if (strcmp(req.path, "/stream/info") == 0) {
            set_client_response(c, REQUEST_STREAM_INFO, s->stream_info);
        }
        else {
            // /stream/0, /stream/1, etc
            index = atoi(&req.path[strlen("/stream/")]);

            if (index < 0 || index >= fbs->count) {
                set_client_response(c, REQUEST_NOT_FOUND, HTTP_NOT_FOUND);
            }
            else {
                set_client_response(c, REQUEST_STREAM, STREAM_HEADER);
                
                c->fb = &fbs->buffers[index];
                c->current_frame = c->fb->current_frame;
                c->current_frame_pos = 0;
            }
        }
    }
    else {

        if (!strlen(s->static_root)) {
            return set_client_response(c, REQUEST_NOT_FOUND, HTTP_NOT_FOUND);
        }
                
        // Serving static file
        strncpy(tmp_filename, s->static_root, PATH_MAX);
        strncat(tmp_filename, req.path, PATH_MAX);
        if (tmp_filename[strlen(tmp_filename) - 1] == '/') {
            strncat(tmp_filename, INDEX_FILE_NAME, PATH_MAX);
        }

        // This also checks if the file exists
        if (NULL == realpath(tmp_filename, filename) ||
            strncmp(filename, s->static_root, strlen(s->static_root)) != 0) {
                return set_client_response(c, REQUEST_NOT_FOUND, HTTP_NOT_FOUND);

        }

        // Fill in response template
        snprintf(resp_head, sizeof(resp_head), HTTP_STATIC_FILE_HEADERS_TMPL,
            (long) file_size(filename),
            get_mime_type(filename)
        );

        set_client_response(c, REQUEST_STATIC_FILE, resp_head);
        c->static_file = fopen(filename, "rb");
    }
    
}
Exemple #14
0
int init_v4l2(struct video_device *vd) {
    int i;
    struct v4l2_streamparm setfps;

    if ((vd->fd = OPEN_VIDEO(vd->device_filename, O_RDWR)) == -1) {
        log_itf(LOG_ERROR, "Error opening V4L2 interface on %s. errno %d", vd->device_filename, errno);
    }

    memset(&vd->cap, 0, sizeof(struct v4l2_capability));

    if (xioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap) < 0) {
        log_itf(LOG_ERROR, "Error opening device %s: unable to query device.", vd->device_filename);
        return -1;
    }

    if ((vd->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
        log_itf(LOG_ERROR, "Error opening device %s: video capture not supported.", vd->device_filename);
        return -1;
    }

    if (vd->use_streaming) {
        if (!(vd->cap.capabilities & V4L2_CAP_STREAMING)) {
            log_itf(LOG_ERROR, "%s does not support streaming I/O", vd->device_filename);
            return -1;
        }
    }
    else {
        if (!(vd->cap.capabilities & V4L2_CAP_READWRITE)) {
            log_itf(LOG_ERROR, "%s does not support read I/O", vd->device_filename);
            return -1;
        }
    }

    vd->streaming_state = STREAMING_OFF;

    // set format in
    memset(&vd->fmt, 0, sizeof(struct v4l2_format));
    vd->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd->fmt.fmt.pix.width = vd->width;
    vd->fmt.fmt.pix.height = vd->height;
    vd->fmt.fmt.pix.pixelformat = vd->format_in;
    vd->fmt.fmt.pix.field = V4L2_FIELD_ANY;

    if (xioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt) < 0) {
        log_itf(LOG_WARNING, "Unable to set format %d, res %dx%d, device %s. Trying fallback.", vd->format_in, vd->width, vd->height, vd->device_filename);

        // Try the fallback format
        vd->format_in = UVC_FALLBACK_FORMAT;
        vd->fmt.fmt.pix.pixelformat = vd->format_in;

        if (xioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt) < 0) {
            log_itf(LOG_ERROR, "Unable to set fallback format %d, res %dx%d, device %s.", vd->format_in, vd->width, vd->height, vd->device_filename);
            return -1;
        }
    }

    if ((vd->fmt.fmt.pix.width != vd->width) ||
            (vd->fmt.fmt.pix.height != vd->height)) {
        log_itf(LOG_WARNING, "The format asked unavailable, so the width %d height %d on device %s.", vd->fmt.fmt.pix.width, vd->fmt.fmt.pix.height, vd->device_filename);

        vd->width = vd->fmt.fmt.pix.width;
        vd->height = vd->fmt.fmt.pix.height;

        // look the format is not part of the deal ???
        if (vd->format_in != vd->fmt.fmt.pix.pixelformat) {
            if (vd->format_in == V4L2_PIX_FMT_MJPEG) {
                log_itf(LOG_ERROR, "The input device %s does not supports MJPEG mode.\nYou may also try the YUV mode, but it requires a much more CPU power.", vd->device_filename);
                return -1;
            } else if (vd->format_in == V4L2_PIX_FMT_YUYV) {
                log_itf(LOG_ERROR, "The input device %s does not supports YUV mode.", vd->device_filename);
                return -1;
            }
        } else {
            vd->format_in = vd->fmt.fmt.pix.pixelformat;
        }
    }

    // set framerate
    memset(&setfps, 0, sizeof(struct v4l2_streamparm));
    setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    setfps.parm.capture.timeperframe.numerator = 1;
    setfps.parm.capture.timeperframe.denominator = vd->fps;

    if (xioctl(vd->fd, VIDIOC_S_PARM, &setfps) < 0) {
        log_itf(LOG_ERROR, "Unable to set FPS to %d on device %s.", vd->fps, vd->device_filename);
        return -1;
    }


    // request buffers
    memset(&vd->rb, 0, sizeof(struct v4l2_requestbuffers));
    vd->rb.count = NB_BUFFER;
    vd->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd->rb.memory = V4L2_MEMORY_MMAP;

    if (xioctl(vd->fd, VIDIOC_REQBUFS, &vd->rb) < 0) {
        log_itf(LOG_ERROR, "Unable to allocate buffers for device %s.", vd->device_filename);
        return -1;
    }

    // map the buffers
    for(i = 0; i < NB_BUFFER; i++) {
        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
        vd->buf.index = i;
        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vd->buf.memory = V4L2_MEMORY_MMAP;
        if (xioctl(vd->fd, VIDIOC_QUERYBUF, &vd->buf)) {
            log_itf(LOG_ERROR, "Unable to query buffer on device %s.", vd->device_filename);
            return -1;
        }

        vd->mem[i] = mmap(0 /* start anywhere */ ,
                          vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
                          vd->buf.m.offset);
        if (vd->mem[i] == MAP_FAILED) {
            log_itf(LOG_ERROR, "Unable to map buffer on device %s.", vd->device_filename);
            return -1;
        }

        DBG("Buffer mapped at address %p for device %s.", vd->mem[i], vd->device_filename);
    }

    // Queue the buffers.
    for(i = 0; i < NB_BUFFER; ++i) {
        memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
        vd->buf.index = i;
        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vd->buf.memory = V4L2_MEMORY_MMAP;

        if (xioctl(vd->fd, VIDIOC_QBUF, &vd->buf) < 0) {
            log_itf(LOG_ERROR, "Unable to query buffer on device %s.", vd->device_filename);
            return -1;
        }
    }

    if (video_enable(vd)) {
        log_itf(LOG_ERROR, "Unable to enable video for device %s.", vd->device_filename);
        return -1;
    }

    return 0;
}