接入某些USB摄像头后, 扬声器没有声音
2017-01-01 21:19:24.770 228-287/? E/AudioHardwareTiny: pcm_open(PCM_CARD_HDMI) failed: 222 cannot open device '/dev/snd/pcmC0D1p': No such file or directory
USB摄像头包含有MIC输入, 在启动初始化后, 会直接抢占card0, 而默认的声卡的初始化比这个慢, 导致card0中, 只有一个有效的输入, 而没有输出;
主要的原因还是, 在HAL中, 并没有对无效的输出作处理, 有的, 只有上面一个错误的LOG.
方案一
修改USB上电顺序, 延迟初始化到主板声卡后
方案二
修改HAL, 在打开card0失败后, 尝试打开主板声卡, 可能是card1, card2…
参考:
diff --git a/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c b/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
index 53069ab..266fcac 100755
--- a/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
+++ b/hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
@@ -660,33 +660,54 @@ static int start_output_stream(struct stream_out *out)
}
}
+ ALOGD("start_output_stream :666 out->device=%x", out->device);
if (out->device & (AUDIO_DEVICE_OUT_SPEAKER |
AUDIO_DEVICE_OUT_WIRED_HEADSET |
AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
AUDIO_DEVICE_OUT_ALL_SCO)) {
+ int card = PCM_CARD_HDMI;
+ bool success = true;
/* open & close hdmi card to mute hdmi audio */
- out->pcm_device = 1;
- out->pcm[PCM_CARD_HDMI] = pcm_open(PCM_CARD_HDMI, out->pcm_device,
+ out->pcm_device = 1;
+ ALOGD("start_output_stream pcm_open(%d, %d)", card, out->pcm_device);
+ out->pcm[0] = pcm_open(card, out->pcm_device,
PCM_OUT | PCM_MONOTONIC, &out->config);
- if (out->pcm[PCM_CARD_HDMI] &&
- !pcm_is_ready(out->pcm[PCM_CARD_HDMI])) {
- ALOGE("pcm_open(PCM_CARD_HDMI) failed: 222%s",
- pcm_get_error(out->pcm[PCM_CARD_HDMI]));
- pcm_close(out->pcm[PCM_CARD_HDMI]);
- return -ENOMEM;
+ if (out->pcm[0] &&
+ !pcm_is_ready(out->pcm[0])) {
+ ALOGE("pcm_open(PCM_CARD_HDMI) failed: 222 %s",
+ pcm_get_error(out->pcm[0]));
+ pcm_close(out->pcm[0]);
+ success = false;
+ //return -ENOMEM;
}
- if (out->pcm[PCM_CARD_HDMI])
- pcm_close(out->pcm[PCM_CARD_HDMI]);
- out->pcm_device = 0;
- out->pcm[PCM_CARD] = pcm_open(PCM_CARD, out->pcm_device,
+
+ if(!success){
+ //CARD 1
+ card = PCM_CARD_BOARD;
+ out->pcm[0] = pcm_open(card, out->pcm_device,
+ PCM_OUT | PCM_MONOTONIC, &out->config);
+ if (out->pcm[0] &&
+ !pcm_is_ready(out->pcm[0])) {
+ ALOGE("pcm_open(%d) failed: 222 %s",
+ card, pcm_get_error(out->pcm[0]));
+ pcm_close(out->pcm[0]);
+ return -ENOMEM;
+ }
+ }
+
+ if (out->pcm[0])
+ pcm_close(out->pcm[0]);
+
+ out->pcm_device = 0;
+ ALOGD("start_output_stream pcm_open(%d, %d) b", card, out->pcm_device);
+ out->pcm[0] = pcm_open(card, out->pcm_device,
PCM_OUT | PCM_MONOTONIC, &out->config);
- if (out->pcm[PCM_CARD] && !pcm_is_ready(out->pcm[PCM_CARD])) {
- ALOGE("pcm_open(PCM_CARD) failed: %s",
- pcm_get_error(out->pcm[PCM_CARD]));
- pcm_close(out->pcm[PCM_CARD]);
- return -ENOMEM;
+ if (out->pcm[0] && !pcm_is_ready(out->pcm[0])) {
+ ALOGE("pcm_open(PCM_CARD) failed: %s",
+ pcm_get_error(out->pcm[0]));
+ pcm_close(out->pcm[0]);
+ return -ENOMEM;
}
-
}
if (out->device & AUDIO_DEVICE_OUT_SPDIF) {
@@ -707,8 +728,10 @@ static int start_output_stream(struct stream_out *out)
adev->out_device |= out->device;
if (out->device & AUDIO_DEVICE_OUT_ALL_SCO) {
+ ALOGD("start_output_stream ALL_SCO");
start_bt_sco(adev);
#ifdef BT_AP_SCO // HARD CODE FIXME
+ ALOGD("start_output_stream BT_AP_SCO");
out->pcm[PCM_BT] = pcm_open(PCM_BT, 0,
PCM_OUT | PCM_MONOTONIC, &pcm_config_ap_sco);
ret = create_resampler(48000,
@@ -1545,6 +1568,7 @@ static void set_data_slice(void *in_data,struct stream_out *out,size_t length)
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
size_t bytes)
{
+ ALOGD("out_write");
int ret = 0;
struct stream_out *out = (struct stream_out *)stream;
struct audio_device *adev = out->dev;
diff --git a/hardware/rockchip/audio/tinyalsa_hal/audio_hw.h b/hardware/rockchip/audio/tinyalsa_hal/audio_hw.h
index aebcd0a..f579c10 100755
--- a/hardware/rockchip/audio/tinyalsa_hal/audio_hw.h
+++ b/hardware/rockchip/audio/tinyalsa_hal/audio_hw.h
@@ -83,6 +83,7 @@ int PCM_CARD_SPDIF = 1;
#else
int PCM_CARD = 0;
int PCM_CARD_HDMI = 0;
+int PCM_CARD_BOARD = 1;
int PCM_CARD_SPDIF = 2;
#endif
int PCM_BT = 3;
//声卡
rk3288:/ # ll proc/asound/
total 0
lrwxrwxrwx 1 root root 5 2017-01-02 00:52 AMCAP -> card0
dr-xr-xr-x 3 root root 0 2017-01-02 00:52 card0
dr-xr-xr-x 5 root root 0 2017-01-02 00:52 card1
-r--r--r-- 1 root root 0 2017-01-02 00:52 cards
-r--r--r-- 1 root root 0 2017-01-02 00:52 devices
-r--r--r-- 1 root root 0 2017-01-02 00:52 hwdep
-r--r--r-- 1 root root 0 2017-01-02 00:52 pcm
lrwxrwxrwx 1 root root 5 2017-01-02 00:52 rockchipes8316c -> card1
-r--r--r-- 1 root root 0 2017-01-02 00:52 timers
-r--r--r-- 1 root root 0 2017-01-02 00:52 version
//声卡信息, 0为USB摄像头
rk3288:/ # cat /proc/asound/cards
0 [AMCAP ]: USB-Audio - USB 2.0 AMCAP
Sonix Technology Co., Ltd. USB 2.0 AMCAP at usb-ff540000.usb-1.2, high speed
1 [rockchipes8316c]: rockchip_es8316 - rockchip,es8316-codec
rockchip,es8316-codec
//声卡设备
rk3288:/ # cat /proc/asound/devices
2: [ 0] : control
3: [ 0- 0]: digital audio capture
4: [ 1] : control
5: [ 1- 0]: digital audio playback
6: [ 1- 0]: digital audio capture
7: [ 1- 1]: digital audio playback
33: : timer
//设备节点
rk3288:/dev/snd # ll
total 0
crw-rw---- 1 system audio 116, 2 2017-01-02 00:26 controlC0
crw-rw---- 1 system audio 116, 4 2017-01-02 00:26 controlC1
crw-rw---- 1 system audio 116, 3 2017-01-02 00:26 pcmC0D0c
crw-rw---- 1 system audio 116, 6 2017-01-02 00:26 pcmC1D0c
crw-rw---- 1 system audio 116, 5 2017-01-02 00:26 pcmC1D0p
crw-rw---- 1 system audio 116, 7 2017-01-02 00:26 pcmC1D1p
crw-rw---- 1 system audio 116, 33 2017-01-02 00:26 timer
//输出PCM信息
rk3288:/ # tinypcminfo -D 0
Info for card 0, device 0:
PCM out:
cannot open device '/dev/snd/pcmC0D0p'
Device does not exist.
PCM in:
Access: 0x000009
Format[0]: 0x000004
Format[1]: 00000000
Format Name: S16_LE
Subformat: 0x000001
Rate: min=8000Hz max=48000Hz
Channels: min=2 max=2
Sample bits: min=16 max=16
Period size: min=16 max=131072
Period count: min=2 max=1024
rk3288:/ # tinypcminfo -D 1
Info for card 1, device 0:
PCM out:
Access: 0x000009
Format[0]: 0x000044
Format[1]: 00000000
Format Name: S16_LE, S24_LE
Subformat: 0x000001
Rate: min=8000Hz max=96000Hz
Channels: min=2 max=2
Sample bits: min=16 max=32
Period size: min=32 max=65536
Period count: min=2 max=4096
PCM in:
Access: 0x000009
Format[0]: 0x000044
Format[1]: 00000000
Format Name: S16_LE, S24_LE
Subformat: 0x000001
Rate: min=8000Hz max=96000Hz
Channels: min=2 max=2
Sample bits: min=16 max=32
Period size: min=32 max=65536
Period count: min=2 max=4096
rk3288_n712$ ll hardware/rockchip/audio/tinyalsa_hal/
alsa_audio.h
alsa_mixer.c
alsa_route.c
amix.c
Android.mk
asound.h
audio_bitstream.c
audio_bitstream.h
audio_hw.c
audio_hw.h
audio_hw_hdmi.c
audio_hw_hdmi.h
audio_setting.c
audio_setting.h
codec_config/
voice_preprocess.c
voice_preprocess.h
|-- hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
static int start_output_stream(struct stream_out *out)
{
ALOGD("start_output_stream");
char value[PROPERTY_VALUE_MAX] = "";
struct audio_device *adev = out->dev;
int type;
bool connect_hdmi = true;
int ret = 0;
ALOGD("%s",__FUNCTION__);
if (out == adev->outputs[OUTPUT_HDMI_MULTI]) {
force_non_hdmi_out_standby(adev);
} else if (adev->outputs[OUTPUT_HDMI_MULTI] && !adev->outputs[OUTPUT_HDMI_MULTI]->standby) {
out->disabled = true;
return 0;
}
out->disabled = false;
read_hdmi_audioinfo();
int device = getOutputDevice();
ALOGD("start_output_stream device=%x", device);
ALOGD("start_output_stream out->device=%x", out->device);
if (device == SPDIF_PASSTHROUGH_MODE) {
out->device &= ~AUDIO_DEVICE_OUT_AUX_DIGITAL;
out->device |= AUDIO_DEVICE_OUT_SPDIF;
} else if (device == HDMI_BITSTREAM_MODE) {
out->device &= ~AUDIO_DEVICE_OUT_SPDIF;
out->device |= AUDIO_DEVICE_OUT_AUX_DIGITAL;
}
#ifdef BOX_HAL
if (out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
/*BOX hdmi & codec use the same i2s,so only config the codec card*/
out->device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}
read_snd_card_info();
if (out->config.flag == HW_PARAMS_FLAG_LPCM){
if(hasSpdif() && ((out->device & AUDIO_DEVICE_OUT_SPDIF)==0)){
out->device |= AUDIO_DEVICE_OUT_SPDIF;
}
}
#ifdef RK3228
if (out->config.flag == HW_PARAMS_FLAG_LPCM) {
if (out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
out->device |= AUDIO_DEVICE_OUT_SPEAKER;
}
}
#endif
out_dump(out, 0);
#endif
connect_hdmi = true;
route_pcm_open(getRouteFromDevice(out->device));
if (out->device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
if (connect_hdmi) {
#ifdef BOX_HAL
#ifdef USE_DRM
int ret = 0;
ret = mixer_mode_set(out);
if (ret!=0) {
ALOGE("mixer mode set error,ret=%d!",ret);
}
#endif
#endif
out->pcm_device = 1;
out->pcm[PCM_CARD_HDMI] = pcm_open(PCM_CARD_HDMI, out->pcm_device,
PCM_OUT | PCM_MONOTONIC, &out->config);
if (out->pcm[PCM_CARD_HDMI] &&
!pcm_is_ready(out->pcm[PCM_CARD_HDMI])) {
ALOGE("pcm_open(PCM_CARD_HDMI) failed:111 %s",
pcm_get_error(out->pcm[PCM_CARD_HDMI]));
pcm_close(out->pcm[PCM_CARD_HDMI]);
return -ENOMEM;
}
} else {
ALOGD("The current HDMI is DVI mode");
out->device |= AUDIO_DEVICE_OUT_SPEAKER;
}
}
if (out->device & (AUDIO_DEVICE_OUT_SPEAKER |
AUDIO_DEVICE_OUT_WIRED_HEADSET |
AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
AUDIO_DEVICE_OUT_ALL_SCO)) {
/* open & close hdmi card to mute hdmi audio */
out->pcm_device = 1;
out->pcm[PCM_CARD_HDMI] = pcm_open(PCM_CARD_HDMI, out->pcm_device,
PCM_OUT | PCM_MONOTONIC, &out->config);
if (out->pcm[PCM_CARD_HDMI] &&
!pcm_is_ready(out->pcm[PCM_CARD_HDMI])) {
ALOGE("pcm_open(PCM_CARD_HDMI) failed: 222 %s",
pcm_get_error(out->pcm[PCM_CARD_HDMI]));
pcm_close(out->pcm[PCM_CARD_HDMI]);
return -ENOMEM;
}
if (out->pcm[PCM_CARD_HDMI])
pcm_close(out->pcm[PCM_CARD_HDMI]);
out->pcm_device = 0;
out->pcm[PCM_CARD] = pcm_open(PCM_CARD, out->pcm_device,
PCM_OUT | PCM_MONOTONIC, &out->config);
if (out->pcm[PCM_CARD] && !pcm_is_ready(out->pcm[PCM_CARD])) {
ALOGE("pcm_open(PCM_CARD) failed: %s",
pcm_get_error(out->pcm[PCM_CARD]));
pcm_close(out->pcm[PCM_CARD]);
return -ENOMEM;
}
}
if (out->device & AUDIO_DEVICE_OUT_SPDIF) {
out->pcm_device = 0;
out->pcm[PCM_CARD_SPDIF] = pcm_open(PCM_CARD_SPDIF, out->pcm_device,
PCM_OUT | PCM_MONOTONIC, &out->config);
if (out->pcm[PCM_CARD_SPDIF] &&
!pcm_is_ready(out->pcm[PCM_CARD_SPDIF])) {
ALOGE("pcm_open(PCM_CARD_SPDIF) failed: %s",
pcm_get_error(out->pcm[PCM_CARD_SPDIF]));
pcm_close(out->pcm[PCM_CARD_SPDIF]);
return -ENOMEM;
}
}
adev->out_device |= out->device;
if (out->device & AUDIO_DEVICE_OUT_ALL_SCO) {
start_bt_sco(adev);
#ifdef BT_AP_SCO // HARD CODE FIXME
out->pcm[PCM_BT] = pcm_open(PCM_BT, 0,
PCM_OUT | PCM_MONOTONIC, &pcm_config_ap_sco);
ret = create_resampler(48000,
8000,
2,
RESAMPLER_QUALITY_DEFAULT,
NULL,
&out->resampler);
if (ret != 0) {
ret = -EINVAL;
}
#endif
}
if(adev->hdmiin_state){
ALOGD("%s HDMIin state open hdmiin route",__FUNCTION__);
route_pcm_open(HDMI_IN_NORMAL_ROUTE);
}
return 0;
}
|-- external/tinyalsa/pcm.c(PCM结构体)
struct pcm {
int fd;
unsigned int flags;
int running:1;
int prepared:1;
int underruns;
unsigned int buffer_size;
unsigned int boundary;
char error[PCM_ERROR_MAX];
struct pcm_config config;
struct snd_pcm_mmap_status *mmap_status;
struct snd_pcm_mmap_control *mmap_control;
struct snd_pcm_sync_ptr *sync_ptr;
void *mmap_buffer;
unsigned int noirq_frames_per_msec;
int wait_for_avail_min;
};
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config)
{
struct pcm *pcm;
struct snd_pcm_info info;
struct snd_pcm_hw_params params;
struct snd_pcm_sw_params sparams;
char fn[256];
int rc;
pcm = calloc(1, sizeof(struct pcm));
if (!pcm || !config)
return &bad_pcm; /* TODO: could support default config here */
pcm->config = *config;
snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
flags & PCM_IN ? 'c' : 'p');
pcm->flags = flags;
pcm->fd = open(fn, O_RDWR|O_NONBLOCK);
if (pcm->fd < 0) {
oops(pcm, errno, "cannot open device '%s'", fn);
return pcm;
}
if (fcntl(pcm->fd, F_SETFL, fcntl(pcm->fd, F_GETFL) &
~O_NONBLOCK) < 0) {
oops(pcm, errno, "failed to reset blocking mode '%s'", fn);
goto fail_close;
}
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
oops(pcm, errno, "cannot get info");
goto fail_close;
}
param_init(¶ms);
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT,
pcm_format_to_alsa(config->format));
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT,
SNDRV_PCM_SUBFORMAT_STD);
param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
pcm_format_to_bits(config->format));
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS,
pcm_format_to_bits(config->format) * config->channels);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS,
config->channels);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate);
param_set_flag(¶ms, config->flag);
if (flags & PCM_NOIRQ) {
if (!(flags & PCM_MMAP)) {
oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
goto fail_close;
}
params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
pcm->noirq_frames_per_msec = config->rate / 1000;
}
if (flags & PCM_MMAP)
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
else
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
SNDRV_PCM_ACCESS_RW_INTERLEAVED);
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
oops(pcm, errno, "cannot set hw params");
goto fail_close;
}
/* get our refined hw_params */
config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS);
pcm->buffer_size = config->period_count * config->period_size;
if (flags & PCM_MMAP) {
pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
if (pcm->mmap_buffer == MAP_FAILED) {
oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
pcm_frames_to_bytes(pcm, pcm->buffer_size));
goto fail_close;
}
}
memset(&sparams, 0, sizeof(sparams));
sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
sparams.period_step = 1;
if (!config->start_threshold) {
if (pcm->flags & PCM_IN)
pcm->config.start_threshold = sparams.start_threshold = 1;
else
pcm->config.start_threshold = sparams.start_threshold =
config->period_count * config->period_size / 2;
} else
sparams.start_threshold = config->start_threshold;
/* pick a high stop threshold - todo: does this need further tuning */
if (!config->stop_threshold) {
if (pcm->flags & PCM_IN)
pcm->config.stop_threshold = sparams.stop_threshold =
config->period_count * config->period_size * 10;
else
pcm->config.stop_threshold = sparams.stop_threshold =
config->period_count * config->period_size;
}
else
sparams.stop_threshold = config->stop_threshold;
if (!pcm->config.avail_min) {
if (pcm->flags & PCM_MMAP)
pcm->config.avail_min = sparams.avail_min = pcm->config.period_size;
else
pcm->config.avail_min = sparams.avail_min = 1;
} else
sparams.avail_min = config->avail_min;
sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
sparams.silence_threshold = config->silence_threshold;
sparams.silence_size = config->silence_size;
pcm->boundary = sparams.boundary = pcm->buffer_size;
while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
pcm->boundary *= 2;
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
oops(pcm, errno, "cannot set sw params");
goto fail;
}
rc = pcm_hw_mmap_status(pcm);
if (rc < 0) {
oops(pcm, rc, "mmap status failed");
goto fail;
}
#ifdef SNDRV_PCM_IOCTL_TTSTAMP
if (pcm->flags & PCM_MONOTONIC) {
int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
if (rc < 0) {
oops(pcm, rc, "cannot set timestamp type");
goto fail;
}
}
#endif
pcm->underruns = 0;
return pcm;
fail:
if (flags & PCM_MMAP)
munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
fail_close:
close(pcm->fd);
pcm->fd = -1;
return pcm;
}
int pcm_is_ready(struct pcm *pcm)
{
return pcm->fd >= 0;
}
完整补丁下载