diff options
author | kartofen <mladenovnasko0@gmail.com> | 2022-10-22 23:53:29 +0300 |
---|---|---|
committer | kartofen <mladenovnasko0@gmail.com> | 2022-10-22 23:53:29 +0300 |
commit | f45b143dcf90970122436d10bd6226f577a9310d (patch) | |
tree | 986563759bf1f5935e15a66613e673e445b26e97 | |
parent | f4c66a4c77e5818ca9703f59a80ea92e27ed45c6 (diff) |
-rw-r--r-- | README.md | 9 | ||||
-rwxr-xr-x | build.sh | 7 | ||||
-rw-r--r-- | src/audio.c | 143 | ||||
-rw-r--r-- | src/audio.h | 18 | ||||
-rw-r--r-- | src/listener.c | 10 | ||||
-rw-r--r-- | src/talker.c | 11 | ||||
-rw-r--r-- | src/typedef.h | 4 |
7 files changed, 124 insertions, 78 deletions
@@ -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 @@ -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(¶ms); // 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)) |