两个USB设备各自是:
A:USB摄像头带录音功能,但不带放音功能。
B:USB无线耳机是使用USB转2.4G的无线耳机。

详细现象:
1, A,B两者同一时候插上机顶盒,并开机进入android,此时去播放音乐或电影,声音是从HDMI出来的,并不是从无线耳机出来。

此时又一次插拔一下2.4G无线耳机,声音就会从耳机中出来。

2, 机顶盒上电。进入android系统,然后播放音乐或电影,此时声音从HDMI中出来。这个时候接上USB摄像头,声音还是从HDMI出来。

再接上无线耳机。这时候声音却还是从HDMI中出来,此时应该要从耳机出来。又一次插拔一下耳机就恢复正常了。

总结现象,基本能够得出一个结论:开机后。先插上USB摄像头再插上2.4G耳麦,声音并不会从耳机出来,仅仅有又一次插拔一次USB耳麦后才会正常。

从现象能够看出,出现异常的原因是音频系统没有从摄像头切换到麦克风。

细致查看日志,发现正常时会有一段以下的打印。不正常时并没有。
V/AudioService( 1582): Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = 1
I/AudioPolicyManagerBase( 1059): setDeviceConnectionState() device: 800, state 1, address 
I/AudioPolicyManagerBase( 1059): [setDeviceConnectionState : 245] device already connected: 16777216
V/WiredAccessoryObserver( 1582): Headset UEVENT: {SUBSYSTEM=switch, SWITCH_STATE=1, DEVPATH=/devices/virtual/switch/usb_audio, SEQNUM=1271, ACTION=change, SWITCH_NAME=usb_audio}
V/WiredAccessoryObserver( 1582): newState = 4, headsetState = 4,mHeadsetState = 0
V/WiredAccessoryObserver( 1582): Intent.ACTION_USB_HEADSET_PLUG: state: 1 name: usb_audio

从日志能够看出,AudioService收到了ACTION_USB_ANLG_HEADSET_PLUG广播消息。才干正常。在源代码中搜索发出ACTION_USB_ANLG_HEADSET_PLUG广播的地方,查得在WiredAccessoryObserver.java文件里sendIntent()函数调用了。

private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
        .......
                    intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    intent.putExtra("state", state);
                    intent.putExtra("name", headsetName);
                    ActivityManagerNative.broadcastStickyIntent(intent, null);
        .......
    }
此函数被sendIntents()调用,接着被mHandler的handleMessage()调用。
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
            mWakeLock.release();
        }
    };
接着被update()调用
    private synchronized final void update(String newName, int newState) {
        ........
        mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
                                                           mHeadsetState,
                                                           mPrevHeadsetState,
                                                           mHeadsetName),
                                    delay);
    }
接着被updateState()调用。然后被onUEvent()调用。

onUEvent()重载了UEventObserver.java中的相应函数。细致查看该文件源代码能够得知当中有个线程会读取netlink消息。并由相应的Observer去处理。

在run()函数中增加打印日志的函数。将uevent打印出来。

public void run() {
            native_setup();

byte[] buffer = new byte[1024];
            int len;
            while (true) {
                len = next_event(buffer);
                if (len > 0) {
                    String bufferStr = new String(buffer, 0, len);  // easier to search a String
                    Log.d (TAG,"uevent:"+bufferStr);
                    synchronized (mObservers) {
                        for (int i = 0; i < mObservers.size(); i += 2) {
                            if (bufferStr.indexOf((String)mObservers.get(i)) != -1) {
                                ((UEventObserver)mObservers.get(i+1))
                                        .onUEvent(new UEvent(bufferStr));
                            }
                        }
                    }
                }
            }
        }

改完后编译frameworks/base。将core.jar文件push到机顶盒进行測试。当正常时会有例如以下打印
D/UEventObserver( 1582): uevent:change@/devices/virtual/switch/usb_audio��ACTION=change��DEVPATH=/devices/virtual/switch/usb_audio��SUBSYSTEM=switch��SWITCH_NAME=usb_audio��SWITCH_STATE=1��SEQNUM=1271��
V/AudioService( 1582): Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = 1
I/AudioPolicyManagerBase( 1059): setDeviceConnectionState() device: 800, state 1, address 
I/AudioPolicyManagerBase( 1059): [setDeviceConnectionState : 245] device already connected: 16777216
V/WiredAccessoryObserver( 1582): Headset UEVENT: {SUBSYSTEM=switch, SWITCH_STATE=1, DEVPATH=/devices/virtual/switch/usb_audio, SEQNUM=1271, ACTION=change, SWITCH_NAME=usb_audio}
V/WiredAccessoryObserver( 1582): newState = 4, headsetState = 4,mHeadsetState = 0
V/WiredAccessoryObserver( 1582): Intent.ACTION_USB_HEADSET_PLUG: state: 1 name: usb_audio

从日志能够知道,不正常的原因在于kernel并没有发出uevent。google去查uevent机制,了解到change这个ACTION由KOBJ_CHANGE来控制。

依据uevent中SWITCH_NAME和SWITCH_STATE对kernel/下进行搜索,得到drivers/switch/switch_class.c文件。同一时候也依据KOBJ_CHANGE对drivers下进行搜索。也发现switch/switch_class.c文件,推測这个文件是关键。在switch_set_state()函数中增加打印,又一次编译内核,执行,查看日志,果然发现都调了这里。

void switch_set_state(struct switch_dev *sdev, int state)
{
    ......
    if (sdev->state != state) {
        sdev->state = state;
        ......
    }
    ......
}

继续搜索调用switch_set_state()的地方,发如今sound/usb/card.c的snd_usb_audio_probe()函数中调用了。

static void *snd_usb_audio_probe(struct usb_device *dev,
                 struct usb_interface *intf,
                 const struct usb_device_id *usb_id)
{
    ......
    //switch_set_state(&sdev, STATE_DISCONNECTED);
    switch_set_state(&sdev, STATE_CONNECTED);
    ......
}

能够知道多个usb音频设备通过switch进行管理。一个时刻仅仅使用一个。

当带录音功能的usb摄像头插上时。sdev的状态改为已连接。

当usb耳麦接上后,相同会调用switch_set_state(),但由于先前已经连了一个usb音频设备。sdev->state已经变为1,不再继续发消息。于是我凝视掉推断语句:if (sdev->state != state) 。再次进行測试,发现uevent已经上报了,但声音仍然没有从usb耳麦中出来。
继续回到Android层,查看WiredAccessoryObserver.java中update()函数
    private synchronized final void update(String newName, int newState) {
        ......
        if (LOG) Slog.v(TAG, "newState = "+newState+", headsetState = "+headsetState+","
            + "mHeadsetState = "+mHeadsetState);
        if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) {
            Log.e(TAG, "unsetting h2w flag");
            h2wStateChange = false;
        }
        // - c: 0 usb headset to 1 usb headset
        // - d: 1 usb headset to 0 usb headset
        if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) {
            Log.e(TAG, "unsetting usb flag");
            usbStateChange = false;
        }
        if (!h2wStateChange && !usbStateChange) {
            Log.e(TAG, "invalid transition, returning ...");
            return;
        }
        .......
}
分析代码可知,因为上报的消息跟上次一样,此函数并未继续运行。
为了简单起见,我想直接在内核载入新的usb音频设备时先将switch断开原来的。再连接新的。于是在snd_usb_audio_probe()函数中,先运行switch_set_state(&sdev, STATE_DISCONNECTED);欺骗系统switch已经断开。然后再运行switch_set_state(&sdev, STATE_CONNECTED);

编译,烧录,測试后发现一切都正常了。录音与放音都正常。

当USB耳麦连接时用耳麦进行录音和放音。

当USB耳麦不在时用USB摄像头进行录音,由HDMI进行放音。

对这个结果感到非常意外。意想之中的状况应该是始终使用后插上的那个设备。查看日志后得知,由摄像头没有放音功能,在open这个设备进行放音的时候会失败。于是就会使用下一个设备,直到找到能放音的设备,查看日志发现这是alsa-lib这么设计的。为何录音也总是优先使用USB耳麦,没有深究,应该也是alsa-lib完毕了。

海思hi3716c机顶盒接usb摄像头和usb无线耳机时,无线耳机有时没有声音相关推荐

  1. 2.7 usb摄像头之usb摄像头描述符打印

    学习目标:参考lsusb源码,打印USB摄像头的设备描述符.配置描述符.接口联合描述符.端点描述符: 一.lsusb命令和源码 使用命令lsusb可以看看设备的id,并执行 # lsusb -v -d ...

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

    问题 接入某些USB摄像头后, 扬声器没有声音 2017-01-01 21:19:24.770 228-287/? E/AudioHardwareTiny: pcm_open(PCM_CARD_HDM ...

  3. 海思Hi3798MV200机顶盒芯片处理器简介

    Hi3798MV200是用于IPTV/OTT机顶盒市场的支持4KP60解码的全4K高性能SOC芯片.集成4核64位高性能Cortex A53处理器和多核高性能2D/3D加速引擎:支持H.265 4Kx ...

  4. 【海思3798系列机顶盒NAS分区表修改软件】

    修改软件下载地址:海思3798系列机顶盒NAS分区表修改软件-makebootargs.exe-Linux文档类资源-CSDN下载可以修改海思机顶盒hi3798mv系列机顶盒分区文件生成,可以修改分区 ...

  5. 海思一颗料暴涨7倍,全球芯片缺货真相:原厂38颗MCU掌控汽车产业链

    origin: https://new.qq.com/omn/20210309/20210309A031G200.html 2020年一场前所未有的疫情灾难席卷全球,作为全球化分工最明确的半导体产业首 ...

  6. 如何用海思HI3516DV300/3518EV200推流H.264

    一开始尝试用hi3518EV200, 后来为了给自己加难度, 就买了个hi3516的板子, 看看差异多大, 结论是, 不大... 前期准备: 你得有一个3516或者3518的开发板(淘宝有卖各种开发板 ...

  7. 【原创】IP摄像头技术纵览(一)---linux 内核编译,USB摄像头设备识别

    IP摄像头技术纵览(一)- linux 内核编译,USB摄像头设备识别 开始正文之前先来认识一下我的开发环境: 系统:ubuntu 10.04 开发板:AT91SAM9260 + Linux-2.6. ...

  8. 1.gstreamer USB摄像头保存至图片及视频

    目录 1.操作系统版本 2.USB摄像头驱动 3.usb摄像头预览 4.采集USB摄像头保存至图片 5.采集USB摄像头保存至mp4文件 1.操作系统版本 使用的虚拟机加ubuntu 20.04 2. ...

  9. php调用linux摄像头,Linux_Linux中开发USB摄像头驱动详解,USB摄像头以其良好的性能和低 - phpStudy...

    Linux中开发USB摄像头驱动详解 USB摄像头以其良好的性能和低廉的价格得到广泛应用.同时因其灵活.方便的特性,易于集成到嵌入式系统中.但是如果使用现有的符合Video for Linux标准的驱 ...

最新文章

  1. 生命的礼赞,请记住我的名字,我叫科比-布莱恩特
  2. 从头开始写框架(一):浅谈JS模块化发展
  3. linux 云主机 卡顿 排查过程
  4. Quartz教程二:API,Job和Trigger
  5. C语言面向对象编程(一):封装与继承
  6. 浅谈数据中的偏差问题和推荐系统去偏最新研究进展
  7. html rotate()函数,CSS rotate()用法及代码示例
  8. mysql 5.7.11 x64_mysql 5.7.11 winx64安装配置教程
  9. Python+OpenCV:Canny边缘检测
  10. 《TCP IP 详解卷1:协议》阅读笔记 - 第十四章
  11. 深度理解Powell优化算法
  12. 微信小黄鸡php,微信表情包小黄鸡含义
  13. Power BI前置知识+ 一张报表制作的流程
  14. prisma2.0和nexus搭建graphql后端(1)—prisma2.0
  15. 关于iphone手机影片预览的格式 转自 我的恩师 郑哥
  16. 机器学习实战——绘制决策树(代码)
  17. Android实战(MediaPlayer填坑)
  18. 自己写小工具 让工作更简单更效率
  19. C语言 -统计并打印1000年-2000年之间的闰年
  20. 操作简单的影音文件播放工具-万能播放器下载

热门文章

  1. mips中的li_MIPS学习笔记(一)
  2. python 使用多个elif代码块 计算阶梯电费
  3. [转]设置IE背景色保护你的眼睛视力_鹤壁吧_贴吧
  4. 常见几个排序源码及二分查找源码
  5. matlab利用已知数据画图三维,Matlab三维数据画图和等高线数据提取
  6. 重磅:Mobileye官宣推迟IPO,营收增速放缓、市场竞争加剧
  7. 饭谈:盘点六种错误的技术问题提问
  8. 安卓玩机搞机-----没有第三方包 刷写第三方各种GSI系统 体验非官方系统
  9. 物联网卡要求实名认证的真正原因你知道吗?
  10. linux 无线网卡ping不同,请教高手,本地网卡和无线网卡均不能ping通网关