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

Posted llguanli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了海思hi3716c机顶盒接usb摄像头和usb无线耳机时,无线耳机有时没有声音相关的知识,希望对你有一定的参考价值。

两个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:[email protected]/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无线耳机时,无线耳机有时没有声音的主要内容,如果未能解决你的问题,请参考以下文章

Hi3798Mv100 U 盘卡刷教程(全新刷&&升级刷)

海思Hi3518Ev200 4G wifi无线网络视频监控摄像开发板可二次开发

机顶盒Hi3798m芯片怎么样?

华为6110和海思3798哪个好?

手机控制电视的软件

华为海思刷机工具TTL刷机教程