From d3ad229aaf6bfc73a64e5b41bc254902607e5c89 Mon Sep 17 00:00:00 2001 From: fuleyi Date: Thu, 6 Nov 2025 10:39:59 +0800 Subject: [PATCH] fix: fix bluetooth audio port priority assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changes address Bluetooth audio port priority issues by improving the priority assignment logic. Previously, port priorities were hardcoded based on port names, which was unreliable. Now priorities are determined by analyzing the port's profile information to accurately identify the audio mode (A2DP, headset, or handsfree). Additionally, the audio configuration system has been simplified by removing unused configuration file handling code and the first-run initialization logic since these features are no longer needed. Log: Fixed Bluetooth audio port priority assignment to correctly identify audio modes Influence: 1. Test Bluetooth audio device connection and automatic profile selection 2. Verify A2DP mode has higher priority than headset mode 3. Check that audio switches correctly between different Bluetooth profiles 4. Test audio output quality in different Bluetooth modes 5. Verify mono audio settings work properly with Bluetooth devices fix: 修复蓝牙音频端口优先级分配问题 本次修改解决了蓝牙音频端口优先级问题,改进了优先级分配逻辑。之前基于端口 名称硬编码优先级的方式不可靠,现在通过分析端口的配置文件信息来准确识别音 频模式(A2DP、耳机或免提)。此外,简化了音频配置系统,移除了未使用的配置 文件处理代码和首次运行初始化逻辑,因为这些功能不再需要。 Log: 修复蓝牙音频端口优先级分配,正确识别音频模式 Influence: 1. 测试蓝牙音频设备连接和自动配置文件选择 2. 验证A2DP模式优先级高于耳机模式 3. 检查音频在不同蓝牙配置文件之间正确切换 4. 测试不同蓝牙模式下的音频输出质量 5. 验证单声道设置与蓝牙设备正常工作 --- audio1/audio.go | 10 -- audio1/audio_config.go | 43 +----- audio1/bluez_audio.go | 3 +- audio1/card.go | 30 +++- audio1/config.go | 135 ------------------ audio1/config_test.go | 20 --- audio1/sink.go | 7 +- .../org.deepin.dde.daemon.audio.json | 11 -- 8 files changed, 29 insertions(+), 230 deletions(-) delete mode 100644 audio1/config.go delete mode 100644 audio1/config_test.go diff --git a/audio1/audio.go b/audio1/audio.go index 7438876d1..14f0663ea 100644 --- a/audio1/audio.go +++ b/audio1/audio.go @@ -845,16 +845,6 @@ func (a *Audio) init() error { go a.handleStateChanged() logger.Debug("init done") - firstRun, err := a.audioDConfig.GetValueBool(dsgKeyFirstRun) - if err != nil { - logger.Warning(err) - } - if firstRun { - logger.Info("first run, Will remove old audio config") - removeConfig() - a.audioDConfig.SetValue(dsgKeyFirstRun, false) - } - if a.defaultSink != nil && !a.needAutoSwitchOutputPort() { a.resumeSinkConfig(a.defaultSink) a.setMono(a.Mono) diff --git a/audio1/audio_config.go b/audio1/audio_config.go index ecafd26a4..59701660d 100644 --- a/audio1/audio_config.go +++ b/audio1/audio_config.go @@ -6,7 +6,6 @@ package audio import ( "fmt" - "os" "time" dbus "github.com/godbus/dbus/v5" @@ -35,52 +34,12 @@ func (a *Audio) saveConfig() { } func (a *Audio) doSaveConfig() { - var info = config{ - Profiles: make(map[string]string), - } - ctx := a.context() if ctx == nil { logger.Warning("failed to save config, ctx is nil") return } - - for _, card := range ctx.GetCardList() { - info.Profiles[card.Name] = card.ActiveProfile.Name - } - - for _, sinkInfo := range ctx.GetSinkList() { - if a.getDefaultSinkName() != sinkInfo.Name { - continue - } - - info.Sink = sinkInfo.Name - info.SinkPort = sinkInfo.ActivePort.Name - info.SinkVolume = sinkInfo.Volume.Avg() - break - } - - for _, sourceInfo := range ctx.GetSourceList() { - if a.getDefaultSourceName() != sourceInfo.Name { - continue - } - - info.Source = sourceInfo.Name - info.SourcePort = sourceInfo.ActivePort.Name - info.SourceVolume = sourceInfo.Volume.Avg() - break - } - _, err := readConfig() - if err != nil && !os.IsNotExist(err) { - logger.Warning(err) - } - if len(info.SourcePort) != 0 { - err = saveConfig(&info) - if err != nil { - logger.Warning("save config file failed:", info.string(), err) - } - } - err = a.saveAudioState() + err := a.saveAudioState() if err != nil { logger.Warning(err) } diff --git a/audio1/bluez_audio.go b/audio1/bluez_audio.go index 963b70876..327b03963 100644 --- a/audio1/bluez_audio.go +++ b/audio1/bluez_audio.go @@ -17,7 +17,8 @@ const ( ) const ( - PriorityHandset = 20000 + PriorityA2dp = 20000 + PriorityHandset = 10000 PriorityHandfree = 10000 ) diff --git a/audio1/card.go b/audio1/card.go index 2e35f1b72..a82220b4e 100644 --- a/audio1/card.go +++ b/audio1/card.go @@ -8,6 +8,7 @@ import ( "fmt" "math" "sort" + "strings" "github.com/linuxdeepin/go-lib/pulse" "github.com/linuxdeepin/go-lib/strv" @@ -113,11 +114,13 @@ func (c *Card) update(card *pulse.Card) { logger.Debugf("filter bluez input port %s", port.Name) port.Available = pulse.AvailableTypeNo } - switch port.Name { - case "headset-output": - port.Priority += PriorityHandset - case "headset-hf-output": - port.Priority += PriorityHandfree + switch portBluezMode(&port) { + case bluezModeA2dp: + port.Priority = PriorityA2dp + case bluezModeHeadset: + port.Priority = PriorityHandset + case bluezModeHandsfree: + port.Priority = PriorityHandfree } } } @@ -128,6 +131,23 @@ func (c *Card) update(card *pulse.Card) { c.sortPortsByPriority(card) } +func portBluezMode(port *pulse.CardPortInfo) string { + for _, profile := range port.Profiles { + logger.Warningf("port<%s> profile<%s> %v", port.Name, profile.Name, profile.Available) + + if strings.Contains(strings.ToLower(profile.Name), bluezModeA2dp) { + return bluezModeA2dp + } + if strings.Contains(strings.ToLower(profile.Name), bluezModeHeadset) { + return bluezModeHeadset + } + if strings.Contains(strings.ToLower(profile.Name), bluezModeHandsfree) { + return bluezModeHeadset + } + } + return "" +} + // sortPortsByPriority 根据端口的 Priority 属性排序,Priority 值小的在前(由低到高) func (c *Card) sortPortsByPriority(card *pulse.Card) { sort.SliceStable(c.Ports, func(i, j int) bool { diff --git a/audio1/config.go b/audio1/config.go deleted file mode 100644 index 7ec273be9..000000000 --- a/audio1/config.go +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package audio - -import ( - "encoding/json" - "os" - "path/filepath" - "sync" - - "github.com/linuxdeepin/go-lib/xdg/basedir" -) - -var ( - fileLocker sync.Mutex - configCache *config - configFile = filepath.Join(basedir.GetUserConfigDir(), "deepin/dde-daemon/audio.json") -) - -type config struct { - Profiles map[string]string // Profiles[cardName] = activeProfile - Sink string - Source string - SinkPort string - SourcePort string - - SinkVolume float64 - SourceVolume float64 -} - -func (c *config) string() string { - data, _ := json.Marshal(c) - return string(data) -} - -func (c *config) equal(b *config) bool { - if c == nil && b == nil { - return true - } - if c == nil || b == nil { - return false - } - return c.Sink == b.Sink && - c.Source == b.Source && - c.SinkPort == b.SinkPort && - c.SourcePort == b.SourcePort && - c.SinkVolume == b.SinkVolume && - c.SourceVolume == b.SourceVolume && - mapStrStrEqual(c.Profiles, b.Profiles) -} - -func readConfig() (*config, error) { - fileLocker.Lock() - defer fileLocker.Unlock() - - if configCache != nil { - return configCache, nil - } - - var info config - content, err := os.ReadFile(configFile) - if err != nil { - return nil, err - } - err = json.Unmarshal(content, &info) - if err != nil { - return nil, err - } - - configCache = &info - return configCache, nil -} - -func saveConfig(info *config) error { - fileLocker.Lock() - defer fileLocker.Unlock() - - if configCache.equal(info) { - logger.Debug("[saveConfigInfo] config info not changed") - return nil - } else { - logger.Debug("[saveConfigInfo] will save:", info.string()) - } - - content, err := json.Marshal(info) - if err != nil { - return err - } - err = os.MkdirAll(filepath.Dir(configFile), 0755) - if err != nil { - return err - } - err = os.WriteFile(configFile, content, 0644) - if err != nil { - return err - } - - configCache = info - return nil -} - -func removeConfig() { - fileLocker.Lock() - defer fileLocker.Unlock() - - err := os.Remove(configFile) - if err != nil && !os.IsNotExist(err) { - logger.Warning(err) - } - - err = os.Remove(globalConfigKeeperFile) - if err != nil && !os.IsNotExist(err) { - logger.Warning(err) - } - - err = os.Remove(globalPrioritiesFilePath) - if err != nil && !os.IsNotExist(err) { - logger.Warning(err) - } -} - -func mapStrStrEqual(a, b map[string]string) bool { - if len(a) != len(b) { - return false - } - - for k, v := range a { - if w, ok := b[k]; !ok || v != w { - return false - } - } - return true -} diff --git a/audio1/config_test.go b/audio1/config_test.go deleted file mode 100644 index dfba2c4c5..000000000 --- a/audio1/config_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package audio - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_mapStrStrEqual(t *testing.T) { - var test = map[string]string{"aaa": "aaa", "bbb": "bbb", "ccc": "ccc"} - var test1 = map[string]string{"aaa": "aaa", "bbb": "bbb", "ccc": "ccc"} - var test2 = map[string]string{"aaa": "aaa", "bbb": "bbb", "ccc": "ddd"} - - assert.Equal(t, mapStrStrEqual(test, test1), true) - assert.Equal(t, mapStrStrEqual(test, test2), false) -} diff --git a/audio1/sink.go b/audio1/sink.go index 2cd0296be..b7c30a41f 100644 --- a/audio1/sink.go +++ b/audio1/sink.go @@ -128,12 +128,7 @@ func (s *Sink) SetMono(enable bool) error { var err error a := s.audio // 当存在单声道时,都给移除掉 - for _, sink := range a.sinks { - if sink.Name == "remap-sink-mono" { - a.unsetMono() - break - } - } + a.unsetMono() if enable { // sink_master并非标准接口,而是识别物理设备对应的sink a.context().LoadModule("module-remap-sink", fmt.Sprintf("sink_name=remap-sink-mono channels=1 channel_map=mono sink_master=%s", s.Name)) diff --git a/misc/dsg-configs/org.deepin.dde.daemon.audio.json b/misc/dsg-configs/org.deepin.dde.daemon.audio.json index 6371848f3..b028a259a 100644 --- a/misc/dsg-configs/org.deepin.dde.daemon.audio.json +++ b/misc/dsg-configs/org.deepin.dde.daemon.audio.json @@ -109,17 +109,6 @@ "permissions": "readwrite", "visibility": "private" }, - "firstRun": { - "value": true, - "serial": 0, - "flags": [], - "name": "firstRun", - "name[zh_CN]": "是否已初始化", - "description": "If no initialized, do it, otherwise nothing to do.", - "description[zh_CN]": "如果未初始化则执行初始化,否则不执行任何操作", - "permissions": "readwrite", - "visibility": "private" - }, "inputVolume": { "value": 50, "serial": 0,