aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkartofen <mladenovnasko0@gmail.com>2022-08-31 12:31:38 +0300
committerkartofen <mladenovnasko0@gmail.com>2022-08-31 12:31:38 +0300
commit675ded0d66b9fd60777d3037ded1446a3f9ef986 (patch)
tree6db3f0524437304f9e5a7b90175e3f6b043d4d49
Big Bang
-rw-r--r--.gitignore2
-rwxr-xr-xbuild.sh44
-rw-r--r--src/audio.c80
-rw-r--r--src/audio.h8
-rw-r--r--src/camera.c25
-rw-r--r--src/camera.h12
-rw-r--r--src/common_v4l2.h141
-rw-r--r--src/display.c53
-rw-r--r--src/display.h6
-rw-r--r--src/listener.c37
-rw-r--r--src/socket.c153
-rw-r--r--src/socket.h8
-rw-r--r--src/talker.c33
-rw-r--r--src/typedef.h7
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