diff options
| -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)) | 
