123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /*
- @file
- @brief record demo for linux
- @author taozhang9
- @date 2016/05/27
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <alsa/asoundlib.h>
- #include <signal.h>
- #include <sys/stat.h>
- #include <pthread.h>
- #include "../../include/formats.h"
- #include "../../include/linuxrec.h"
- #define DBG_ON 1
- #if DBG_ON
- #define dbg printf
- #else
- #define dbg
- #endif
- /* Do not change the sequence */
- enum {
- RECORD_STATE_CREATED, /* Init */
- RECORD_STATE_CLOSING,
- RECORD_STATE_READY, /* Opened */
- RECORD_STATE_STOPPING, /* During Stop */
- RECORD_STATE_RECORDING, /* Started */
- };
- #define SAMPLE_RATE 16000
- #define SAMPLE_BIT_SIZE 16
- #define FRAME_CNT 10
- //#define BUF_COUNT 1
- #define DEF_BUFF_TIME 500000
- #define DEF_PERIOD_TIME 100000
- static int show_xrun = 1;
- static int start_record_internal(snd_pcm_t *pcm)
- {
- return snd_pcm_start(pcm);
- }
- static int stop_record_internal(snd_pcm_t *pcm)
- {
- return snd_pcm_drop(pcm);
- }
- static int is_stopped_internal(struct recorder *rec)
- {
- snd_pcm_state_t state;
- state = snd_pcm_state((snd_pcm_t *)rec->wavein_hdl);
- switch (state) {
- case SND_PCM_STATE_RUNNING:
- case SND_PCM_STATE_DRAINING:
- return 0;
- default: break;
- }
- return 1;
- }
- static int format_ms_to_alsa(const WAVEFORMATEX * wavfmt,
- snd_pcm_format_t * format)
- {
- snd_pcm_format_t tmp;
- tmp = snd_pcm_build_linear_format(wavfmt->wBitsPerSample,
- wavfmt->wBitsPerSample, wavfmt->wBitsPerSample == 8 ? 1 : 0, 0);
- if ( tmp == SND_PCM_FORMAT_UNKNOWN )
- return -EINVAL;
- *format = tmp;
- return 0;
- }
- /* set hardware and software params */
- static int set_hwparams(struct recorder * rec, const WAVEFORMATEX *wavfmt,
- unsigned int buffertime, unsigned int periodtime)
- {
- snd_pcm_hw_params_t *params;
- int err;
- unsigned int rate;
- snd_pcm_format_t format;
- snd_pcm_uframes_t size;
- snd_pcm_t *handle = (snd_pcm_t *)rec->wavein_hdl;
- rec->buffer_time = buffertime;
- rec->period_time = periodtime;
- snd_pcm_hw_params_alloca(¶ms);
- err = snd_pcm_hw_params_any(handle, params);
- if (err < 0) {
- dbg("Broken configuration for this PCM");
- return err;
- }
- err = snd_pcm_hw_params_set_access(handle, params,
- SND_PCM_ACCESS_RW_INTERLEAVED);
- if (err < 0) {
- dbg("Access type not available");
- return err;
- }
- err = format_ms_to_alsa(wavfmt, &format);
- if (err) {
- dbg("Invalid format");
- return - EINVAL;
- }
- err = snd_pcm_hw_params_set_format(handle, params, format);
- if (err < 0) {
- dbg("Sample format non available");
- return err;
- }
- err = snd_pcm_hw_params_set_channels(handle, params, wavfmt->nChannels);
- if (err < 0) {
- dbg("Channels count non available");
- return err;
- }
- rate = wavfmt->nSamplesPerSec;
- err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0);
- if (err < 0) {
- dbg("Set rate failed");
- return err;
- }
- if(rate != wavfmt->nSamplesPerSec) {
- dbg("Rate mismatch");
- return -EINVAL;
- }
- if (rec->buffer_time == 0 || rec->period_time == 0) {
- err = snd_pcm_hw_params_get_buffer_time_max(params,
- &rec->buffer_time, 0);
- assert(err >= 0);
- if (rec->buffer_time > 500000)
- rec->buffer_time = 500000;
- rec->period_time = rec->buffer_time / 4;
- }
- err = snd_pcm_hw_params_set_period_time_near(handle, params,
- &rec->period_time, 0);
- if (err < 0) {
- dbg("set period time fail");
- return err;
- }
- err = snd_pcm_hw_params_set_buffer_time_near(handle, params,
- &rec->buffer_time, 0);
- if (err < 0) {
- dbg("set buffer time failed");
- return err;
- }
- err = snd_pcm_hw_params_get_period_size(params, &size, 0);
- if (err < 0) {
- dbg("get period size fail");
- return err;
- }
- rec->period_frames = size;
- err = snd_pcm_hw_params_get_buffer_size(params, &size);
- if (size == rec->period_frames) {
- dbg("Can't use period equal to buffer size (%lu == %u)",
- size, rec->period_frames);
- return -EINVAL;
- }
- rec->buffer_frames = size;
- rec->bits_per_frame = wavfmt->wBitsPerSample;
- /* set to driver */
- err = snd_pcm_hw_params(handle, params);
- if (err < 0) {
- dbg("Unable to install hw params:");
- return err;
- }
- return 0;
- }
- static int set_swparams(struct recorder * rec)
- {
- int err;
- snd_pcm_sw_params_t *swparams;
- snd_pcm_t * handle = (snd_pcm_t*)(rec->wavein_hdl);
- /* sw para */
- snd_pcm_sw_params_alloca(&swparams);
- err = snd_pcm_sw_params_current(handle, swparams);
- if (err < 0) {
- dbg("get current sw para fail");
- return err;
- }
- err = snd_pcm_sw_params_set_avail_min(handle, swparams,
- rec->period_frames);
- if (err < 0) {
- dbg("set avail min failed");
- return err;
- }
- /* set a value bigger than the buffer frames to prevent the auto start.
- * we use the snd_pcm_start to explicit start the pcm */
- err = snd_pcm_sw_params_set_start_threshold(handle, swparams,
- rec->buffer_frames * 2);
- if (err < 0) {
- dbg("set start threshold fail");
- return err;
- }
- if ( (err = snd_pcm_sw_params(handle, swparams)) < 0) {
- dbg("unable to install sw params:");
- return err;
- }
- return 0;
- }
- static int set_params(struct recorder *rec, WAVEFORMATEX *fmt,
- unsigned int buffertime, unsigned int periodtime)
- {
- int err;
- WAVEFORMATEX defmt =
- {WAVE_FORMAT_PCM, 1, 16000, 32000, 2, 16, sizeof(WAVEFORMATEX)};
- if (fmt == NULL) {
- fmt = &defmt;
- }
- err = set_hwparams(rec, fmt, buffertime, periodtime);
- if (err)
- return err;
- err = set_swparams(rec);
- if (err)
- return err;
- return 0;
- }
- /*
- * Underrun and suspend recovery
- */
- static int xrun_recovery(snd_pcm_t *handle, int err)
- {
- if (err == -EPIPE) { /* over-run */
- if (show_xrun)
- printf("!!!!!!overrun happend!!!!!!");
- err = snd_pcm_prepare(handle);
- if (err < 0) {
- if (show_xrun)
- printf("Can't recovery from overrun,"
- "prepare failed: %s\n", snd_strerror(err));
- return err;
- }
- return 0;
- } else if (err == -ESTRPIPE) {
- while ((err = snd_pcm_resume(handle)) == -EAGAIN)
- usleep(200000); /* wait until the suspend flag is released */
- if (err < 0) {
- err = snd_pcm_prepare(handle);
- if (err < 0) {
- if (show_xrun)
- printf("Can't recovery from suspend,"
- "prepare failed: %s\n", snd_strerror(err));
- return err;
- }
- }
- return 0;
- }
- return err;
- }
- static ssize_t pcm_read(struct recorder *rec, size_t rcount)
- {
- ssize_t r;
- size_t count = rcount;
- char *data;
- snd_pcm_t *handle = (snd_pcm_t *)rec->wavein_hdl;
- if(!handle)
- return -EINVAL;
- data = rec->audiobuf;
- while (count > 0) {
- r = snd_pcm_readi(handle, data, count);
- if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
- snd_pcm_wait(handle, 100);
- } else if (r < 0) {
- if(xrun_recovery(handle, r) < 0) {
- return -1;
- }
- }
- if (r > 0) {
- count -= r;
- data += r * rec->bits_per_frame / 8;
- }
- }
- return rcount;
- }
- static void * record_thread_proc(void * para)
- {
- struct recorder * rec = (struct recorder *) para;
- size_t frames, bytes;
- sigset_t mask, oldmask;
- sigemptyset(&mask);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGTERM);
- pthread_sigmask(SIG_BLOCK, &mask, &oldmask);
- while(1) {
- frames = rec->period_frames;
- bytes = frames * rec->bits_per_frame / 8;
- /* closing, exit the thread */
- if (rec->state == RECORD_STATE_CLOSING)
- break;
- if(rec->state < RECORD_STATE_RECORDING)
- usleep(100000);
- if (pcm_read(rec, frames) != frames) {
- return NULL;
- }
- if (rec->on_data_ind)
- rec->on_data_ind(rec->audiobuf, bytes,
- rec->user_cb_para);
- }
- return rec;
- }
- static int create_record_thread(void * para, pthread_t * tidp)
- {
- int err;
- err = pthread_create(tidp, NULL, record_thread_proc, (void *)para);
- if (err != 0)
- return err;
- return 0;
- }
- static void free_rec_buffer(struct recorder * rec)
- {
- if (rec->audiobuf) {
- free(rec->audiobuf);
- rec->audiobuf = NULL;
- }
- }
- static int prepare_rec_buffer(struct recorder * rec )
- {
- /* the read and QISRWrite is blocked, currently only support one buffer,
- * if overrun too much, need more buffer and another new thread
- * to write the audio to network */
- size_t sz = (rec->period_frames * rec->bits_per_frame / 8);
- rec->audiobuf = (char *)malloc(sz);
- if(!rec->audiobuf)
- return -ENOMEM;
- return 0;
- }
- static int open_recorder_internal(struct recorder * rec,
- record_dev_id dev, WAVEFORMATEX * fmt)
- {
- int err = 0;
- err = snd_pcm_open((snd_pcm_t **)&rec->wavein_hdl, dev.u.name,
- SND_PCM_STREAM_CAPTURE, 0);
- if(err < 0)
- goto fail;
- err = set_params(rec, fmt, DEF_BUFF_TIME, DEF_PERIOD_TIME);
- if(err)
- goto fail;
- assert(rec->bufheader == NULL);
- err = prepare_rec_buffer(rec);
- if(err)
- goto fail;
- err = create_record_thread((void*)rec,
- &rec->rec_thread);
- if(err)
- goto fail;
- return 0;
- fail:
- if(rec->wavein_hdl)
- snd_pcm_close((snd_pcm_t *) rec->wavein_hdl);
- rec->wavein_hdl = NULL;
- free_rec_buffer(rec);
- return err;
- }
- static void close_recorder_internal(struct recorder *rec)
- {
- snd_pcm_t * handle;
- handle = (snd_pcm_t *) rec->wavein_hdl;
- /* may be the thread is blocked at read, cancel it */
- pthread_cancel(rec->rec_thread);
- /* wait for the pcm thread quit first */
- pthread_join(rec->rec_thread, NULL);
- if(handle) {
- snd_pcm_close(handle);
- rec->wavein_hdl = NULL;
- }
- free_rec_buffer(rec);
- }
- /* return the count of pcm device */
- /* list all cards */
- static int get_pcm_device_cnt(snd_pcm_stream_t stream)
- {
- void **hints, **n;
- char *io, *filter, *name;
- int cnt = 0;
- if (snd_device_name_hint(-1, "pcm", &hints) < 0)
- return 0;
- n = hints;
- filter = stream == SND_PCM_STREAM_CAPTURE ? "Input" : "Output";
- while (*n != NULL) {
- io = snd_device_name_get_hint(*n, "IOID");
- name = snd_device_name_get_hint(*n, "NAME");
- if (name && (io == NULL || strcmp(io, filter) == 0))
- cnt ++;
- if (io != NULL)
- free(io);
- if (name != NULL)
- free(name);
- n++;
- }
- snd_device_name_free_hint(hints);
- return cnt;
- }
- /* -------------------------------------
- * Interfaces
- --------------------------------------*/
- /* the device id is a pcm string name in linux */
- record_dev_id get_default_input_dev()
- {
- record_dev_id id;
- id.u.name = "default";
- return id;
- }
- record_dev_id * list_input_device()
- {
- // TODO: unimplemented
- return NULL;
- }
- int get_input_dev_num()
- {
- return get_pcm_device_cnt(SND_PCM_STREAM_CAPTURE);
- }
- /* callback will be run on a new thread */
- int create_recorder(struct recorder ** out_rec,
- void (*on_data_ind)(char *data, unsigned long len, void *user_cb_para),
- void* user_cb_para)
- {
- struct recorder * myrec;
- myrec = (struct recorder *)malloc(sizeof(struct recorder));
- if(!myrec)
- return -RECORD_ERR_MEMFAIL;
- memset(myrec, 0, sizeof(struct recorder));
- myrec->on_data_ind = on_data_ind;
- myrec->user_cb_para = user_cb_para;
- myrec->state = RECORD_STATE_CREATED;
- *out_rec = myrec;
- return 0;
- }
- void destroy_recorder(struct recorder *rec)
- {
- if(!rec)
- return;
- free(rec);
- }
- int open_recorder(struct recorder * rec, record_dev_id dev, WAVEFORMATEX * fmt)
- {
- int ret = 0;
- if(!rec )
- return -RECORD_ERR_INVAL;
- if(rec->state >= RECORD_STATE_READY)
- return 0;
- ret = open_recorder_internal(rec, dev, fmt);
- if(ret == 0)
- rec->state = RECORD_STATE_READY;
- return 0;
- }
- void close_recorder(struct recorder *rec)
- {
- if(rec == NULL || rec->state < RECORD_STATE_READY)
- return;
- if(rec->state == RECORD_STATE_RECORDING)
- stop_record(rec);
- rec->state = RECORD_STATE_CLOSING;
- close_recorder_internal(rec);
- rec->state = RECORD_STATE_CREATED;
- }
- int start_record(struct recorder * rec)
- {
- int ret = 0;
- if(rec == NULL)
- return -RECORD_ERR_INVAL;
- if( rec->state < RECORD_STATE_READY)
- return -RECORD_ERR_NOT_READY;
- if( rec->state == RECORD_STATE_RECORDING)
- return 0;
- ret = start_record_internal((snd_pcm_t *)rec->wavein_hdl);
- if(ret == 0)
- rec->state = RECORD_STATE_RECORDING;
- return ret;
- }
- int stop_record(struct recorder * rec)
- {
- int ret;
- if(rec == NULL)
- return -RECORD_ERR_INVAL;
- if( rec->state < RECORD_STATE_RECORDING)
- return 0;
- rec->state = RECORD_STATE_STOPPING;
- ret = stop_record_internal((snd_pcm_t *)rec->wavein_hdl);
- if(ret == 0) {
- rec->state = RECORD_STATE_READY;
- }
- return ret;
- }
- int is_record_stopped(struct recorder *rec)
- {
- if(rec->state == RECORD_STATE_RECORDING)
- return 0;
- return is_stopped_internal(rec);
- }
|