📄 ao_alsa.c
字号:
}}/* stop playing and empty buffers (for seeking/pause) */static void reset(){ int err; if ((err = snd_pcm_drop(alsa_handler)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-reset: pcm drop error: %s\n", snd_strerror(err)); return; } if ((err = snd_pcm_prepare(alsa_handler)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-reset: pcm prepare error: %s\n", snd_strerror(err)); return; } return;}#ifdef USE_POLLstatic int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count){ unsigned short revents; while (1) { poll(ufds, count, -1); snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents); if (revents & POLLERR) return -EIO; if (revents & POLLOUT) return 0; }} #endif#ifndef timersub#define timersub(a, b, result) \do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \} while (0)#endif/* I/O error handler */static int xrun(u_char *str_mode){ int err; snd_pcm_status_t *status; snd_pcm_status_alloca(&status); if ((err = snd_pcm_status(alsa_handler, status))<0) { mp_msg(MSGT_AO,MSGL_ERR,"status error: %s", snd_strerror(err)); return(0); } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { struct timeval now, diff, tstamp; gettimeofday(&now, 0); snd_pcm_status_get_trigger_tstamp(status, &tstamp); timersub(&now, &tstamp, &diff); mp_msg(MSGT_AO,MSGL_INFO,"alsa-%s: xrun of at least %.3f msecs. resetting stream\n", str_mode, diff.tv_sec * 1000 + diff.tv_usec / 1000.0); } if ((err = snd_pcm_prepare(alsa_handler))<0) { mp_msg(MSGT_AO,MSGL_ERR,"xrun: prepare error: %s", snd_strerror(err)); return(0); } return(1); /* ok, data should be accepted again */}static int play_normal(void* data, int len);static int play_mmap(void* data, int len);static int play(void* data, int len, int flags){ int result; if (ao_mmap) result = play_mmap(data, len); else result = play_normal(data, len); return result;}/* plays 'len' bytes of 'data' returns: number of bytes played modified last at 29.06.02 by jp thanxs for marius <marius@rospot.com> for giving us the light ;)*/static int play_normal(void* data, int len){ //bytes_per_sample is always 4 for 2 chn S16_LE int num_frames = len / bytes_per_sample; char *output_samples = (char *)data; snd_pcm_sframes_t res = 0; //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len); if (!alsa_handler) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: device configuration error"); return 0; } while (num_frames > 0) { res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames); if (res == -EAGAIN) { snd_pcm_wait(alsa_handler, 1000); } else if (res == -EPIPE) { /* underrun */ if (xrun("play") <= 0) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: xrun reset error"); return(0); } } else if (res == -ESTRPIPE) { /* suspend */ mp_msg(MSGT_AO,MSGL_INFO,"alsa-play: pcm in suspend mode. trying to resume\n"); while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN) sleep(1); } else if (res < 0) { mp_msg(MSGT_AO,MSGL_INFO,"alsa-play: unknown status, trying to reset soundcard\n"); if ((res = snd_pcm_prepare(alsa_handler)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: snd prepare error"); return(0); break; } } if (res > 0) { /* output_samples += ao_data.channels * res; */ output_samples += res * bytes_per_sample; num_frames -= res; } } //end while if (res < 0) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: write error %s", snd_strerror(res)); return 0; } return len - len % bytes_per_sample;}/* mmap-mode mainly based on descriptions by Joshua Haberman <joshua@haberman.com> * 'An overview of the ALSA API' http://people.debian.org/~joshua/x66.html * and some help by Paul Davis <pbd@op.net> */static int play_mmap(void* data, int len){ snd_pcm_sframes_t commitres, frames_available; snd_pcm_uframes_t frames_transmit, size, offset; const snd_pcm_channel_area_t *area; void *outbuffer; int result;#ifdef USE_POLL //seems not really be needed struct pollfd *ufds; int count; count = snd_pcm_poll_descriptors_count (alsa_handler); ufds = malloc(sizeof(struct pollfd) * count); snd_pcm_poll_descriptors(alsa_handler, ufds, count); //first wait_for_poll if (err = (wait_for_poll(alsa_handler, ufds, count) < 0)) { if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) { xrun("play"); } }#endif outbuffer = alloca(ao_data.buffersize); //don't trust get_space() ;) frames_available = snd_pcm_avail_update(alsa_handler) * bytes_per_sample; if (frames_available < 0) xrun("play"); if (frames_available < 4) { if (first) { first = 0; snd_pcm_start(alsa_handler); } else { //FIXME should break and return 0? snd_pcm_wait(alsa_handler, -1); first = 1; } } /* len is simply the available bufferspace got by get_space() * but real avail_buffer in frames is ab/bytes_per_sample */ size = len / bytes_per_sample; //mp_msg(MSGT_AO,MSGL_V,"len: %i size %i, f_avail %i, bps %i ...\n", len, size, frames_available, bytes_per_sample); frames_transmit = size; /* prepare areas and set sw-pointers * frames_transmit returns the real available buffer-size * sometimes != frames_available cause of ringbuffer 'emulation' */ snd_pcm_mmap_begin(alsa_handler, &area, &offset, &frames_transmit); /* this is specific to interleaved streams (or non-interleaved * streams with only one channel) */ outbuffer = ((char *) area->addr + (area->first + area->step * offset) / 8); //8 //write data memcpy(outbuffer, data, (frames_transmit * bytes_per_sample)); commitres = snd_pcm_mmap_commit(alsa_handler, offset, frames_transmit); if (commitres < 0 || commitres != frames_transmit) { if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) { xrun("play"); } } //mp_msg(MSGT_AO,MSGL_V,"mmap ft: %i, cres: %i\n", frames_transmit, commitres); /* err = snd_pcm_area_copy(&area, offset, &data, offset, len, alsa_format); */ /* if (err < 0) { */ /* mp_msg(MSGT_AO,MSGL_ERR,"area-copy-error\n"); */ /* return 0; */ /* } */ //calculate written frames! result = commitres * bytes_per_sample; /* if (verbose) { */ /* if (len == result) */ /* mp_msg(MSGT_AO,MSGL_V,"result: %i, frames written: %i ...\n", result, frames_transmit); */ /* else */ /* mp_msg(MSGT_AO,MSGL_V,"result: %i, frames written: %i, result != len ...\n", result, frames_transmit); */ /* } */ //mplayer doesn't like -result if (result < 0) result = 0;#ifdef USE_POLL free(ufds);#endif return result;}/* how many byes are free in the buffer */static int get_space(){ snd_pcm_status_t *status; int ret; char *str_status; //snd_pcm_sframes_t avail_frames = 0; snd_pcm_status_alloca(&status); if ((ret = snd_pcm_status(alsa_handler, status)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-space: cannot get pcm status: %s\n", snd_strerror(ret)); return(0); } switch(snd_pcm_status_get_state(status)) { case SND_PCM_STATE_OPEN: str_status = "open"; ret = snd_pcm_status_get_avail(status) * bytes_per_sample; break; case SND_PCM_STATE_PREPARED: str_status = "prepared"; first = 1; ret = snd_pcm_status_get_avail(status) * bytes_per_sample; if (ret == 0) //ugly workaround for hang in mmap-mode ret = 10; break; case SND_PCM_STATE_RUNNING: ret = snd_pcm_status_get_avail(status) * bytes_per_sample; //avail_frames = snd_pcm_avail_update(alsa_handler) * bytes_per_sample; if (str_status != "open" && str_status != "prepared") str_status = "running"; break; case SND_PCM_STATE_PAUSED: mp_msg(MSGT_AO,MSGL_V,"alsa-space: paused"); str_status = "paused"; ret = 0; break; case SND_PCM_STATE_XRUN: xrun("space"); str_status = "xrun"; first = 1; ret = 0; break; default: str_status = "undefined"; ret = snd_pcm_status_get_avail(status) * bytes_per_sample; if (ret <= 0) { xrun("space"); } } if (snd_pcm_status_get_state(status) != SND_PCM_STATE_RUNNING) mp_msg(MSGT_AO,MSGL_V,"alsa-space: free space = %i, %s --\n", ret, str_status); if (ret < 0) { mp_msg(MSGT_AO,MSGL_ERR,"negative value!!\n"); ret = 0; } // workaround for too small value returned if (ret < MIN_CHUNK_SIZE) ret = 0; return(ret);}/* delay in seconds between first and last sample in buffer */static float get_delay(){ if (alsa_handler) { snd_pcm_status_t *status; float ret; snd_pcm_status_alloca(&status); if ((ret = snd_pcm_status(alsa_handler, status)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,"alsa-delay: cannot get pcm status: %s\n", snd_strerror(ret)); } switch(snd_pcm_status_get_state(status)) { case SND_PCM_STATE_OPEN: case SND_PCM_STATE_PREPARED: case SND_PCM_STATE_RUNNING: ret = (float)snd_pcm_status_get_delay(status)/(float)ao_data.samplerate; break; default: ret = 0; } if (ret < 0) ret = 0; return(ret); } else { return(0); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -