diff options
author | kartofen <mladenovnasko0@gmail.com> | 2022-08-31 12:31:38 +0300 |
---|---|---|
committer | kartofen <mladenovnasko0@gmail.com> | 2022-08-31 12:31:38 +0300 |
commit | 675ded0d66b9fd60777d3037ded1446a3f9ef986 (patch) | |
tree | 6db3f0524437304f9e5a7b90175e3f6b043d4d49 |
Big Bang
-rw-r--r-- | .gitignore | 2 | ||||
-rwxr-xr-x | build.sh | 44 | ||||
-rw-r--r-- | src/audio.c | 80 | ||||
-rw-r--r-- | src/audio.h | 8 | ||||
-rw-r--r-- | src/camera.c | 25 | ||||
-rw-r--r-- | src/camera.h | 12 | ||||
-rw-r--r-- | src/common_v4l2.h | 141 | ||||
-rw-r--r-- | src/display.c | 53 | ||||
-rw-r--r-- | src/display.h | 6 | ||||
-rw-r--r-- | src/listener.c | 37 | ||||
-rw-r--r-- | src/socket.c | 153 | ||||
-rw-r--r-- | src/socket.h | 8 | ||||
-rw-r--r-- | src/talker.c | 33 | ||||
-rw-r--r-- | src/typedef.h | 7 |
14 files changed, 609 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cbbd0b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +obj/
\ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d35c2f0 --- /dev/null +++ b/build.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +cd ${0%/*} # go to project root + +FLAGS="-Wall -Wextra -g -pedantic -lpthread -D_GNU_SOURCE" +FGLUT="-lglut -lGLU -lGL" +FV4L2="-lv4l2" +FPASM="-lpulse -lpulse-simple" +SRC="src" +OBJ="obj" +BIN="bin" +RUN=0 + +function __run__ { + RUN=1 +} + +function __clean__ { + rm -rf $BIN + rm -rf $OBJ + kill $( ps -q $$ -o pgid= ) # exit +} + +set -xe + +if ! { [[ $# -eq 0 ]]; } 2> /dev/null +then + __$1__ +fi + +mkdir -p $BIN +mkdir -p $OBJ + +gcc -o $OBJ/socket.o -c $SRC/socket.c +gcc -o $OBJ/display.o -c $SRC/display.c +gcc -o $OBJ/camera.o -c $SRC/camera.c +gcc -o $OBJ/audio.o -c $SRC/audio.c + +gcc -o $BIN/listener $SRC/listener.c $OBJ/socket.o $OBJ/display.o $OBJ/audio.o $FLAGS $FPASM $FGLUT +gcc -o $BIN/talker $SRC/talker.c $OBJ/socket.o $OBJ/camera.o $OBJ/audio.o $FLAGS $FPASM $FV4L2 + +# if ! { [[ $RUN -eq 0 ]]; } 2> /dev/null +# then +# fi diff --git a/src/audio.c b/src/audio.c new file mode 100644 index 0000000..0c40f9f --- /dev/null +++ b/src/audio.c @@ -0,0 +1,80 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <pulse/simple.h> +#include <pulse/error.h> + +#include "audio.h" +#include "typedef.h" + +static const pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 1 +}; + +static pa_simple *play = NULL; + +int audip_play(char *buf) +{ + int ret = 1; + int error; + + if(!play) { + if (!(play = pa_simple_new(NULL, __FILE__, PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error))) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto finish; + } + } + + if(pa_simple_write(play, buf, REC_CAP, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); + goto finish; + } + + if(pa_simple_drain(play, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error)); + goto finish; + } + + ret = 0; + +finish: + + if (play) + pa_simple_free(play); + + return ret; +} + +int audio_record(int fd) +{ + pa_simple *rec = NULL; + int ret = 1; + int error; + + if (!(rec = pa_simple_new(NULL, __FILE__, PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto finish; + } + + for(;;) + { + char buf[REC_CAP]; + + if (pa_simple_read(rec, buf, sizeof(buf), &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); + goto finish; + } + + write(fd, buf, sizeof(buf)); + } + +finish: + if(rec) + pa_simple_free(rec); + + return ret; +} diff --git a/src/audio.h b/src/audio.h new file mode 100644 index 0000000..f7f4ae4 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,8 @@ +#ifndef AUDIO_H +#define AUDIO_H + +int audip_play(char *buf); + +int audio_record(int fd); + +#endif diff --git a/src/camera.c b/src/camera.c new file mode 100644 index 0000000..fcd0606 --- /dev/null +++ b/src/camera.c @@ -0,0 +1,25 @@ +#include "camera.h" +#include "common_v4l2.h" + +CommonV4l2 common_v4l2; + +void camera_init(char *dev_name, unsigned int x_res, unsigned int y_res) +{ + CommonV4l2_init(&common_v4l2, dev_name, x_res, y_res); +} + +char *camera_get_image() +{ + CommonV4l2_update_image(&common_v4l2); + return CommonV4l2_get_image(&common_v4l2); +} + +void camera_deinit() +{ + CommonV4l2_deinit(&common_v4l2); +} + +int camera_get_image_size() +{ + return (int)CommonV4l2_get_image_size(&common_v4l2); +} diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 0000000..6a3db62 --- /dev/null +++ b/src/camera.h @@ -0,0 +1,12 @@ +#ifndef CAMERA_H +#define CAMERA_H + +void camera_init(char *dev_name, unsigned int x_res, unsigned int y_res); + +char *camera_get_image(); + +void camera_deinit(); + +int camera_get_image_size(); + +#endif diff --git a/src/common_v4l2.h b/src/common_v4l2.h new file mode 100644 index 0000000..c3762b5 --- /dev/null +++ b/src/common_v4l2.h @@ -0,0 +1,141 @@ +#ifndef COMMON_V4L2_H +#define COMMON_V4L2_H + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <libv4l2.h> +#include <linux/videodev2.h> + +#define COMMON_V4L2_CLEAR(x) memset(&(x), 0, sizeof(x)) + +typedef struct { + void *start; + size_t length; +} CommonV4l2_Buffer; + +typedef struct { + int fd; + CommonV4l2_Buffer *buffers; + struct v4l2_buffer buf; + unsigned int n_buffers; +} CommonV4l2; + +void CommonV4l2_xioctl(int fh, unsigned long int request, void *arg) +{ + int r; + do { + r = v4l2_ioctl(fh, request, arg); + } while (r == -1 && ((errno == EINTR) || (errno == EAGAIN))); + if (r == -1) { + fprintf(stderr, "error %d, %s\n", errno, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +void CommonV4l2_init(CommonV4l2 *this, char *dev_name, unsigned int x_res, unsigned int y_res) { + enum v4l2_buf_type type; + struct v4l2_format fmt; + struct v4l2_requestbuffers req; + unsigned int i; + + this->fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0); + if (this->fd < 0) { + perror("Cannot open device"); + exit(EXIT_FAILURE); + } + COMMON_V4L2_CLEAR(fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = x_res; + fmt.fmt.pix.height = y_res; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + CommonV4l2_xioctl(this->fd, VIDIOC_S_FMT, &fmt); + if ((fmt.fmt.pix.width != x_res) || (fmt.fmt.pix.height != y_res)) + printf("Warning: driver is sending image at %dx%d\n", + fmt.fmt.pix.width, fmt.fmt.pix.height); + COMMON_V4L2_CLEAR(req); + req.count = 2; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + CommonV4l2_xioctl(this->fd, VIDIOC_REQBUFS, &req); + this->buffers = calloc(req.count, sizeof(*this->buffers)); + for (this->n_buffers = 0; this->n_buffers < req.count; ++this->n_buffers) { + COMMON_V4L2_CLEAR(this->buf); + this->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + this->buf.memory = V4L2_MEMORY_MMAP; + this->buf.index = this->n_buffers; + CommonV4l2_xioctl(this->fd, VIDIOC_QUERYBUF, &this->buf); + this->buffers[this->n_buffers].length = this->buf.length; + this->buffers[this->n_buffers].start = v4l2_mmap(NULL, this->buf.length, + PROT_READ | PROT_WRITE, MAP_SHARED, this->fd, this->buf.m.offset); + if (MAP_FAILED == this->buffers[this->n_buffers].start) { + perror("mmap"); + exit(EXIT_FAILURE); + } + } + for (i = 0; i < this->n_buffers; ++i) { + COMMON_V4L2_CLEAR(this->buf); + this->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + this->buf.memory = V4L2_MEMORY_MMAP; + this->buf.index = i; + CommonV4l2_xioctl(this->fd, VIDIOC_QBUF, &this->buf); + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + CommonV4l2_xioctl(this->fd, VIDIOC_STREAMON, &type); +} + +void CommonV4l2_update_image(CommonV4l2 *this) { + fd_set fds; + int r; + struct timeval tv; + + do { + FD_ZERO(&fds); + FD_SET(this->fd, &fds); + + /* Timeout. */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + r = select(this->fd + 1, &fds, NULL, NULL, &tv); + } while ((r == -1 && (errno == EINTR))); + if (r == -1) { + perror("select"); + exit(EXIT_FAILURE); + } + COMMON_V4L2_CLEAR(this->buf); + this->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + this->buf.memory = V4L2_MEMORY_MMAP; + CommonV4l2_xioctl(this->fd, VIDIOC_DQBUF, &this->buf); + CommonV4l2_xioctl(this->fd, VIDIOC_QBUF, &this->buf); +} + +char * CommonV4l2_get_image(CommonV4l2 *this) { + return ((char *)this->buffers[this->buf.index].start); +} + +size_t CommonV4l2_get_image_size(CommonV4l2 *this) { + return this->buffers[this->buf.index].length; +} + +void CommonV4l2_deinit(CommonV4l2 *this) { + unsigned int i; + enum v4l2_buf_type type; + + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + CommonV4l2_xioctl(this->fd, VIDIOC_STREAMOFF, &type); + for (i = 0; i < this->n_buffers; ++i) + v4l2_munmap(this->buffers[i].start, this->buffers[i].length); + v4l2_close(this->fd); + free(this->buffers); +} + +#endif diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..33131ef --- /dev/null +++ b/src/display.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <GL/glut.h> +#include "display.h" +#include "typedef.h" + +unsigned char WIDTH = 1; +unsigned char HEIGHT = 1; + +GLubyte pixels[BUF_CAP] = {0}; +static int fd; + +void pixel(int x, int y, char *buf) +{ + int p = (y*WIDTH + x)*3; + int b = ((HEIGHT-1-y)*WIDTH+x)*3; + pixels[p+0] = buf[b+0]; + pixels[p+1] = buf[b+1]; + pixels[p+2] = buf[b+2]; +} + +void render() +{ + read(fd, &WIDTH, sizeof(WIDTH)); + read(fd, &HEIGHT, sizeof(HEIGHT)); + + char buf[BUF_CAP] = {0}; + if(read(fd, buf, sizeof(buf)) != -1) + for(int i = 0; i < HEIGHT; i++) + for(int j = 0; j < WIDTH; j++) + pixel(j, i, buf); + + glClear(GL_COLOR_BUFFER_BIT); + glDrawPixels(WIDTH, HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + glutSwapBuffers(); + glutReshapeWindow(WIDTH, HEIGHT); + glutPostRedisplay(); +} + +void display(int *argc, char **argv, int readfd) +{ + glutInit(argc, argv); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); + glutInitWindowSize(69, 69); + glutCreateWindow(argv[0]); + + fd = readfd; + + glutDisplayFunc(render); + glutMainLoop(); +} diff --git a/src/display.h b/src/display.h new file mode 100644 index 0000000..7e4f187 --- /dev/null +++ b/src/display.h @@ -0,0 +1,6 @@ +#ifndef DISPLAY_H +#define DISPLAY_H + +void display(int *argc, char **argv, int readfd); + +#endif diff --git a/src/listener.c b/src/listener.c new file mode 100644 index 0000000..0a5df2d --- /dev/null +++ b/src/listener.c @@ -0,0 +1,37 @@ +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> + +#include "socket.h" +#include "display.h" +#include "typedef.h" + +static int fd; + +void on_recv(char *buf, int numbytes) +{ + // read and play audio + write(fd, buf, numbytes); +} + +int main(int argc, char **argv) +{ + int pipefd[2]; + if(pipe2(pipefd, O_NONBLOCK) == -1) { + fputs("pipe: failed", stderr); + return 1; + } + + pid_t p = fork(); + if(p < 0) { + fputs("fork: failed", stderr); + return 1; + } else if(p > 0) { + close(pipefd[0]); + fd = pipefd[1]; + return listener("4950", &on_recv); + } else { + close(pipefd[1]); + display(&argc, argv, pipefd[0]); + } +} diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..e388e45 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,153 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <arpa/inet.h> +#include <netdb.h> +#include "socket.h" +#include "typedef.h" + +void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +int listener(char *port, void (*on_recv)(char *, int)) +{ + int sockfd; + int ret; + + struct addrinfo hints, *servinfo; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET6; // set to AF_INET to use IPv4 + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; // use my IP + + int rv; + if((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return 1; + } + + struct addrinfo *p; + for(p = servinfo; p != NULL; p = p->ai_next) + { + if((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("listener: socket"); + continue; + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("listener: bind"); + continue; + } + + break; + } + + if(p == NULL) { + fprintf(stderr, "listener: failed to bind socket\n"); + return 2; + } + + freeaddrinfo(servinfo); + + #include <time.h> + int i = 0; + time_t now = 0; + while(1) + { + if(now < time(NULL)) { + now = time(NULL); + printf("FPS: %d\n", i); + i = 0; + } + + char buf[BUF_CAP] = {0}; + struct sockaddr_storage their_addr; + socklen_t addr_len = sizeof(their_addr); + + int numbytes; + if ((numbytes = recvfrom(sockfd, buf, BUF_CAP-1 , 0, + (struct sockaddr *)&their_addr, &addr_len)) == -1) { + perror("recvfrom"); + return 1; + } + + #ifdef LOG_INFO_STDOUT + char s[INET6_ADDRSTRLEN]; + printf("listener: got packet from %s, %d bytes long\n", + inet_ntop(their_addr.ss_family, + get_in_addr((struct sockaddr *)&their_addr), + s, sizeof(s)), numbytes); + #endif + + on_recv(buf, numbytes); + + i++; + } + + return 0; +} + +int talker(char *port, char *address, void (*on_send)(char *, int *)) +{ + int sockfd; + + struct addrinfo hints, *servinfo; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET6; // set to AF_INET to use IPv4 + hints.ai_socktype = SOCK_DGRAM; + + int rv; + if((rv = getaddrinfo(address, port, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return 1; + } + + struct addrinfo *p; + for(p = servinfo; p != NULL; p = p->ai_next) + { + if((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("talker: socket"); + continue; + } + + break; + } + + if(p == NULL) { + fprintf(stderr, "talker: failed to create socket\n"); + return 2; + } + + while(1) + { + char buf[BUF_CAP] = {0}; + int bytes; + on_send(buf, &bytes); + + int numbytes; + if((numbytes = sendto(sockfd, buf, bytes, 0, + p->ai_addr, p->ai_addrlen)) == -1) { + perror("talker: sendto"); + return 1; + } + #ifdef LOG_INFO_STDOUT + printf("talker: sent %d bytes to %s\n", numbytes, address); + #endif + } + + freeaddrinfo(servinfo); + close(sockfd); + + return 0; +} diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..ecb27ab --- /dev/null +++ b/src/socket.h @@ -0,0 +1,8 @@ +#ifndef SOCKET_H +#define SOCKET_H + +int listener(char *port, void (*on_recv)(char *, int)); + +int talker(char *port, char *address, void (*on_send)(char *, int *)); + +#endif diff --git a/src/talker.c b/src/talker.c new file mode 100644 index 0000000..8b09274 --- /dev/null +++ b/src/talker.c @@ -0,0 +1,33 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include "socket.h" +#include "camera.h" +#include "typedef.h" + +#define CAM "/dev/video0" +#define X_RES 160 +#define Y_RES 120 + +void on_send(char *buf, int *bytes) +{ + // write here audio + + int cisz = camera_get_image_size(); + buf[0] = (unsigned char)X_RES; + buf[1] = (unsigned char)Y_RES; + + *bytes = cisz + 2; + + memcpy(&(buf[2]), camera_get_image(), cisz); + + printf("%d\n", *bytes); +} + +int main(void) +{ + camera_init(CAM, X_RES, Y_RES); + return talker("4950", "localhost", &on_send); +} diff --git a/src/typedef.h b/src/typedef.h new file mode 100644 index 0000000..a8871fd --- /dev/null +++ b/src/typedef.h @@ -0,0 +1,7 @@ +#ifndef TYPEDEF_H +#define TYPEDEF_H + +#define BUF_CAP 65536 +#define REC_CAP 2940 // second of audio + +#endif |