aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rwxr-xr-xbuild.sh7
-rw-r--r--src/audio.c143
-rw-r--r--src/audio.h18
-rw-r--r--src/listener.c10
-rw-r--r--src/talker.c11
-rw-r--r--src/typedef.h4
7 files changed, 124 insertions, 78 deletions
diff --git a/README.md b/README.md
index 48241f2..b4803cc 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ through UDP datagrams.
This project is written only in C, using the following technologies:
1. V4L2 - video for linux api for getting images from the camera
-2. PulseAudio Simple API for playing and recording audio (will be changed to ALSA soon)
+2. ALSA for recording and playing audio
3. GLFW with OpenGL for displaying the recieved image
No other external libraries or APIs are used.
@@ -16,13 +16,14 @@ No other external libraries or APIs are used.
### Limitations
1. 160x120 video resolution because of the limited size of sockets
-2. Crappy audio, most likely because of pulse audio
+2. Kinda crappy audio
3. Works only on GNU/Linux, I don't plan porting it to Windows
### Todo
-1. Use ALSA and not PulseAudio Simple API
-2. MUST run at 30 fps
+1. Higher resolution by splitting it into multiple datagrams
+2. Encryption
+3. More user-friendly
### Build
diff --git a/build.sh b/build.sh
index 423bf57..096fa61 100755
--- a/build.sh
+++ b/build.sh
@@ -5,7 +5,8 @@ cd ${0%/*} # go to project root
FLAGS="-g -Wall -Wextra -pedantic -lpthread -std=c99"
FGLFW="-lGL -lglfw"
FV4L2="-lv4l2"
-FPASM="-lpulse -lpulse-simple"
+# FPASM="-lpulse -lpulse-simple"
+FALSA="-lasound"
SRC="src"
OBJ="obj"
BIN="bin"
@@ -31,5 +32,5 @@ 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 $FGLFW
-gcc -o $BIN/talker $SRC/talker.c $OBJ/socket.o $OBJ/camera.o $OBJ/audio.o $FLAGS $FPASM $FV4L2
+gcc -o $BIN/listener $SRC/listener.c $OBJ/socket.o $OBJ/display.o $OBJ/audio.o $FLAGS $FALSA $FGLFW
+gcc -o $BIN/talker $SRC/talker.c $OBJ/socket.o $OBJ/camera.o $OBJ/audio.o $FLAGS $FALSA $FV4L2
diff --git a/src/audio.c b/src/audio.c
index 710c10c..d9bf3da 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -1,90 +1,105 @@
-// TODO: Rewrite with alsa, fuck pulseaudio
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-
-#include <pulse/simple.h>
-#include <pulse/error.h>
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#include <alsa/asoundlib.h>
#include "audio.h"
#include "typedef.h"
-static const pa_sample_spec ss = {
- .format = PA_SAMPLE_S16BE,
- .rate = 44100,
- .channels = 2
-};
+#define TEMP_BUF_SZ (handle->frames * CHANNELS * 2)
+#define HANDLE (*(snd_pcm_t**)(handle->h))
-static pa_simple *play = NULL;
-static pa_simple *rec = NULL;
+#define MIN(a,b) (((a)<(b))?(a):(b))
-int audio_play(char *buf)
+int audio_create(audio_handle *handle, audio_handle_types t)
{
- int ret = 1;
- int error;
-
- if(play == NULL) {
- 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;
- }
+ handle->h = malloc(sizeof(snd_pcm_t *));
+ snd_pcm_stream_t type = (t == RECORD) ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
+ int rc = snd_pcm_open(handle->h, "default", type, 0);
+ if (rc < 0) {
+ err("unable to open pcm device: %s\n", snd_strerror(rc));
+ return 1;
}
- if(pa_simple_flush(play, &error) < 0)
- {
- fprintf(stderr, __FILE__": pa_simple_flush() failed: %s\n", pa_strerror(error));
- goto finish;
- }
+ int dir;
+ snd_pcm_hw_params_t *params;
- if(pa_simple_write(play, buf, REC_CAP, &error) < 0) {
- fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
- goto finish;
- }
+ snd_pcm_hw_params_alloca(&params); // allocate params
+ snd_pcm_hw_params_any(HANDLE, params); // set params
+ snd_pcm_hw_params_set_access(HANDLE, params, SND_PCM_ACCESS_RW_INTERLEAVED); // interleaved mode
+ snd_pcm_hw_params_set_format(HANDLE, params, SND_PCM_FORMAT_S16_LE);
- if(pa_simple_drain(play, &error) < 0) {
- fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
- goto finish;
- }
+ snd_pcm_hw_params_set_channels(HANDLE, params, CHANNELS);
- ret = 0;
+ unsigned int samp_rate = SAMPLING_RATE;
+ snd_pcm_hw_params_set_rate_near(HANDLE, params, &samp_rate, &dir);
-finish:
+ snd_pcm_uframes_t frames = FRAMES;
+ snd_pcm_hw_params_set_period_size_near(HANDLE, params, &frames, &dir);
- // if (play)
- // pa_simple_free(play);
+ rc = snd_pcm_hw_params(HANDLE, params); // write params to handle
+ if (rc < 0) {
+ err("unable to set hw parameters: %s\n", snd_strerror(rc));
+ return 1;
+ }
- return ret;
+ snd_pcm_hw_params_get_period_size(params, &frames, &dir);
+ handle->frames = frames;
+
+ return 0;
}
-int audio_record(char *buf)
+int audio_play(audio_handle *handle, char *buf)
{
- int ret = 1;
- int error;
-
- if(!rec) {
- 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;
+ char t[TEMP_BUF_SZ];
+ for(int i = 0; ;)
+ {
+ if(i >= REC_CAP) break;;
+
+ memset(t, 0, TEMP_BUF_SZ);
+ memcpy(t, &(buf[i]), MIN(TEMP_BUF_SZ, REC_CAP - i));
+
+ int rc = snd_pcm_writei(HANDLE, t, FRAMES);
+ if (rc == -EPIPE) {
+ err("underrun occurred\n");
+ snd_pcm_prepare(HANDLE);
+ } else if (rc < 0) {
+ err("error from writei: %s\n", snd_strerror(rc));
+ } else if (rc != FRAMES) {
+ err("short write, write %d frames\n", rc);
}
- }
- if(pa_simple_flush(rec, &error) < 0) {
- fprintf(stderr, __FILE__": pa_simple_flush() failed: %s\n", pa_strerror(error));
- goto finish;
+ i += TEMP_BUF_SZ;
}
+ return 0;
+}
- if (pa_simple_read(rec, buf, REC_CAP, &error) < 0) {
- fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
- goto finish;
- }
+int audio_record(audio_handle *handle, char *buf)
+{
+ char t[TEMP_BUF_SZ];
+ for(int i = 0; ;)
+ {
+ if(i >= REC_CAP) break;
+ memset(t, 0, TEMP_BUF_SZ);
+
+ int rc = snd_pcm_readi(HANDLE, t, FRAMES);
+ if (rc == -EPIPE) {
+ err("overrun occurred\n");
+ snd_pcm_prepare(HANDLE);
+ } else if (rc < 0) {
+ err("error from read: %s\n", snd_strerror(rc));
+ } else if (rc != FRAMES) {
+ err("short read, read %d frames\n", rc);
+ }
- ret = 0;
+ memcpy(&(buf[i]), t, MIN(TEMP_BUF_SZ, REC_CAP - i));
+ i += TEMP_BUF_SZ;
+ }
-finish:
- // if(rec)
- // pa_simple_free(rec);
+ return 0;
+}
- return ret;
+void audio_destroy(audio_handle *handle)
+{
+ snd_pcm_drain(HANDLE);
+ snd_pcm_close(HANDLE);
+ free(handle->h);
}
diff --git a/src/audio.h b/src/audio.h
index 2ca554d..0b6fbab 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -1,8 +1,22 @@
#ifndef AUDIO_H
#define AUDIO_H
-int audio_play(char *buf);
+typedef struct audio_handle {
+ void *h;
+ unsigned long frames;
+} audio_handle;
-int audio_record(char *buf);
+typedef enum audio_handle_types {
+ RECORD,
+ PLAYBACK
+} audio_handle_types;
+
+int audio_create(audio_handle *handle, audio_handle_types t);
+
+int audio_play(audio_handle *handle, char *buf);
+
+int audio_record(audio_handle *handle, char *buf);
+
+void audio_destroy(audio_handle *handle);
#endif
diff --git a/src/listener.c b/src/listener.c
index f819383..1968590 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -14,6 +14,8 @@ char cam_data[BUF_CAP] = {0};
unsigned int WIDTH = 1;
unsigned int HEIGHT = 1;
+audio_handle aud_handle;
+
void *display_thread(void *arg)
{
(void)arg;
@@ -32,7 +34,7 @@ void on_recv(message *m)
HEIGHT = m->HEIGHT;
memcpy(cam_data, m->video, WIDTH * HEIGHT * 3);
- audio_play(m->audio);
+ audio_play(&aud_handle, m->audio);
}
int main(void)
@@ -40,6 +42,12 @@ int main(void)
pthread_t tid;
pthread_create(&tid, NULL, display_thread, NULL);
+ if(audio_create(&aud_handle, PLAYBACK) != 0) {
+ err("audio_create: failed\n");
+ return 1;
+ }
+
+
if(listener("4950", &on_recv) != 0) {
err("listener: failed");
return 1;
diff --git a/src/talker.c b/src/talker.c
index e7271ad..d70df52 100644
--- a/src/talker.c
+++ b/src/talker.c
@@ -14,10 +14,11 @@
#define Y_RES 120
camera_handle cam_handle;
+audio_handle aud_handle;
void on_send(message *m)
{
- audio_record(m->audio);
+ audio_record(&aud_handle, m->audio);
m->WIDTH = X_RES;
m->HEIGHT = Y_RES;
@@ -34,11 +35,19 @@ int main(void)
camera_init(&cam_handle, params);
+ if(audio_create(&aud_handle, RECORD) != 0) {
+ err("audio_create: failed\n");
+ return 1;
+ }
+
+ info("created audio\n");
+
if(talker("4950", "localhost", &on_send) != 0) {
err("talker: failed\n");
return 1;
}
+ audio_destroy(&aud_handle);
camera_deinit(&cam_handle);
return 0;
}
diff --git a/src/typedef.h b/src/typedef.h
index b6b2b29..180d100 100644
--- a/src/typedef.h
+++ b/src/typedef.h
@@ -16,12 +16,10 @@
#define BUF_CAP 65500 // max datagram size with some bytes left for the headers and things
-#define FRAMES 32
+#define FRAMES 64
#define BITRATE 16
#define SAMPLING_RATE 44100
#define CHANNELS 2
-
-#define TEMP_BUF_SZ (FRAMES * CHANNELS * 2) // 2 bytes per sample
#define REC_CAP ((BITRATE * SAMPLING_RATE * CHANNELS)/240) // 30th of a second of audio
#define VID_CAP (BUF_CAP - ((2*(sizeof(int))) + REC_CAP))