海思hi3716c机顶盒接usb摄像头和usb无线耳机时,无线耳机有时没有声音
A:USB攝像頭帶錄音功能,但不帶放音功能。
B:USB無線耳機是使用USB轉2.4G的無線耳機。
詳細現(xiàn)象:
1, A,B兩者同一時候插上機頂盒,并開機進入android,此時去播放音樂或電影,聲音是從HDMI出來的,并不是從無線耳機出來。
此時又一次插拔一下2.4G無線耳機,聲音就會從耳機中出來。
2, 機頂盒上電。進入android系統(tǒng),然后播放音樂或電影,此時聲音從HDMI中出來。這個時候接上USB攝像頭,聲音還是從HDMI出來。
再接上無線耳機。這時候聲音卻還是從HDMI中出來,此時應該要從耳機出來。又一次插拔一下耳機就恢復正常了。
總結現(xiàn)象,基本能夠得出一個結論:開機后。先插上USB攝像頭再插上2.4G耳麥,聲音并不會從耳機出來,僅僅有又一次插拔一次USB耳麥后才會正常。
從現(xiàn)象能夠看出,出現(xiàn)異常的原因是音頻系統(tǒng)沒有從攝像頭切換到麥克風。
細致查看日志,發(fā)現(xiàn)正常時會有一段以下的打印。不正常時并沒有。
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廣播消息。才干正常。在源代碼中搜索發(fā)出ACTION_USB_ANLG_HEADSET_PLUG廣播的地方,查得在WiredAccessoryObserver.java文件里sendIntent()函數(shù)調用了。
? ? 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);
? ? ? ? .......
? ? }
此函數(shù)被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中的相應函數(shù)。細致查看該文件源代碼能夠得知當中有個線程會讀取netlink消息。并由相應的Observer去處理。
在run()函數(shù)中增加打印日志的函數(shù)。將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并沒有發(fā)出uevent。google去查uevent機制,了解到change這個ACTION由KOBJ_CHANGE來控制。
依據(jù)uevent中SWITCH_NAME和SWITCH_STATE對kernel/下進行搜索,得到drivers/switch/switch_class.c文件。同一時候也依據(jù)KOBJ_CHANGE對drivers下進行搜索。也發(fā)現(xiàn)switch/switch_class.c文件,推測這個文件是關鍵。在switch_set_state()函數(shù)中增加打印,又一次編譯內核,執(zhí)行,查看日志,果然發(fā)現(xiàn)都調了這里。
void switch_set_state(struct switch_dev *sdev, int state)
{
? ? ......
? ? if (sdev->state != state) {
? ? ? ? sdev->state = state;
? ? ? ? ......
? ? }
? ? ......
}
繼續(xù)搜索調用switch_set_state()的地方,發(fā)如今sound/usb/card.c的snd_usb_audio_probe()函數(shù)中調用了。
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的狀態(tài)改為已連接。
當usb耳麥接上后,相同會調用switch_set_state(),但由于先前已經(jīng)連了一個usb音頻設備。sdev->state已經(jīng)變?yōu)?,不再繼續(xù)發(fā)消息。于是我凝視掉推斷語句:if (sdev->state != state) 。再次進行測試,發(fā)現(xiàn)uevent已經(jīng)上報了,但聲音仍然沒有從usb耳麥中出來。
繼續(xù)回到Android層,查看WiredAccessoryObserver.java中update()函數(shù)
? ? 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;
? ? ? ? }
? ? ? ? .......
}
分析代碼可知,因為上報的消息跟上次一樣,此函數(shù)并未繼續(xù)運行。
為了簡單起見,我想直接在內核載入新的usb音頻設備時先將switch斷開原來的。再連接新的。于是在snd_usb_audio_probe()函數(shù)中,先運行switch_set_state(&sdev, STATE_DISCONNECTED);欺騙系統(tǒng)switch已經(jīng)斷開。然后再運行switch_set_state(&sdev, STATE_CONNECTED);
編譯,燒錄,測試后發(fā)現(xiàn)一切都正常了。錄音與放音都正常。
當USB耳麥連接時用耳麥進行錄音和放音。
當USB耳麥不在時用USB攝像頭進行錄音,由HDMI進行放音。
對這個結果感到非常意外。意想之中的狀況應該是始終使用后插上的那個設備。查看日志后得知,由攝像頭沒有放音功能,在open這個設備進行放音的時候會失敗。于是就會使用下一個設備,直到找到能放音的設備,查看日志發(fā)現(xiàn)這是alsa-lib這么設計的。為何錄音也總是優(yōu)先使用USB耳麥,沒有深究,應該也是alsa-lib完畢了。
總結
以上是生活随笔為你收集整理的海思hi3716c机顶盒接usb摄像头和usb无线耳机时,无线耳机有时没有声音的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Gimp 将图片中的颜色更改
- 下一篇: Linux(CentOS)搭建redmi