Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions audio1/audio.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ type Audio struct {
mu sync.Mutex
quit chan struct{}

cards CardList
cards CardList
cardFixGroup *CardFixGroup

isSaving bool
saverLocker sync.Mutex
Expand All @@ -205,10 +206,11 @@ type Audio struct {

func newAudio(service *dbusutil.Service) *Audio {
a := &Audio{
service: service,
meters: make(map[string]*Meter),
MaxUIVolume: pulse.VolumeUIMax,
service: service,
meters: make(map[string]*Meter),
MaxUIVolume: pulse.VolumeUIMax,
AudioServerState: AudioStateChanged,
cardFixGroup: newCardFixGroup(),
}

var err error
Expand Down Expand Up @@ -991,13 +993,39 @@ func (a *Audio) setPort(cardId uint32, portName string, direction int, auto bool
logger.Warning(err)
return err
}

// 提前校验:sink 方向的端口必须有可用的 profiles
if direction == pulse.DirectionSink && port.Profiles == nil {
logger.Warningf("setPort: card %d port %s has nil profiles", cardId, portName)
return fmt.Errorf("card %d port %s has nil profiles", cardId, portName)
}

// 获取声卡配置中的profile
var profile string
if direction == pulse.DirectionSink {
profile = GetConfigKeeper().GetMode(card, portName)
if profile == "" {
profile = port.Profiles.SelectProfile()
if profile == "" {
logger.Warningf("setPort: no available profile for card %d port %s", cardId, portName)
return fmt.Errorf("no available profile for card %d port %s", cardId, portName)
}
GetConfigKeeper().SetMode(card, portName, profile)
} else {
// 情况一:兼容pipewire升级导致的声卡的配置文件的发生变化,导致配置找不到
// 情况二:另一种情况是pipewire初始化时,音频端口不是同时上报上来的,导致配置文件找不到,暂时先切换到已存在的profile
// 由于情况二的场景存在,不能在这里修改profile
if !card.Profiles.Exists(profile) {
oldProfile := profile
profile = port.Profiles.SelectProfile()
if profile == "" {
logger.Warningf("setPort: no available fallback profile for card %d port %s", cardId, portName)
return fmt.Errorf("no available fallback profile for card %d port %s", cardId, portName)
}
a.cardFixGroup.addFix(cardId, 30*time.Second, func() {
card.fixProfile(a, portName, oldProfile)
})
}
}
} else {
profile = card.ActiveProfile.Name
Expand Down
1 change: 1 addition & 0 deletions audio1/audio_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ func (a *Audio) handleCardRemoved(idx uint32) {
return
}
logger.Infof("card <%s:%d> removed", oldCard.core.Name, idx)
a.cardFixGroup.deleteFix(idx)
a.cards, _ = a.cards.delete(idx)
cards := a.cards.string()
a.setPropCards(cards)
Expand Down
107 changes: 107 additions & 0 deletions audio1/card.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"math"
"sort"
"strings"
"sync"
"time"

"github.com/linuxdeepin/go-lib/pulse"
"github.com/linuxdeepin/go-lib/strv"
Expand Down Expand Up @@ -425,3 +427,108 @@ func (card *Card) doDiff(oldCard *Card, autoPause bool) ChangeType {
}
return changed
}

// CardFixGroup 管理需要延迟修复的声卡
type CardFixGroup struct {
mu sync.Mutex
fixes map[uint32]*CardFix
}

func newCardFixGroup() *CardFixGroup {
return &CardFixGroup{
fixes: make(map[uint32]*CardFix),
}
}

// addFix 添加一个待修复的声卡,延迟delay后执行fixFn(sync.Once保证只执行一次)
func (cfg *CardFixGroup) addFix(cardId uint32, delay time.Duration, fixFn func()) {
if fixFn == nil {
logger.Warning("addFix: fixFn is nil, skip")
return
}

cfg.mu.Lock()
defer cfg.mu.Unlock()

// 已存在则跳过
if _, exists := cfg.fixes[cardId]; exists {
return
}

fix := &CardFix{
cardId: cardId,
fixFn: fixFn,
}
fix.timer = time.AfterFunc(delay, func() {
fix.once.Do(func() {
fix.fixFn()
})
})
cfg.fixes[cardId] = fix
}

// deleteFix 删除并停止指定声卡的修复定时器
func (cfg *CardFixGroup) deleteFix(cardId uint32) {
cfg.mu.Lock()
defer cfg.mu.Unlock()

if fix, exists := cfg.fixes[cardId]; exists {
if fix.timer != nil {
fix.timer.Stop()
}
delete(cfg.fixes, cardId)
}
}

// fixProfile 修复声卡配置中的profile:配置中保存的profile一直不在card的profile列表中,则更新配置
func (c *Card) fixProfile(a *Audio, portName string, oldProfile string) {
if a == nil {
logger.Warning("fixProfile: audio is nil")
return
}

// 加锁保护 a.cards 的并发访问,避免与 handleCardRemoved 产生数据竞争
a.mu.Lock()
currentCard, err := a.cards.get(c.Id)
a.mu.Unlock()

if err != nil {
logger.Warningf("fixProfile: card %d not found", c.Id)
return
}

if currentCard == nil {
logger.Warningf("fixProfile: card %d is nil", c.Id)
return
}

// 配置已经被其他流程更新,无需修复
currentMode := GetConfigKeeper().GetMode(currentCard, portName)
if currentMode != oldProfile {
logger.Debugf("fixProfile: config already changed to %s, skip", currentMode)
return
}

// 配置中保存的profile又出现了,说明是临时性变化,跳过修复
if currentCard.Profiles.Exists(oldProfile) {
logger.Debugf("fixProfile: old profile %s exists again, skip", oldProfile)
return
}

if currentCard.ActiveProfile == nil {
logger.Warningf("fixProfile: card %d ActiveProfile is nil", c.Id)
return
}

logger.Infof("fixProfile: update config profile from %s to %s for port %s",
oldProfile, currentCard.ActiveProfile.Name, portName)
GetConfigKeeper().SetMode(currentCard, portName, currentCard.ActiveProfile.Name)
}

// CardFix 单个声卡的修复项,fixFn由调用方提供具体修复逻辑
type CardFix struct {
cardId uint32
fixFn func()
timer *time.Timer
once sync.Once
}
14 changes: 13 additions & 1 deletion audio1/profile.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

Expand Down Expand Up @@ -33,6 +33,18 @@ func newProfile(info pulse.ProfileInfo2) *Profile {

type ProfileList []*Profile

func (l ProfileList) Exists(name string) bool {
if l == nil {
return false
}
for _, p := range l {
if p != nil && p.Name == name {
return true
}
}
return false
}

func newProfileList(src []pulse.ProfileInfo2) ProfileList {
var result ProfileList
blacklist := profileBlacklist()
Expand Down
Loading