📄 audiolib.c
字号:
/* Small library for reading and writing audio. This library forks an audio task and communicates with it via a shared memory segment. All what this library does can be done in principal with ordinary read/write calls on the sound device. The Linux audio driver uses so small buffers, however, that overruns/underruns are unavoidable in many cases. Copyright (C) 2000 Rainer Johanni <Rainer@Johanni.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#ifdef HAVE_CONFIG_H#include "../config.h"#endif#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <signal.h>#include <string.h>#include <errno.h>#ifdef HAVE_SYS_SOUNDCARD_H#include <sys/soundcard.h>#endif#include <sys/time.h>#include <sys/resource.h>#include <sys/mman.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/ioctl.h>/* The shared memory things */#include <sys/ipc.h>#include <sys/shm.h>#ifndef FORK_NOT_THREAD#include <pthread.h>#endif#include "mjpeg_logging.h"#include "audiolib.h"#ifdef FORK_NOT_THREADstatic int pid; /* pid of child */static int shm_seg;#elsestatic pthread_t capture_thread;#endif#define TIME_STAMP_TOL 100000 /* tolerance for timestamps in us */#define N_SHM_BUFFS 256 /* Number of buffers, must be a power of 2 */#define SHM_BUFF_MASK (N_SHM_BUFFS-1)/* #define BUFFSIZE (8192) *//* A.Stevens Jul 2000: Several drivers for modern PCI cards can't deliver frags larger than 4096 so lets not even try for 8192 byte buffer chunks*/#define BUFFSIZE (4096)#define NBUF(x) ((x)&SHM_BUFF_MASK)struct shm_buff_s{ volatile uint8_t audio_data[N_SHM_BUFFS][BUFFSIZE]; volatile int used_flag[N_SHM_BUFFS]; volatile struct timeval tmstmp[N_SHM_BUFFS]; volatile int status[N_SHM_BUFFS]; volatile int exit_flag; /* set by parent */ volatile int audio_status; /* set by audio task */ volatile int audio_start; /* trigger start in playing */ volatile char error_string[4096];} *shmemptr;static int audio_buffer_size = BUFFSIZE; /* The buffer size actually used *//* The parameters for audio capture/playback */static int initialized=0;static int audio_capt; /* Flag for capture/playback */static int mmap_capt; /* Flag for mmap/read capture */static int stereo; /* 0: capture mono, 1: capture stereo */static int audio_size; /* size of an audio sample: 8 or 16 bits */static int audio_rate; /* sampling rate for audio */static int audio_byte_rate; /* sampling rate for audio Bps*//* Buffer counter */static int n_audio;/* Bookkeeping of the write buffers */static char audio_left_buf[BUFFSIZE]; static int audio_bytes_left; /* Number of bytes in audio_left_buf */static unsigned int n_buffs_output, n_buffs_error;static struct timeval buffer_timestamp;static int usecs_per_buff;/* Forward declarations: */void do_audio(void);char *audio_strerror(void);void set_timestamp(struct timeval tmstmp);void swpcpy(char *dst, char *src, int num);typedef void *(*start_routine_p)(void *);/* some (internally used only) error numbers */static int audio_errno = 0;#define AUDIO_ERR_INIT 1 /* Not initialized */#define AUDIO_ERR_INIT2 2 /* allready initialized */#define AUDIO_ERR_ASIZE 3 /* audio size not 8 or 16 */#define AUDIO_ERR_SHMEM 4 /* Error getting shared memory segment */#define AUDIO_ERR_FORK 5 /* Can not fork audio task */#define AUDIO_ERR_MODE 6 /* Wrong read/write mode */#define AUDIO_ERR_BSIZE 7 /* Buffer size for read too small */#define AUDIO_ERR_TMOUT 8 /* Timeout waiting for audio */#define AUDIO_ERR_BOVFL 9 /* Buffer overflow when writing */#define AUDIO_ERR_ATASK 99 /* Audio task died - more in shmemptr->error_string */static char errstr[4096];char *audio_strerror(void){ switch(audio_errno) { case 0: strcpy(errstr,"No Error"); break; case AUDIO_ERR_INIT: strcpy(errstr,"Audio not initialized"); break; case AUDIO_ERR_INIT2: strcpy(errstr,"audio_init called but audio allready initialized"); break; case AUDIO_ERR_ASIZE: strcpy(errstr,"audio sample size not 8 or 16"); break; case AUDIO_ERR_SHMEM: strcpy(errstr,"Audio: Error getting shared memory segment"); break; case AUDIO_ERR_FORK: strcpy(errstr,"Can not fork audio task"); break; case AUDIO_ERR_MODE: strcpy(errstr,"Audio: Wrong read/write mode"); break; case AUDIO_ERR_BSIZE: strcpy(errstr,"Audio: Buffer size for read too small"); break; case AUDIO_ERR_TMOUT: strcpy(errstr,"Timeout waiting for audio initialization"); break; case AUDIO_ERR_BOVFL: strcpy(errstr,"Buffer overflow writing audio"); break; case AUDIO_ERR_ATASK: sprintf(errstr,"Audio task died. Reason: %s",shmemptr->error_string); break; default: strcpy(errstr,"Audio: Unknown error"); } return errstr;}/* * audio_init: Initialize audio system. * * a_read 0: User is going to write (output) audio * 1: User is going to read (input) audio * a_stereo 0: mono, 1: stereo * a_size size of an audio sample: 8 or 16 bits * a_rate sampling rate for audio * * returns 0 for success, -1 for failure * */int audio_init(int a_read, int use_read, int a_stereo, int a_size, int a_rate){ int i; /* Check if the audio task is allready initialized */ if(initialized) { audio_errno = AUDIO_ERR_INIT2; return -1; } /* Checks of parameters */ if (a_size != 8 && a_size != 16) { audio_errno = AUDIO_ERR_ASIZE; return -1; } if( use_read ) mjpeg_info( "Using read(2) system call for capture"); else mjpeg_info( "Using mmap(2) system call for capture"); /* Copy our parameters into static space */ audio_capt = a_read; mmap_capt = !use_read; stereo = a_stereo; audio_size = a_size; audio_rate = a_rate; /* Reset counters */ n_audio = 0; audio_bytes_left = 0; n_buffs_output = 0; n_buffs_error = 0; buffer_timestamp.tv_sec = 0; buffer_timestamp.tv_usec = 0; /* * Calculate bytes/second of the audio stream */ audio_byte_rate = audio_rate; if (stereo) audio_byte_rate *= 2; if (audio_size==16) audio_byte_rate *= 2; /* Set audio buffer size */ audio_buffer_size = BUFFSIZE; /* A.Stevens Jul 2000 modified to allow cards with max frag size of 4096.... if(tmp<88200) audio_buffer_size = 4096; */ if(audio_byte_rate<44100) audio_buffer_size = BUFFSIZE/2; if(audio_byte_rate<22050) audio_buffer_size = BUFFSIZE/4; /* Do not change the following calculations, they are this way to avoid overflows ! */ usecs_per_buff = audio_buffer_size*100000/audio_byte_rate; usecs_per_buff *= 10;#ifdef FORK_NOT_THREAD /* Allocate shared memory segment */ shm_seg = shmget(IPC_PRIVATE, sizeof(struct shm_buff_s), IPC_CREAT | 0777); if(shm_seg < 0) { audio_errno = AUDIO_ERR_SHMEM; return -1; } /* attach the segment and get its address */ shmemptr = (struct shm_buff_s *) shmat(shm_seg,0,0); if(shmemptr < 0) { audio_errno = AUDIO_ERR_SHMEM; return -1; } /* mark the segment as destroyed, it will be removed after the last process which had attached it is gone */ if( shmctl( shm_seg, IPC_RMID, (struct shmid_ds *)0 ) == -1 ) { audio_errno = AUDIO_ERR_SHMEM; return -1; }#else shmemptr = (struct shm_buff_s *) malloc(sizeof(struct shm_buff_s)); if( shmemptr == NULL ) { audio_errno = AUDIO_ERR_SHMEM; return -1; }#endif /* set the flags in the shared memory */ for(i=0;i<N_SHM_BUFFS;i++) shmemptr->used_flag[i] = 0; for(i=0;i<N_SHM_BUFFS;i++) shmemptr->status[i] = 0; shmemptr->exit_flag = 0; shmemptr->audio_status = 0; shmemptr->audio_start = 0; /* do the fork */#ifdef FORK_NOT_THREAD pid = fork(); if(pid<0) { audio_errno = AUDIO_ERR_FORK; return -1; } /* the child goes into the audio task */ if (pid==0) { /* The audio buffers in Linux are ridiculosly small, therefore the audio task must have a high priority, This call will fail if we are not superuser, we don't care. */ setpriority(PRIO_PROCESS, getpid(), -20); /* Ignore SIGINT while capturing, the parent wants to catch it */ if(audio_capt) signal(SIGINT,SIG_IGN); do_audio(); exit(0); }#else if( pthread_create( &capture_thread, NULL, (start_routine_p)do_audio, NULL) ) { audio_errno = AUDIO_ERR_FORK; return -1; } #endif /* Since most probably errors happen during initialization, we wait until the audio task signals either success or failure */ for(i=0;;i++) { /* Check for timeout, 10 Seconds should be plenty */ if(i>1000) {#ifdef FORK_NOT_THREAD kill(pid,SIGKILL); shmemptr->exit_flag = 1; waitpid(pid,0,0);#else shmemptr->exit_flag = 1; pthread_cancel( capture_thread ); pthread_join( capture_thread, NULL );#endif audio_errno = AUDIO_ERR_TMOUT; return -1; } if(shmemptr->audio_status<0) { audio_errno = AUDIO_ERR_ATASK; return -1; } if(shmemptr->audio_status>0) break; usleep(10000); } initialized = 1; return 0;}/* * audio_shutdown: Shutdown audio system * * It is important that this routine is called whenever the host * program finished, or else there will be the audio task * left over, having the sound device still opened and preventing * other programs from using sound. * */void audio_shutdown(void){ if(!initialized) return; /* show the child we want to exit */ shmemptr->exit_flag = 1;#ifdef FORK_NOT_THREAD waitpid(pid,0,0);#else pthread_join( capture_thread, NULL );#endif initialized = 0;}long audio_get_buffer_size(void){ return audio_buffer_size;}/* * audio_start: Actually trigger the start of audio after all * initializations have been done. * Only for playing! * * returns 0 for success, -1 for failure */void audio_start(void){ /* signal the audio task that we want to start */ shmemptr->audio_start = 1;}/* * set_timestamp: * Set buffer timestamp either to the value of the tmstmp parameter * or calculate it from previous value */voidset_timestamp(struct timeval tmstmp){ if( tmstmp.tv_sec != 0 ) { /* Time stamp is realiable */ buffer_timestamp = tmstmp; } else { /* Time stamp not reliable - calculate from previous */ if(buffer_timestamp.tv_sec != 0) { buffer_timestamp.tv_usec += usecs_per_buff; while(buffer_timestamp.tv_usec>=1000000) { buffer_timestamp.tv_usec -= 1000000; buffer_timestamp.tv_sec += 1; } } }}/* * swpcpy: like memcpy, but bytes are swapped during copy */void swpcpy(char *dst, char *src, int num){ int i; num &= ~1; /* Safety first */ for(i=0;i<num;i+=2) { dst[i ] = src[i+1]; dst[i+1] = src[i ]; }}/* * audio_read: Get audio data, if available * Behaves like a nonblocking read * * buf Buffer where to copy the data * size Size of the buffer * swap Flag if to swap the endian-ness of 16 bit data * tmstmp returned: timestamp when buffer was finished reading * contains 0 if time could not be reliably determined * status returned: 0 if buffer is corrupted * 1 if buffer is ok. * tmstmp and status are set only if something was read * * returns the number of bytes in the buffer, * 0 if nothing available * -1 if an error occured * */int audio_read( uint8_t *buf, int size, int swap, struct timeval *tmstmp, int *status){ if(!initialized) { audio_errno = AUDIO_ERR_INIT; return -1; } /* Is audio task still ok ? */ if(shmemptr->audio_status < 0) { audio_errno = AUDIO_ERR_ATASK; return -1; } if(!audio_capt) { audio_errno = AUDIO_ERR_MODE; return -1; } if(size<audio_buffer_size) { audio_errno = AUDIO_ERR_BSIZE; return -1; } /* Check if a new audio sample is ready */ if(shmemptr->used_flag[NBUF(n_audio)]) { /* Got an audio sample, copy it to the output buffer */ if(swap && audio_size==16) swpcpy((void*)buf,(void*)shmemptr->audio_data[NBUF(n_audio)],audio_buffer_size); else memcpy((void*)buf,(void*)shmemptr->audio_data[NBUF(n_audio)],audio_buffer_size); /* set the other return values */ set_timestamp(shmemptr->tmstmp[NBUF(n_audio)]); if(tmstmp) *tmstmp = buffer_timestamp; if(status) *status = shmemptr->status[NBUF(n_audio)] > 0; /* reset used_flag, increment n-audio */ shmemptr->status[NBUF(n_audio)] = 0; shmemptr->used_flag[NBUF(n_audio)] = 0; n_audio++; return audio_buffer_size; } return 0;}static void update_output_status(void){ while(shmemptr->status[NBUF(n_buffs_output)]) { if(shmemptr->status[NBUF(n_buffs_output)] < 0) n_buffs_error++; set_timestamp(shmemptr->tmstmp[NBUF(n_buffs_output)]); shmemptr->status[NBUF(n_buffs_output)] = 0; n_buffs_output++; }}void audio_get_output_status(struct timeval *tmstmp, unsigned int *nb_out, unsigned int *nb_err){ if(tmstmp) *tmstmp = buffer_timestamp; if(nb_out) *nb_out = n_buffs_output; if(nb_err) *nb_err = n_buffs_error;} /* * audio_write: Buffer audio data for output * Behaves like a nonblocking write * * buf Buffer with audio data * size Size of the buffer * swap Flag if to swap the endian-ness of 16 bit data * * returns the number of bytes actually written * -1 if an error occured * * If the number of bytes actually written is smaller * than size, the audio ringbuffer is completely filled * */int audio_write(uint8_t *buf, int size, int swap){ int nb; if(!initialized) { audio_errno = AUDIO_ERR_INIT; return -1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -