fix mpv mem leak, waveform gen when double-clicking to play tracks; clean up logs
This commit is contained in:
@@ -100,7 +100,7 @@ func (a *AudioCache) cacheFile(id, dlURL string) {
|
|||||||
e.done = true
|
e.done = true
|
||||||
}
|
}
|
||||||
a.mutex.Unlock()
|
a.mutex.Unlock()
|
||||||
} else if err != context.DeadlineExceeded {
|
} else if err != nil && err != context.DeadlineExceeded {
|
||||||
log.Printf("error downloading audio file: %v", err)
|
log.Printf("error downloading audio file: %v", err)
|
||||||
}
|
}
|
||||||
cancel() // release ctx resources when done
|
cancel() // release ctx resources when done
|
||||||
@@ -120,9 +120,9 @@ func (a *AudioCache) CacheOnly(keep string, fetch []AudioCacheRequest) {
|
|||||||
return a.ID == id
|
return a.ID == id
|
||||||
}) {
|
}) {
|
||||||
_ = e
|
_ = e
|
||||||
//e.cancel()
|
e.cancel()
|
||||||
//_ = os.Remove(a.pathForID(id))
|
_ = os.Remove(a.pathForID(id))
|
||||||
//delete(a.entries, id)
|
delete(a.entries, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -584,7 +584,6 @@ func (p *playbackEngine) cacheNextTracks() {
|
|||||||
id = np.Metadata().ID
|
id = np.Metadata().ID
|
||||||
}
|
}
|
||||||
p.audiocache.CacheOnly(id, fetch)
|
p.audiocache.CacheOnly(id, fetch)
|
||||||
log.Println("fetching files", sharedutil.MapSlice(fetch, func(a AudioCacheRequest) string { return a.ID }))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,7 +684,6 @@ func (p *playbackEngine) setTrack(idx int, next bool, startTime float64) error {
|
|||||||
if isTrack && p.audiocache != nil {
|
if isTrack && p.audiocache != nil {
|
||||||
if filepath := p.audiocache.PathForCachedFile(track.ID); filepath != "" {
|
if filepath := p.audiocache.PathForCachedFile(track.ID); filepath != "" {
|
||||||
url = filepath
|
url = filepath
|
||||||
log.Println("playing file from cache")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if url == "" {
|
if url == "" {
|
||||||
|
|||||||
+42
-29
@@ -84,34 +84,44 @@ func (p *PlaybackManager) addOnTrackChangeHook() {
|
|||||||
p.lastPlayTime = curTime
|
p.lastPlayTime = curTime
|
||||||
})
|
})
|
||||||
|
|
||||||
var curWaveformJob *WaveformImageJob
|
var waveformJobs [3]*WaveformImageJob
|
||||||
var nextWaveformJob *WaveformImageJob
|
findJob := func(id string) (*WaveformImageJob, bool) {
|
||||||
var refreshCancel context.CancelFunc
|
for _, j := range waveformJobs {
|
||||||
|
if j != nil && j.ItemID == id {
|
||||||
|
return j, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
addJob := func(job *WaveformImageJob) {
|
||||||
|
waveformJobs[0].Cancel()
|
||||||
|
waveformJobs[0] = waveformJobs[1]
|
||||||
|
waveformJobs[1] = waveformJobs[2]
|
||||||
|
waveformJobs[2] = job
|
||||||
|
}
|
||||||
|
|
||||||
p.engine.onBeforeSongChange = append(p.engine.onBeforeSongChange, func(item mediaprovider.MediaItem) {
|
p.engine.onBeforeSongChange = append(p.engine.onBeforeSongChange, func(item mediaprovider.MediaItem) {
|
||||||
if p.wfmGen != nil && item != nil && item.Metadata().Type == mediaprovider.MediaItemTypeTrack {
|
if p.wfmGen != nil && item != nil && item.Metadata().Type == mediaprovider.MediaItemTypeTrack {
|
||||||
log.Println("preparing waveform image for next track ", item.Metadata().ID)
|
// start generating waveform image for next-up track
|
||||||
id := item.Metadata().ID
|
addJob(p.wfmGen.StartWaveformGeneration(item.(*mediaprovider.Track)))
|
||||||
_ = p.cache.PathForCachedOrDownloadingFile(id)
|
|
||||||
|
|
||||||
curWaveformJob.Cancel()
|
|
||||||
curWaveformJob = nextWaveformJob
|
|
||||||
nextWaveformJob = p.wfmGen.StartWaveformGeneration(item.(*mediaprovider.Track))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
lastPlayingID := ""
|
||||||
|
var wfmImageUpdateCancel context.CancelFunc
|
||||||
|
|
||||||
p.OnSongChange(func(item mediaprovider.MediaItem, _ *mediaprovider.Track) {
|
p.OnSongChange(func(item mediaprovider.MediaItem, _ *mediaprovider.Track) {
|
||||||
// Autoplay if enabled and we are on the last track
|
// Autoplay if enabled and we are on the last track
|
||||||
if p.autoplay && p.NowPlayingIndex() == len(p.engine.playQueue)-1 {
|
if p.autoplay && p.NowPlayingIndex() == len(p.engine.playQueue)-1 {
|
||||||
p.enqueueAutoplayTracks()
|
p.enqueueAutoplayTracks()
|
||||||
}
|
}
|
||||||
if refreshCancel != nil {
|
if wfmImageUpdateCancel != nil {
|
||||||
refreshCancel()
|
wfmImageUpdateCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUnfinishedJob := func(job *WaveformImageJob) {
|
updateUnfinishedJob := func(job *WaveformImageJob) {
|
||||||
ctx, c := context.WithCancel(p.cache.rootCtx)
|
ctx, c := context.WithCancel(p.cache.rootCtx)
|
||||||
refreshCancel = c
|
wfmImageUpdateCancel = c
|
||||||
go func(ctx context.Context, job *WaveformImageJob) {
|
go func(ctx context.Context, job *WaveformImageJob) {
|
||||||
for {
|
for {
|
||||||
time.Sleep(333 * time.Millisecond)
|
time.Sleep(333 * time.Millisecond)
|
||||||
@@ -132,24 +142,27 @@ func (p *PlaybackManager) addOnTrackChangeHook() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if item != nil {
|
if item != nil {
|
||||||
log.Println("Playing track ", item.Metadata().ID)
|
// cancel possible waveform generation job for previous track
|
||||||
var im *WaveformImage
|
if old, ok := findJob(lastPlayingID); ok {
|
||||||
done := false
|
old.Cancel()
|
||||||
if nextWaveformJob.ItemID == item.Metadata().ID {
|
|
||||||
log.Println("Have waveform in progress for", item.Metadata().ID)
|
|
||||||
done = nextWaveformJob.Done()
|
|
||||||
im = nextWaveformJob.Get()
|
|
||||||
}
|
}
|
||||||
if im != nil {
|
lastPlayingID = item.Metadata().ID
|
||||||
for _, cb := range p.onWaveformImgUpdate {
|
|
||||||
cb(im)
|
var job *WaveformImageJob
|
||||||
}
|
if j, ok := findJob(item.Metadata().ID); ok {
|
||||||
if !done {
|
job = j
|
||||||
updateUnfinishedJob(nextWaveformJob)
|
|
||||||
}
|
|
||||||
} else if tr, ok := item.(*mediaprovider.Track); ok {
|
} else if tr, ok := item.(*mediaprovider.Track); ok {
|
||||||
curWaveformJob = p.wfmGen.StartWaveformGeneration(tr)
|
job = p.wfmGen.StartWaveformGeneration(tr)
|
||||||
updateUnfinishedJob(curWaveformJob)
|
addJob(job)
|
||||||
|
}
|
||||||
|
if job != nil {
|
||||||
|
img := job.Get()
|
||||||
|
for _, cb := range p.onWaveformImgUpdate {
|
||||||
|
cb(img)
|
||||||
|
}
|
||||||
|
if !job.done && job != nil {
|
||||||
|
updateUnfinishedJob(job)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ func (w *WaveformImageJob) Err() error {
|
|||||||
|
|
||||||
func (w *WaveformImageJob) Get() *WaveformImage {
|
func (w *WaveformImageJob) Get() *WaveformImage {
|
||||||
if w.Done() {
|
if w.Done() {
|
||||||
log.Println("returning image directly")
|
|
||||||
return w.img
|
return w.img
|
||||||
}
|
}
|
||||||
// return a new *WaveformImage with data copied
|
// return a new *WaveformImage with data copied
|
||||||
@@ -143,7 +142,6 @@ func (w *WaveformImageGenerator) StartWaveformGeneration(item *mediaprovider.Tra
|
|||||||
|
|
||||||
path = srv.Addr()
|
path = srv.Addr()
|
||||||
|
|
||||||
log.Println("streaming file to MPV at ", path)
|
|
||||||
go srv.Serve()
|
go srv.Serve()
|
||||||
time.Sleep(10 * time.Millisecond) // make sure server has time to come up
|
time.Sleep(10 * time.Millisecond) // make sure server has time to come up
|
||||||
}
|
}
|
||||||
@@ -155,7 +153,6 @@ func (w *WaveformImageGenerator) StartWaveformGeneration(item *mediaprovider.Tra
|
|||||||
err := convertToWav(ctx, path, transcodeFile)
|
err := convertToWav(ctx, path, transcodeFile)
|
||||||
wavConvertDone = true
|
wavConvertDone = true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error converting to wav", err)
|
|
||||||
job.setError(err)
|
job.setError(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -178,7 +175,6 @@ func (w *WaveformImageGenerator) StartWaveformGeneration(item *mediaprovider.Tra
|
|||||||
go func() {
|
go func() {
|
||||||
err := analyzeWavFile(ctx, transcodeFile, data, item.Duration.Milliseconds(), func() bool { return wavConvertDone })
|
err := analyzeWavFile(ctx, transcodeFile, data, item.Duration.Milliseconds(), func() bool { return wavConvertDone })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error analyzing wav", err.Error())
|
|
||||||
job.setError(err)
|
job.setError(err)
|
||||||
}
|
}
|
||||||
data.done = true
|
data.done = true
|
||||||
@@ -249,7 +245,6 @@ func generateWaveformImage(ctx context.Context, data *waveformData, job *Wavefor
|
|||||||
func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformData, millisecs int64, fileDone func() bool) error {
|
func analyzeWavFile(ctx context.Context, transcodeFile string, data *waveformData, millisecs int64, fileDone func() bool) error {
|
||||||
f, err := os.Open(transcodeFile)
|
f, err := os.Open(transcodeFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error opening transcoded file")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
@@ -409,6 +404,8 @@ func convertToWav(ctx context.Context, inPath, outPath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer m.TerminateDestroy()
|
||||||
|
|
||||||
m.Command([]string{"loadfile", inPath, "replace"})
|
m.Command([]string{"loadfile", inPath, "replace"})
|
||||||
|
|
||||||
// Wait for MPV idle or ctx expiry
|
// Wait for MPV idle or ctx expiry
|
||||||
|
|||||||
Reference in New Issue
Block a user