📄 snd-oss.c
字号:
#include "config.h"#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <pthread.h>#include <sys/ioctl.h>#ifdef HAVE_SOUNDCARD_H# include <soundcard.h>#endif#ifdef HAVE_SYS_SOUNDCARD_H# include <sys/soundcard.h>#endif#include "grab-ng.h"/* -------------------------------------------------------------------- */extern int debug;static const char *names[] = SOUND_DEVICE_NAMES;static int mixer_read_attr(struct ng_attribute *attr);static void mixer_write_attr(struct ng_attribute *attr, int val);struct mixer_handle { int mix; int volctl; int volume; int muted;};static struct ng_attribute mixer_attrs[] = { { id: ATTR_ID_MUTE, name: "mute", type: ATTR_TYPE_BOOL, read: mixer_read_attr, write: mixer_write_attr, },{ id: ATTR_ID_VOLUME, name: "volume", type: ATTR_TYPE_INTEGER, min: 0, max: 100, read: mixer_read_attr, write: mixer_write_attr, },{ /* end of list */ }};static voidmixer_close(void *handle){ struct mixer_handle *h = handle; if (-1 != h->mix) close(h->mix); free(h);}static void*mixer_open(char *device){ struct mixer_handle *h; h = malloc(sizeof(*h)); memset(h,0,sizeof(*h)); h->mix = -1; h->volctl = -1; if (-1 == (h->mix = open(device,O_RDONLY))) { fprintf(stderr,"open %s: %s\n",device,strerror(errno)); goto err; } fcntl(h->mix,F_SETFD,FD_CLOEXEC); return h; err: mixer_close(h); return NULL;}static struct ng_attribute*mixer_volctl(void *handle, char *channel){ struct mixer_handle *h = handle; struct ng_attribute *attrs; int i, devmask; if (-1 == ioctl(h->mix,MIXER_READ(SOUND_MIXER_DEVMASK),&devmask)) { fprintf(stderr,"oss mixer read devmask: %s",strerror(errno)); return NULL; } for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if ((1<<i) & devmask && strcasecmp(names[i],channel) == 0) { if (-1 == ioctl(h->mix,MIXER_READ(i),&h->volume)) { fprintf(stderr,"oss mixer read volume: %s",strerror(errno)); return NULL; } else { h->volctl = i; } } } if (-1 == h->volctl) { fprintf(stderr,"oss mixer: '%s' not found, available:", channel); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if ((1<<i) & devmask) fprintf(stderr," '%s'",names[i]); fprintf(stderr,"\n"); return NULL; } attrs = malloc(sizeof(mixer_attrs)); memcpy(attrs,mixer_attrs,sizeof(mixer_attrs)); for (i = 0; attrs[i].name != NULL; i++) attrs[i].handle = h; return attrs;}static intmixer_read_attr(struct ng_attribute *attr){ struct mixer_handle *h = attr->handle; int vol; switch (attr->id) { case ATTR_ID_VOLUME: if (-1 == ioctl(h->mix,MIXER_READ(h->volctl),&h->volume)) perror("oss mixer read volume"); vol = h->volume & 0x7f; return vol; case ATTR_ID_MUTE: return h->muted; default: return -1; }}static voidmixer_write_attr(struct ng_attribute *attr, int val){ struct mixer_handle *h = attr->handle; switch (attr->id) { case ATTR_ID_VOLUME: val &= 0x7f; h->volume = val | (val << 8); if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&h->volume)) perror("oss mixer write volume"); h->muted = 0; break; case ATTR_ID_MUTE: h->muted = val; if (h->muted) { int zero = 0; if (-1 == ioctl(h->mix,MIXER_READ(h->volctl),&h->volume)) perror("oss mixer read volume"); if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&zero)) perror("oss mixer write volume"); } else { if (-1 == ioctl(h->mix,MIXER_WRITE(h->volctl),&h->volume)) perror("oss mixer write volume"); } break; }}static struct ng_devinfo* mixer_probe(void){ struct ng_devinfo *info = NULL; int i,n,fd;#ifdef SOUND_MIXER_INFO mixer_info minfo;#endif n = 0; for (i = 0; NULL != ng_dev.mixer_scan[i]; i++) { fd = open(ng_dev.mixer_scan[i],O_RDONLY); if (-1 == fd) continue; info = realloc(info,sizeof(*info) * (n+2)); memset(info+n,0,sizeof(*info)*2); strcpy(info[n].device,ng_dev.mixer_scan[i]);#ifdef SOUND_MIXER_INFO ioctl(fd,SOUND_MIXER_INFO,&minfo); strcpy(info[n].name,minfo.name);#else strcpy(info[n].name,ng_dev.mixer_scan[i]);#endif close(fd); n++; } return info;}static struct ng_devinfo*mixer_channels(char *device){ struct ng_devinfo *info = NULL; static char *names[] = SOUND_DEVICE_NAMES; static char *labels[] = SOUND_DEVICE_LABELS; int fd,i,n,devmask; if (-1 == (fd = open(device,O_RDONLY))) { fprintf(stderr,"open %s: %s\n",device,strerror(errno)); return NULL; } n = 0; ioctl(fd,MIXER_READ(SOUND_MIXER_DEVMASK),&devmask); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (!((1<<i) & devmask)) continue; info = realloc(info,sizeof(*info) * (n+2)); memset(info+n,0,sizeof(*info)*2); strcpy(info[n].device,names[i]); strcpy(info[n].name,labels[i]); n++; } close(fd); return info;}struct ng_mix_driver oss_mixer = { name: "oss", probe: mixer_probe, channels: mixer_channels, open: mixer_open, volctl: mixer_volctl, close: mixer_close,};/* ------------------------------------------------------------------- */struct oss_handle { int fd; /* oss */ struct ng_audio_fmt ifmt; unsigned int afmt,channels,rate; unsigned int blocksize; /* me */ struct ng_audio_fmt ofmt; int byteswap; int bytes; int bytes_per_sec;};static const unsigned int afmt_to_oss[AUDIO_FMT_COUNT] = { 0, AFMT_U8, AFMT_U8, AFMT_S16_LE, AFMT_S16_LE, AFMT_S16_BE, AFMT_S16_BE};static intoss_setformat(struct oss_handle *h, struct ng_audio_fmt *fmt){ int frag; if (0 == afmt_to_oss[fmt->fmtid]) return -1; h->afmt = afmt_to_oss[fmt->fmtid]; h->channels = ng_afmt_to_channels[fmt->fmtid]; frag = 0x7fff000c; /* 4k */ /* format */ ioctl(h->fd, SNDCTL_DSP_SETFMT, &h->afmt); if (h->afmt != afmt_to_oss[fmt->fmtid]) { if (ng_debug) fprintf(stderr,"oss: SNDCTL_DSP_SETFMT(%d): %s\n", afmt_to_oss[fmt->fmtid],strerror(errno)); goto err; } /* channels */ ioctl(h->fd, SNDCTL_DSP_CHANNELS, &h->channels); if (h->channels != ng_afmt_to_channels[fmt->fmtid]) { if (ng_debug) fprintf(stderr,"oss: SNDCTL_DSP_CHANNELS(%d): %s\n", ng_afmt_to_channels[fmt->fmtid],strerror(errno)); goto err; } /* sample rate */ h->rate = fmt->rate; ioctl(h->fd, SNDCTL_DSP_SPEED, &h->rate); ioctl(h->fd, SNDCTL_DSP_SETFRAGMENT, &frag); if (h->rate != fmt->rate) { fprintf(stderr, "oss: warning: got sample rate %d (asked for %d)\n", h->rate,fmt->rate); if (h->rate < fmt->rate * 1001 / 1000 && h->rate > fmt->rate * 999 / 1000) { /* ignore very small differences ... */ h->rate = fmt->rate; } } if (-1 == ioctl(h->fd, SNDCTL_DSP_GETBLKSIZE, &h->blocksize)) { if (ng_debug) perror("SNDCTL_DSP_GETBLKSIZE"); goto err; } if (0 == h->blocksize) /* dmasound bug compatibility */ h->blocksize = 4096; if (ng_debug) fprintf(stderr,"oss: bs=%d rate=%d channels=%d bits=%d (%s)\n", h->blocksize,h->rate, ng_afmt_to_channels[fmt->fmtid], ng_afmt_to_bits[fmt->fmtid], ng_afmt_to_desc[fmt->fmtid]); return 0; err: if (ng_debug) fprintf(stderr,"oss: sound format not supported [%s]\n", ng_afmt_to_desc[fmt->fmtid]); return -1;}static void*oss_open(char *device, struct ng_audio_fmt *fmt, int record){ struct oss_handle *h; struct ng_audio_fmt ifmt; h = malloc(sizeof(*h)); if (NULL == h) return NULL; memset(h,0,sizeof(*h)); if (-1 == (h->fd = open(device ? device : ng_dev.dsp, record ? O_RDONLY : O_WRONLY | O_NONBLOCK))) { fprintf(stderr,"oss: open %s: %s\n", device ? device : ng_dev.dsp, strerror(errno)); goto err; } fcntl(h->fd,F_SETFD,FD_CLOEXEC); if (0 == oss_setformat(h,fmt)) { /* fine, native format works */ fmt->rate = h->rate; h->ifmt = *fmt; h->ofmt = *fmt; h->bytes_per_sec = ng_afmt_to_bits[h->ifmt.fmtid] * ng_afmt_to_channels[h->ifmt.fmtid] * h->ifmt.rate / 8; return h; } /* try byteswapping */ ifmt = *fmt; switch (fmt->fmtid) { case AUDIO_S16_LE_MONO: ifmt.fmtid = AUDIO_S16_BE_MONO; break; case AUDIO_S16_LE_STEREO: ifmt.fmtid = AUDIO_S16_BE_STEREO; break; case AUDIO_S16_BE_MONO: ifmt.fmtid = AUDIO_S16_LE_MONO; break; case AUDIO_S16_BE_STEREO: ifmt.fmtid = AUDIO_S16_LE_STEREO; break; } if (0 == oss_setformat(h,&ifmt)) { if (ng_debug) fprintf(stderr,"oss: byteswapping pcm data\n"); h->byteswap = 1; ifmt.rate = h->rate; fmt->rate = h->rate; h->ifmt = ifmt; h->ofmt = *fmt; h->bytes_per_sec = ng_afmt_to_bits[h->ifmt.fmtid] * ng_afmt_to_channels[h->ifmt.fmtid] * h->ifmt.rate / 8; return h; } fprintf(stderr,"oss: can't use format %s\n", ng_afmt_to_desc[fmt->fmtid]); err: fmt->rate = 0; fmt->fmtid = AUDIO_NONE; if (h->fd) close(h->fd); free(h); return NULL;}static intoss_startrec(void *handle){ struct oss_handle *h = handle; int trigger; if (ng_debug) fprintf(stderr,"oss: startrec\n"); trigger = 0; ioctl(h->fd,SNDCTL_DSP_SETTRIGGER,&trigger);#if 1 /* * Try to clear the sound driver buffers. IMHO this shouldn't * be needed, but looks like it is with some drivers ... */ { int oflags,flags,rc; unsigned char buf[4096]; oflags = fcntl(h->fd,F_GETFL); flags = oflags | O_NONBLOCK; fcntl(h->fd,F_SETFL,flags); for (;;) { rc = read(h->fd,buf,sizeof(buf)); if (ng_debug) fprintf(stderr,"oss: clearbuf rc=%d errno=%s\n",rc,strerror(errno)); if (rc != sizeof(buf)) break; } fcntl(h->fd,F_SETFL,oflags); }#endif trigger = PCM_ENABLE_INPUT; ioctl(h->fd,SNDCTL_DSP_SETTRIGGER,&trigger); return 0;}static voidoss_bufread(int fd,char *buffer,int blocksize){ int rc,count=0; /* why FreeBSD returns chunks smaller than blocksize? */ for (;;) { rc = read(fd,buffer+count,blocksize-count); if (rc < 0) { if (EINTR == errno) continue; perror("oss: read"); exit(1); } count += rc; if (count == blocksize) return; } fprintf(stderr,"#");}static voidoss_bufswap(void *ptr, int size){ unsigned short *buf = ptr; int i; size = size >> 1; for (i = 0; i < size; i++) buf[i] = ((buf[i] >> 8) & 0xff) | ((buf[i] << 8) & 0xff00);}static struct ng_audio_buf*oss_read(void *handle, int64_t stopby){ struct oss_handle *h = handle; struct ng_audio_buf* buf; int bytes; if (stopby) { bytes = stopby * h->bytes_per_sec / 1000000000 - h->bytes; if (ng_debug) fprintf(stderr,"oss: left: %d bytes (%.3fs)\n", bytes,(float)bytes/h->bytes_per_sec); if (bytes <= 0) return NULL; bytes = (bytes + 3) & ~3; if (bytes > (int)h->blocksize) bytes = h->blocksize; } else { bytes = h->blocksize; } buf = ng_malloc_audio_buf(&h->ofmt,bytes); oss_bufread(h->fd,buf->data,bytes); if (h->byteswap) oss_bufswap(buf->data,bytes); h->bytes += bytes; buf->info.ts = (long long)h->bytes * 1000000000 / h->bytes_per_sec; return buf;}static struct ng_audio_buf*oss_write(void *handle, struct ng_audio_buf *buf){ struct oss_handle *h = handle; int rc; if (0 == buf->written && h->byteswap) oss_bufswap(buf->data,buf->size); rc = write(h->fd, buf->data+buf->written, buf->size-buf->written); switch (rc) { case -1: perror("write dsp"); free(buf); buf = NULL; case 0: fprintf(stderr,"write dsp: Huh? no data written?\n"); free(buf); buf = NULL; default: buf->written += rc; if (buf->written == buf->size) { free(buf); buf = NULL; } } return buf;}static int64_toss_latency(void *handle){ struct oss_handle *h = handle; audio_buf_info info; int bytes,samples; long long latency; if (-1 == ioctl(h->fd, SNDCTL_DSP_GETOSPACE, &info)) return 0; bytes = info.fragsize * info.fragstotal; samples = bytes * 8 / ng_afmt_to_bits[h->ifmt.fmtid] / h->channels; latency = (long long)samples * 1000000000 / h->rate; if (ng_debug) fprintf(stderr,"oss: bytes: %d / samples: %d => latency: %lld\n", bytes,samples,latency); return latency;}static intoss_fd(void *handle){ struct oss_handle *h = handle; return h->fd;}static voidoss_close(void *handle){ struct oss_handle *h = handle; close(h->fd); free(h);}/* ------------------------------------------------------------------- */static struct ng_dsp_driver oss_dsp = { name: "oss", open: oss_open, close: oss_close, fd: oss_fd, startrec: oss_startrec, read: oss_read, write: oss_write, latency: oss_latency,};extern void ng_plugin_init(void);void ng_plugin_init(void){ ng_dsp_driver_register(NG_PLUGIN_MAGIC,__FILE__,&oss_dsp); ng_mix_driver_register(NG_PLUGIN_MAGIC,__FILE__,&oss_mixer);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -