RK3288 Android7.1 接USB摄像头后扬声器无声音

问题

接入某些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(&params);
    param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
                   pcm_format_to_alsa(config->format));
    param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
                   SNDRV_PCM_SUBFORMAT_STD);
    param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
    param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
                  pcm_format_to_bits(config->format));
    param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
                  pcm_format_to_bits(config->format) * config->channels);
    param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
                  config->channels);
    param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
    param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
    param_set_flag(&params, 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(&params, SNDRV_PCM_HW_PARAM_ACCESS,
                       SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
    else
        param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
                       SNDRV_PCM_ACCESS_RW_INTERLEAVED);

    if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
        oops(pcm, errno, "cannot set hw params");
        goto fail_close;
    }

    /* get our refined hw_params */
    config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
    config->period_count = param_get_int(&params, 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;
}

资源

完整补丁下载

你可能感兴趣的:(android,PCM,USB摄像头,声卡,HAL)