全志 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;
}
浙公网安备 33010602011771号