📄 chan_oss.c
字号:
if (*ext == NULL) return NULL; if (!o->overridecontext) { /* parse from the right */ *ctx = strrchr(*ext, '@'); if (*ctx) *(*ctx)++ = '\0'; } return *ext;}/* * Returns the number of blocks used in the audio output channel */static int used_blocks(struct chan_oss_pvt *o){ struct audio_buf_info info; if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) { if (!(o->warned & WARN_used_blocks)) { ast_log(LOG_WARNING, "Error reading output space\n"); o->warned |= WARN_used_blocks; } return 1; } if (o->total_blocks == 0) { if (0) /* debugging */ ast_log(LOG_WARNING, "fragtotal %d size %d avail %d\n", info.fragstotal, info.fragsize, info.fragments); o->total_blocks = info.fragments; } return o->total_blocks - info.fragments;}/* Write an exactly FRAME_SIZE sized frame */static int soundcard_writeframe(struct chan_oss_pvt *o, short *data){ int res; if (o->sounddev < 0) setformat(o, O_RDWR); if (o->sounddev < 0) return 0; /* not fatal */ /* * Nothing complex to manage the audio device queue. * If the buffer is full just drop the extra, otherwise write. * XXX in some cases it might be useful to write anyways after * a number of failures, to restart the output chain. */ res = used_blocks(o); if (res > o->queuesize) { /* no room to write a block */ if (o->w_errors++ == 0 && (oss_debug & 0x4)) ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, o->w_errors); return 0; } o->w_errors = 0; return write(o->sounddev, ((void *) data), FRAME_SIZE * 2);}/* * Handler for 'sound writable' events from the sound thread. * Builds a frame from the high level description of the sounds, * and passes it to the audio device. * The actual sound is made of 1 or more sequences of sound samples * (s->datalen, repeated to make s->samplen samples) followed by * s->silencelen samples of silence. The position in the sequence is stored * in o->sampsent, which goes between 0 .. s->samplen+s->silencelen. * In case we fail to write a frame, don't update o->sampsent. */static void send_sound(struct chan_oss_pvt *o){ short myframe[FRAME_SIZE]; int ofs, l, start; int l_sampsent = o->sampsent; struct sound *s; if (o->cursound < 0) /* no sound to send */ return; s = &sounds[o->cursound]; for (ofs = 0; ofs < FRAME_SIZE; ofs += l) { l = s->samplen - l_sampsent; /* # of available samples */ if (l > 0) { start = l_sampsent % s->datalen; /* source offset */ if (l > FRAME_SIZE - ofs) /* don't overflow the frame */ l = FRAME_SIZE - ofs; if (l > s->datalen - start) /* don't overflow the source */ l = s->datalen - start; bcopy(s->data + start, myframe + ofs, l * 2); if (0) ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n", l_sampsent, l, s->samplen, ofs); l_sampsent += l; } else { /* end of samples, maybe some silence */ static const short silence[FRAME_SIZE] = { 0, }; l += s->silencelen; if (l > 0) { if (l > FRAME_SIZE - ofs) l = FRAME_SIZE - ofs; bcopy(silence, myframe + ofs, l * 2); l_sampsent += l; } else { /* silence is over, restart sound if loop */ if (s->repeat == 0) { /* last block */ o->cursound = -1; o->nosound = 0; /* allow audio data */ if (ofs < FRAME_SIZE) /* pad with silence */ bcopy(silence, myframe + ofs, (FRAME_SIZE - ofs) * 2); } l_sampsent = 0; } } } l = soundcard_writeframe(o, myframe); if (l > 0) o->sampsent = l_sampsent; /* update status */}static void *sound_thread(void *arg){ char ign[4096]; struct chan_oss_pvt *o = (struct chan_oss_pvt *) arg; /* * Just in case, kick the driver by trying to read from it. * Ignore errors - this read is almost guaranteed to fail. */ read(o->sounddev, ign, sizeof(ign)); for (;;) { fd_set rfds, wfds; int maxfd, res; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(o->sndcmd[0], &rfds); maxfd = o->sndcmd[0]; /* pipe from the main process */ if (o->cursound > -1 && o->sounddev < 0) setformat(o, O_RDWR); /* need the channel, try to reopen */ else if (o->cursound == -1 && o->owner == NULL) setformat(o, O_CLOSE); /* can close */ if (o->sounddev > -1) { if (!o->owner) { /* no one owns the audio, so we must drain it */ FD_SET(o->sounddev, &rfds); maxfd = MAX(o->sounddev, maxfd); } if (o->cursound > -1) { FD_SET(o->sounddev, &wfds); maxfd = MAX(o->sounddev, maxfd); } } /* ast_select emulates linux behaviour in terms of timeout handling */ res = ast_select(maxfd + 1, &rfds, &wfds, NULL, NULL); if (res < 1) { ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); sleep(1); continue; } if (FD_ISSET(o->sndcmd[0], &rfds)) { /* read which sound to play from the pipe */ int i, what = -1; read(o->sndcmd[0], &what, sizeof(what)); for (i = 0; sounds[i].ind != -1; i++) { if (sounds[i].ind == what) { o->cursound = i; o->sampsent = 0; o->nosound = 1; /* block audio from pbx */ break; } } if (sounds[i].ind == -1) ast_log(LOG_WARNING, "invalid sound index: %d\n", what); } if (o->sounddev > -1) { if (FD_ISSET(o->sounddev, &rfds)) /* read and ignore errors */ read(o->sounddev, ign, sizeof(ign)); if (FD_ISSET(o->sounddev, &wfds)) send_sound(o); } } return NULL; /* Never reached */}/* * reset and close the device if opened, * then open and initialize it in the desired mode, * trigger reads and writes so we can start using it. */static int setformat(struct chan_oss_pvt *o, int mode){ int fmt, desired, res, fd; if (o->sounddev >= 0) { ioctl(o->sounddev, SNDCTL_DSP_RESET, 0); close(o->sounddev); o->duplex = M_UNSET; o->sounddev = -1; } if (mode == O_CLOSE) /* we are done */ return 0; if (ast_tvdiff_ms(ast_tvnow(), o->lastopen) < 1000) return -1; /* don't open too often */ o->lastopen = ast_tvnow(); fd = o->sounddev = open(o->device, mode | O_NONBLOCK); if (fd < 0) { ast_log(LOG_WARNING, "Unable to re-open DSP device %s: %s\n", o->device, strerror(errno)); return -1; } if (o->owner) o->owner->fds[0] = fd;#if __BYTE_ORDER == __LITTLE_ENDIAN fmt = AFMT_S16_LE;#else fmt = AFMT_S16_BE;#endif res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt); if (res < 0) { ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n"); return -1; } switch (mode) { case O_RDWR: res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); /* Check to see if duplex set (FreeBSD Bug) */ res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt); if (res == 0 && (fmt & DSP_CAP_DUPLEX)) { if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Console is full duplex\n"); o->duplex = M_FULL; }; break; case O_WRONLY: o->duplex = M_WRITE; break; case O_RDONLY: o->duplex = M_READ; break; } fmt = 0; res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt); if (res < 0) { ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); return -1; } fmt = desired = DEFAULT_SAMPLE_RATE; /* 8000 Hz desired */ res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt); if (res < 0) { ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); return -1; } if (fmt != desired) { if (!(o->warned & WARN_speed)) { ast_log(LOG_WARNING, "Requested %d Hz, got %d Hz -- sound may be choppy\n", desired, fmt); o->warned |= WARN_speed; } } /* * on Freebsd, SETFRAGMENT does not work very well on some cards. * Default to use 256 bytes, let the user override */ if (o->frags) { fmt = o->frags; res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt); if (res < 0) { if (!(o->warned & WARN_frag)) { ast_log(LOG_WARNING, "Unable to set fragment size -- sound may be choppy\n"); o->warned |= WARN_frag; } } } /* on some cards, we need SNDCTL_DSP_SETTRIGGER to start outputting */ res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; res = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &res); /* it may fail if we are in half duplex, never mind */ return 0;}/* * some of the standard methods supported by channels. */static int oss_digit_begin(struct ast_channel *c, char digit){ return 0;}static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration){ /* no better use for received digits than print them */ ast_verbose(" << Console Received digit %c of duration %u ms >> \n", digit, duration); return 0;}static int oss_text(struct ast_channel *c, const char *text){ /* print received messages */ ast_verbose(" << Console Received text %s >> \n", text); return 0;}/* Play ringtone 'x' on device 'o' */static void ring(struct chan_oss_pvt *o, int x){ write(o->sndcmd[1], &x, sizeof(x));}/* * handler for incoming calls. Either autoanswer, or start ringing */static int oss_call(struct ast_channel *c, char *dest, int timeout){ struct chan_oss_pvt *o = c->tech_pvt; struct ast_frame f = { 0, }; ast_verbose(" << Call to device '%s' dnid '%s' rdnis '%s' on console from '%s' <%s> >>\n", dest, c->cid.cid_dnid, c->cid.cid_rdnis, c->cid.cid_name, c->cid.cid_num); if (o->autoanswer) { ast_verbose(" << Auto-answered >> \n"); f.frametype = AST_FRAME_CONTROL; f.subclass = AST_CONTROL_ANSWER; ast_queue_frame(c, &f); } else { ast_verbose("<< Type 'answer' to answer, or use 'autoanswer' for future calls >> \n"); f.frametype = AST_FRAME_CONTROL; f.subclass = AST_CONTROL_RINGING; ast_queue_frame(c, &f); ring(o, AST_CONTROL_RING); } return 0;}/* * remote side answered the phone */static int oss_answer(struct ast_channel *c){ struct chan_oss_pvt *o = c->tech_pvt; ast_verbose(" << Console call has been answered >> \n");#if 0 /* play an answer tone (XXX do we really need it ?) */ ring(o, AST_CONTROL_ANSWER);#endif ast_setstate(c, AST_STATE_UP); o->cursound = -1; o->nosound = 0; return 0;}static int oss_hangup(struct ast_channel *c){ struct chan_oss_pvt *o = c->tech_pvt; o->cursound = -1; o->nosound = 0; c->tech_pvt = NULL; o->owner = NULL; ast_verbose(" << Hangup on console >> \n"); ast_module_unref(ast_module_info->self); if (o->hookstate) { if (o->autoanswer || o->autohangup) { /* Assume auto-hangup too */ o->hookstate = 0; setformat(o, O_CLOSE); } else { /* Make congestion noise */ ring(o, AST_CONTROL_CONGESTION); } } return 0;}/* used for data coming from the network */static int oss_write(struct ast_channel *c, struct ast_frame *f){ int src; struct chan_oss_pvt *o = c->tech_pvt; /* Immediately return if no sound is enabled */ if (o->nosound) return 0; /* Stop any currently playing sound */ o->cursound = -1; /* * we could receive a block which is not a multiple of our * FRAME_SIZE, so buffer it locally and write to the device * in FRAME_SIZE chunks. * Keep the residue stored for future use. */ src = 0; /* read position into f->data */ while (src < f->datalen) { /* Compute spare room in the buffer */ int l = sizeof(o->oss_write_buf) - o->oss_write_dst; if (f->datalen - src >= l) { /* enough to fill a frame */ memcpy(o->oss_write_buf + o->oss_write_dst, f->data + src, l); soundcard_writeframe(o, (short *) o->oss_write_buf); src += l; o->oss_write_dst = 0; } else { /* copy residue */ l = f->datalen - src; memcpy(o->oss_write_buf + o->oss_write_dst, f->data + src, l); src += l; /* but really, we are done */ o->oss_write_dst += l; } } return 0;}static struct ast_frame *oss_read(struct ast_channel *c){ int res; struct chan_oss_pvt *o = c->tech_pvt; struct ast_frame *f = &o->read_f; /* XXX can be simplified returning &ast_null_frame */ /* prepare a NULL frame in case we don't have enough data to return */ bzero(f, sizeof(struct ast_frame)); f->frametype = AST_FRAME_NULL; f->src = oss_tech.type; res = read(o->sounddev, o->oss_read_buf + o->readpos, sizeof(o->oss_read_buf) - o->readpos); if (res < 0) /* audio data not ready, return a NULL frame */ return f; o->readpos += res; if (o->readpos < sizeof(o->oss_read_buf)) /* not enough samples */ return f; if (o->mute) return f; o->readpos = AST_FRIENDLY_OFFSET; /* reset read pointer for next frame */ if (c->_state != AST_STATE_UP) /* drop data if frame is not up */ return f; /* ok we can build and deliver the frame to the caller */ f->frametype = AST_FRAME_VOICE; f->subclass = AST_FORMAT_SLINEAR; f->samples = FRAME_SIZE; f->datalen = FRAME_SIZE * 2; f->data = o->oss_read_buf + AST_FRIENDLY_OFFSET; if (o->boost != BOOST_SCALE) { /* scale and clip values */ int i, x; int16_t *p = (int16_t *) f->data; for (i = 0; i < f->samples; i++) { x = (p[i] * o->boost) / BOOST_SCALE; if (x > 32767) x = 32767; else if (x < -32768) x = -32768; p[i] = x; } } f->offset = AST_FRIENDLY_OFFSET; return f;}static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan){ struct chan_oss_pvt *o = newchan->tech_pvt; o->owner = newchan;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -