android 采集PCM音频数据并播放(支持USB摄像头MIC)

简介: android 采集PCM音频数据并播放(支持USB摄像头MIC)

平台


RK3288 + Android 7.1


开发


在开始之前需要先了解下当前的音频设备情况:


查看当前支持的声卡设备(找到card[0, N])

rk3288:/proc/asound # ll                                                       
total 0
lrwxrwxrwx 1 root root 5 2020-04-15 13:51 Camera -> card1
dr-xr-xr-x 5 root root 0 2020-04-15 13:51 card0
dr-xr-xr-x 3 root root 0 2020-04-15 13:51 card1
-r--r--r-- 1 root root 0 2020-04-15 13:51 cards
-r--r--r-- 1 root root 0 2020-04-15 13:51 devices
-r--r--r-- 1 root root 0 2020-04-15 13:51 hwdep
-r--r--r-- 1 root root 0 2020-04-15 13:51 pcm
lrwxrwxrwx 1 root root 5 2020-04-15 13:51 rockchipes8316c -> card0
-r--r--r-- 1 root root 0 2020-04-15 13:51 timers
-r--r--r-- 1 root root 0 2020-04-15 13:51 version


查看cards中的内容:

rk3288:/proc/asound # cat cards                                                
 0 [rockchipes8316c]: rockchip_es8316 - rockchip,es8316-codec
                      rockchip,es8316-codec
 1 [Camera         ]: USB-Audio - USB Camera
                      Generic USB Camera at usb-ff540000.usb-1.2, high speed


当前使用的声卡信息

rk3288:/proc/asound # tinypcminfo -D 0                                         
Info for card 0, 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


采集API:AudioRecord

//audioSource: MediaRecorder.AudioSource.CAMCORDER, MediaRecorder.AudioSource.MIC 等
    //    一般情况下会用到这两个
    //sampleRateInHz: 常用的有: 8000,11025,16000,22050,44100,96000;
    //channelConfig: AudioFormat.CHANNEL_CONFIGURATION_DEFAULT, 单声道和立声
    //audioFormat: AudioFormat.ENCODING_PCM_16BIT, 测试中要捕获PCM数据, 其它参数未研究.
    //bufferSizeInBytes: 由AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)获得
    public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes)


播放API: AudioTrack

//streamType: AudioManager.STREAM_MUSIC, 输出通道, 也可以选择ALARM等
    //sampleRateInHz: 常用的有: 8000,11025,16000,22050,44100,96000;
    //channelConfig: AudioFormat.CHANNEL_CONFIGURATION_DEFAULT, 单声道和立声
    //audioFormat: AudioFormat.ENCODING_PCM_16BIT, 测试中要捕获PCM数据, 其它参数未研究.
    //bufferSizeInBytes: 
    //mode: AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。
    //  STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。
    //  这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
    //  这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
    //  而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,
    //  后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
    //  这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
    public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes, int mode)


边采集边播放PCM代码(仅核心代码, 不完整)

//Audio Parameter
    static class AudioParam{
        static final int FREQ_8000 = 8000;
        static final int FREQ_11025 = 11025;
        static final int FREQ_16000 = 16000;
        static final int FREQ_22050 = 22050;
        static final int FREQ_44100 = 44100;
        static final int FREQ_96000 = 96000;
        int device;
        int channel;
        int bitFormat;
        int buffSize;
        //int doubleBuffSize;
        int buffSizeOut;
        int rate;
        public AudioParam(int device, int rate,  int channel, int bitFormat){
            this.device = device;
            this.rate = rate;
            this.channel = channel;
            this.bitFormat = bitFormat;
            initBufferSize();
        }
        private void initBufferSize() {
            buffSize = AudioRecord.getMinBufferSize(rate, channel, bitFormat);
            buffSizeOut = AudioTrack.getMinBufferSize(rate, channel, bitFormat);
            Logger.d("AudioPCM", "buffSize(" + buffSize + "), buffSizeOut(" + buffSizeOut + ")");
            if(buffSizeOut < 0)buffSizeOut = buffSize;
            //doubleBuffSize = buffSize * 2;
        }
        static AudioParam getDefaultInParam(){
            return new AudioParam(MediaRecorder.AudioSource.MIC,
                    FREQ_44100,
                    AudioFormat.CHANNEL_CONFIGURATION_DEFAULT,
                    AudioFormat.ENCODING_PCM_16BIT);
        }
        static AudioParam getDefaultOutParam(){
            return new AudioParam(AudioManager.STREAM_MUSIC,
                    FREQ_44100,
                    AudioFormat.CHANNEL_CONFIGURATION_DEFAULT,
                    AudioFormat.ENCODING_PCM_16BIT);
        }
    }
    //Thread for capture audio
    class CaptureThread extends Thread{
        @Override
        public void run() {
            AudioRecord mic = new AudioRecord(audioParamIn.device, audioParamIn.rate, audioParamIn.channel,
                    audioParamIn.bitFormat, audioParamIn.buffSize);
            mic.startRecording();
            byte[] pcmBuffer = new byte[2048];
            while (!Thread.interrupted()) {
                int size = mic.read(pcmBuffer, 0, pcmBuffer.length);
                Logger.d(TAG, "read " + size + " bytes");
                if (size <= 0) {
                    break;
                }else{
                    if(playThd != null){
                        playThd.write(pcmBuffer, size);
                    }
                }
            }
            mic.stop();
            mic.release();
        }
    }
    //Thread for play
    class PlayThread{
        private AudioTrack mAudioTrack;
        PlayThread(){
            try {
                audioParamOut = new AudioParam(AudioManager.STREAM_MUSIC,
                        AudioParam.FREQ_44100,
                        AudioFormat.CHANNEL_CONFIGURATION_DEFAULT,
                        AudioFormat.ENCODING_PCM_16BIT);
                createAudioTrack();
                mAudioTrack.play();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        void write(byte[] bs, int size){
            Logger.d(TAG, "write " + size + " bytes");
            if(mAudioTrack != null){
                mAudioTrack.write(bs, 0, size);
            }
        }
        void stop(){
            if(mAudioTrack != null){
                mAudioTrack.stop();
            }
        }
        private void createAudioTrack() throws Exception{
//           STREAM_ALARM:警告声
//           STREAM_MUSCI:音乐声,例如music等
//           STREAM_RING:铃声
//           STREAM_SYSTEM:系统声音
//           STREAM_VOCIE_CALL:电话声音
            mAudioTrack = new AudioTrack(audioParamOut.device,
                    audioParamOut.rate,
                    audioParamOut.channel,
                    audioParamOut.bitFormat,
                    audioParamOut.buffSizeOut,
                    AudioTrack.MODE_STREAM);
        }
    }


扩展


需要确定能正常的识别到USB 摄像头的MIC输入设备(这个过程走了许多弯路)

在测试过程中发现 MediaRecorder.AudioSource.CAMCORDER, MediaRecorder.AudioSource.MIC对应的设备并不是固定的

当未接USB摄像头的时候 MediaRecorder.AudioSource.MIC 对应的是 主板的MIC

当接上USB摄像头后, MediaRecorder.AudioSource.MIC 对应的是USB摄像头上的MIC.


参考


关于USB-Audio(USB麦克风)设备的录音验证

Android usb audio录音

Android下音频的测试程序tinyalsa(录音,放音,查看声卡信息)

Android语音采集


相关文章
|
3月前
|
开发工具 Android开发 开发者
Android平台如何不推RTMP|不发布RTSP流|不实时录像|不回传GB28181数据时实时快照?
本文介绍了一种在Android平台上实现实时截图快照的方法,尤其适用于无需依赖系统接口的情况,如在RTMP推送、RTSP服务或GB28181设备接入等场景下进行截图。通过底层模块(libSmartPublisher.so)实现了截图功能,封装了`SnapShotImpl.java`类来管理截图流程。此外,提供了关键代码片段展示初始化SDK实例、执行截图、以及在Activity销毁时释放资源的过程。此方案还考虑到了快照数据的灵活处理需求,符合GB/T28181-2022的技术规范。对于寻求更灵活快照机制的开发者来说,这是一个值得参考的设计思路。
|
1月前
|
存储 大数据 数据库
Android经典面试题之Intent传递数据大小为什么限制是1M?
在 Android 中,使用 Intent 传递数据时存在约 1MB 的大小限制,这是由于 Binder 机制的事务缓冲区限制、Intent 的设计初衷以及内存消耗和性能问题所致。推荐使用文件存储、SharedPreferences、数据库存储或 ContentProvider 等方式传递大数据。
74 0
|
2月前
|
安全 API 开发工具
Android平台RTMP推送|轻量级RTSP服务如何实现麦克风|扬声器声音采集切换
Android平台扬声器播放声音的采集,在无纸化同屏等场景下,意义很大,早期低版本的Android设备,是没法直接采集扬声器audio的(从Android 10开始支持),所以,如果需要采集扬声器audio,需要先做系统版本判断,添加相应的权限。
|
2月前
|
编解码 开发工具 Android开发
Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
Android平台屏幕采集、音频播放声音采集、麦克风采集编码打包推送到RTMP和轻量级RTSP服务的相关技术实现,做成高稳定低延迟的同屏系统,还需要有配套好的RTMP、RTSP直播播放器
|
3月前
|
Android开发
Android 利用MediaPlayer实现音乐播放
本文提供了一个简单的Android MediaPlayer音乐播放示例,包括创建PlayerActivity、配置AndroidManifest.xml和activity_player.xml布局,以及实现播放和暂停功能的代码。
27 0
Android 利用MediaPlayer实现音乐播放
|
3月前
|
JSON Java Android开发
Android 开发者必备秘籍:轻松攻克 JSON 格式数据解析难题,让你的应用更出色!
【8月更文挑战第18天】在Android开发中,解析JSON数据至关重要。JSON以其简洁和易读成为首选的数据交换格式。开发者可通过多种途径解析JSON,如使用内置的`JSONObject`和`JSONArray`类直接操作数据,或借助Google提供的Gson库将JSON自动映射为Java对象。无论哪种方法,正确解析JSON都是实现高效应用的关键,能帮助开发者处理网络请求返回的数据,并将其展示给用户,从而提升应用的功能性和用户体验。
95 1
|
3月前
|
编解码 网络协议 开发工具
Android平台如何实现多路低延迟RTSP|RTMP播放?
本文档详细介绍了大牛直播SDK在Android平台上实现RTSP与RTMP流媒体播放及录像功能的技术细节。早在2015年,SDK的第一版就已经支持了多实例播放,并且通过简单的实例封装就能轻松实现。文档中提供了代码示例,展示了如何开启播放、停止播放以及开始和停止录像等功能。此外,SDK还提供了丰富的配置选项,例如设置录像目录、文件大小限制、转码选项等。总结部分列出了该SDK的关键特性,包括但不限于高稳定性和低延迟的播放能力、多实例支持、事件回调、硬解码支持、网络状态监控以及复杂的网络环境处理等。这些功能使得SDK能够应对各种应用场景,特别是在对延迟和稳定性有极高要求的情况下表现优异。
|
3月前
|
编解码 网络协议 vr&ar
Android平台下VR头显如何低延迟播放4K以上超高分辨率RTSP|RTMP流
这段内容讲述了VR头显中实现高分辨率视频播放的技术背景与实现方法,并强调了其重要性。高分辨率对于提升VR体验至关重要,它能提供更清晰的画面、增强沉浸感、补偿透镜放大效应,并维持宽广视场角下的图像质量。文中提到的大牛直播SDK具备极低的延迟(200-400ms),支持多种协议与格式,并具有丰富的功能特性,如多实例播放、事件回调、视频及音频格式支持等。此外,提供了基于Unity的播放器示例代码,展示了如何配置播放参数并开始播放。最后,作者指出此类技术在远程控制、虚拟仿真等应用场景中的重要意义。
|
3月前
|
存储 缓存 Java
Android项目架构设计问题之优化业务接口数据的加载效率如何解决
Android项目架构设计问题之优化业务接口数据的加载效率如何解决
46 0
|
3月前
|
传感器 API Android开发
Android摄像头采集选Camera1还是Camera2?
Camera1与Camera2是Android平台上的两种摄像头API。Camera1(API1)在Android 5.0后被标记为过时,新项目应优先选用Camera2(API2)。Camera2提供了更精细的控制选项,如曝光时间、ISO感光度等;支持多摄像头管理;采用异步操作提高应用响应速度;并支持RAW图像捕获及实时图像处理。此外,它还具备更好的适配性和扩展性,适用于各类应用场景,如相机应用开发、视频通话和计算机视觉等。因此,在现代Android开发中推荐使用Camera2。