全志 A40i alsalib 关于 -EPIPE 错误
问题和现象:
由于网络语音通话存在网络延时问题,导致声卡发出 杂音.
分析思路:
查看dmesg 发现 底层驱动出现不停的开关声卡.
[ 3011.197736] Enter sunxi_daudio_txctrl_enable, enable 1
[ 3011.197763] End sunxi_daudio_txctrl_enable, enable 1
[ 3011.793949] Enter sunxi_daudio_txctrl_enable, enable 0
[ 3011.793980] End sunxi_daudio_txctrl_enable, enable 0
跟踪发现是出现了XRUN 现象,XRUN详解可参考:
https://blog.csdn.net/u010872301/article/details/84397167
解决方法:
增加period_time 和 buffer_time 的初始大小.
网上扒的测试代码.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <alsa/asoundlib.h> #include <unistd.h> typedef struct { char *file_name; char *device; int ch; int bit; int rate; snd_pcm_format_t format; unsigned int buffer_time; unsigned int period_time; snd_pcm_uframes_t buffer; snd_pcm_uframes_t period; }CONFIG_T; typedef struct { char id[4]; int size; }RIFF_HEADER_T; typedef struct { char format[4]; char sub_id_1[4]; int sub_size_1; short fmt; short ch; int sample_rate; int byte_rate; short block_align; short bits_per_sample; }WAVE_HEADER_T; typedef struct { char sub_id_2[4]; int sub_size_2; }DATA_HEADER_T; RIFF_HEADER_T riff_header; WAVE_HEADER_T wave_header; DATA_HEADER_T data_header; CONFIG_T config; static void help(void) { printf( "Usage: pcm [OPTION]... [FILE]...\n" "-h,--help help\n" "-D,--device playback device\n" "-i,--input_file only support wav file\n" "\n"); } static void parse_cmd(int argc, char **argv) { struct option long_option[] = { {"help", 0, NULL, 'h'}, {"device", 1, NULL, 'D'}, {"input_file", 1, NULL, 'i'}, {NULL, 0, NULL, 0}, }; while (1) { int c; if ((c = getopt_long(argc, argv, "hD:i:", long_option, NULL)) < 0) break; switch (c) { case 'h': help(); break; case 'D': config.device = strdup(optarg); break; case 'i': config.file_name = strdup(optarg); break; } } } static snd_pcm_format_t get_format(int bits_per_sample) { snd_pcm_format_t format = SND_PCM_FORMAT_S8; switch(bits_per_sample) { case 8: format = SND_PCM_FORMAT_S8; break; case 16: format = SND_PCM_FORMAT_S16_LE; break; case 24: format = SND_PCM_FORMAT_S24_LE; break; case 32: format = SND_PCM_FORMAT_S32_LE; break; } return format; } static int config_init(CONFIG_T *pConfig) { FILE *fp = NULL; if (NULL == pConfig) { return -1; } fp = fopen(pConfig->file_name, "rb"); if (NULL == fp) { return -1; } fread((void *)&riff_header, sizeof(RIFF_HEADER_T), 1, fp); fread((void *)&wave_header, sizeof(WAVE_HEADER_T), 1, fp); if (wave_header.sub_size_1 > sizeof (WAVE_HEADER_T) -12) fseek(fp, wave_header.sub_size_1 - (sizeof (WAVE_HEADER_T) - 12), SEEK_CUR); fread((void *)&data_header, sizeof(DATA_HEADER_T), 1, fp); pConfig->ch = wave_header.ch; pConfig->rate= wave_header.sample_rate; pConfig->bit= wave_header.bits_per_sample; pConfig->format = get_format(wave_header.bits_per_sample); fclose(fp); return 0; } static int set_hwparams(snd_pcm_t *handle) { snd_pcm_hw_params_t *params; unsigned int rrate; snd_pcm_uframes_t size; int err, dir; snd_pcm_hw_params_alloca(¶ms); /* choose all parameters */ err = snd_pcm_hw_params_any(handle, params); if (err < 0) { printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); return err; } /* set the interleaved read/write format */ err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { printf("Access type not available for playback: %s\n", snd_strerror(err)); return err; } /* set the sample format */ err = snd_pcm_hw_params_set_format(handle, params, config.format); if (err < 0) { printf("Sample format not available for playback: %s\n", snd_strerror(err)); return err; } /* set the count of channels */ err = snd_pcm_hw_params_set_channels(handle, params, config.ch); if (err < 0) { printf("Channels count (%u) not available for playbacks: %s\n", config.ch, snd_strerror(err)); return err; } /* set the stream rate */ rrate = config.rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); if (err < 0) { printf("Rate %uHz not available for playback: %s\n", config.rate, snd_strerror(err)); return err; } if (rrate != config.rate) { printf("Rate doesn't match (requested %uHz, get %iHz)\n", config.rate, err); return -EINVAL; } /* set the buffer time */ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &config.buffer_time, &dir); if (err < 0) { printf("Unable to set buffer time %u for playback: %s\n", config.buffer_time, snd_strerror(err)); return err; } err = snd_pcm_hw_params_get_buffer_size(params, &size); if (err < 0) { printf("Unable to get buffer size for playback: %s\n", snd_strerror(err)); return err; } config.buffer = size; /* set the period time */ err = snd_pcm_hw_params_set_period_time_near(handle, params, &config.period_time, &dir); if (err < 0) { printf("Unable to set period time %u for playback: %s\n", config.period_time, snd_strerror(err)); return err; } err = snd_pcm_hw_params_get_period_size(params, &size, &dir); if (err < 0) { printf("Unable to get period size for playback: %s\n", snd_strerror(err)); return err; } config.period = size; /* write the parameters to device */ err = snd_pcm_hw_params(handle, params); if (err < 0) { printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); return err; } return 0; } static int set_swparams(snd_pcm_t *handle) { int err; snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); /* get the current swparams */ err = snd_pcm_sw_params_current(handle, swparams); if (err < 0) { printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); return err; } /* start the transfer when the buffer is almost full: */ /* (buffer_size / avail_min) * avail_min */ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (config.buffer / config.period) * config.period); if (err < 0) { printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); return err; } printf(stderr, "---%d--\r\n", (config.buffer / config.period) * config.period); err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, (config.buffer / config.period) * config.period); /* write the parameters to the playback device */ err = snd_pcm_sw_params(handle, swparams); if (err < 0) { printf("Unable to set sw params for playback: %s\n", snd_strerror(err)); return err; } return 0; } /* * Underrun and suspend recovery */ static int xrun_recovery(snd_pcm_t *handle, int err) { printf("stream recovery\n"); if (err == -EPIPE) { /* under-run */ printf("stream recovery----%d\n", __LINE__); //err = snd_pcm_prepare(handle); if (err < 0) printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); return 0; } else if (err == -ESTRPIPE) { while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1); /* wait until the suspend flag is released */ if (err < 0) { printf("stream recovery----%d\n", __LINE__); err = snd_pcm_prepare(handle); if (err < 0) printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); } return 0; } return err; } static int write_loop(snd_pcm_t *handle) { int err; size_t remain, read_size; FILE *fp = NULL; char *read_buffer = NULL, *offset = NULL; int bytes_per_frame = config.ch * (config.bit >> 3); read_size = config.period * bytes_per_frame; fp = fopen(config.file_name, "rb"); if (NULL == fp) { return -1; } read_buffer = (char * )malloc(read_size); fseek(fp, 44, SEEK_CUR); while (1) { memset(read_buffer ,0, read_size); err = fread(read_buffer ,1, read_size, fp); if (err <= 0) break; offset = read_buffer; remain = err / bytes_per_frame; while (remain> 0) { usleep(300000); err = snd_pcm_writei(handle, offset, remain); if (err == -EAGAIN) continue; #if 1 if (err < 0) { if (xrun_recovery(handle, err) < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } continue; } #endif offset += err * bytes_per_frame; remain -= err; } } free(read_buffer); fclose(fp); return 0; } int main(int argc, char **argv) { int err = 0; snd_pcm_t *handle; memset (&config, 0, sizeof(CONFIG_T)); config.buffer_time = 900000;//us config.period_time = 500000; parse_cmd(argc, argv); config_init(&config); if ((err = snd_pcm_open(&handle, config.device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { printf("Playback open error: %s\n", snd_strerror(err)); return 0; } if ((err = set_hwparams(handle)) < 0) { printf("Setting of hwparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if ((err = set_swparams(handle)) < 0) { printf("Setting of swparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } write_loop(handle); snd_pcm_close(handle); return 0; }