mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
fix(transcode): honor bit depth and channel constraints in transcoding selection
selectTranscodingOptions only checked sample rate when deciding whether same-format transcoding was needed, ignoring requested bit depth and channel reductions. This caused the streamer to return raw audio when the transcode decision requested downmix or bit-depth conversion.
This commit is contained in:
parent
8f508d08f7
commit
c69ed2a913
@ -83,7 +83,7 @@ func (ms *mediaStreamer) DoStream(ctx context.Context, mf *model.MediaFile, req
|
||||
"originalFormat", mf.Suffix, "originalBitRate", mf.BitRate)
|
||||
}()
|
||||
|
||||
format, bitRate = selectTranscodingOptions(ctx, ms.ds, mf, req.Format, req.BitRate, req.SampleRate)
|
||||
format, bitRate = selectTranscodingOptions(ctx, ms.ds, mf, req.Format, req.BitRate, req.SampleRate, req.BitDepth, req.Channels)
|
||||
s := &Stream{ctx: ctx, mf: mf, format: format, bitRate: bitRate}
|
||||
filePath := mf.AbsolutePath()
|
||||
|
||||
@ -150,13 +150,15 @@ func (s *Stream) EstimatedContentLength() int {
|
||||
}
|
||||
|
||||
// TODO This function deserves some love (refactoring)
|
||||
func selectTranscodingOptions(ctx context.Context, ds model.DataStore, mf *model.MediaFile, reqFormat string, reqBitRate int, reqSampleRate int) (format string, bitRate int) {
|
||||
func selectTranscodingOptions(ctx context.Context, ds model.DataStore, mf *model.MediaFile, reqFormat string, reqBitRate int, reqSampleRate int, reqBitDepth int, reqChannels int) (format string, bitRate int) {
|
||||
format = "raw"
|
||||
if reqFormat == "raw" {
|
||||
return format, 0
|
||||
}
|
||||
needsResample := reqSampleRate > 0 && reqSampleRate < mf.SampleRate
|
||||
if reqFormat == mf.Suffix && reqBitRate == 0 && !needsResample {
|
||||
needsTranscode := (reqSampleRate > 0 && reqSampleRate < mf.SampleRate) ||
|
||||
(reqBitDepth > 0 && reqBitDepth < mf.BitDepth) ||
|
||||
(reqChannels > 0 && reqChannels < mf.Channels)
|
||||
if reqFormat == mf.Suffix && reqBitRate == 0 && !needsTranscode {
|
||||
bitRate = mf.BitRate
|
||||
return format, bitRate
|
||||
}
|
||||
@ -195,7 +197,7 @@ func selectTranscodingOptions(ctx context.Context, ds model.DataStore, mf *model
|
||||
bitRate = t.DefaultBitRate
|
||||
}
|
||||
}
|
||||
if format == mf.Suffix && bitRate >= mf.BitRate && !needsResample {
|
||||
if format == mf.Suffix && bitRate >= mf.BitRate && !needsTranscode {
|
||||
format = "raw"
|
||||
bitRate = 0
|
||||
}
|
||||
|
||||
@ -26,39 +26,39 @@ var _ = Describe("MediaStreamer", func() {
|
||||
It("returns raw if raw is requested", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "raw", 0, 0)
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "raw", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns raw if a transcoder does not exists", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "m4a", 0, 0)
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "m4a", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns the requested format if a transcoder exists", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("mp3"))
|
||||
Expect(bitRate).To(Equal(160)) // Default Bit Rate
|
||||
})
|
||||
It("returns raw if requested format is the same as the original and it is not necessary to downsample", func() {
|
||||
mf.Suffix = "mp3"
|
||||
mf.BitRate = 112
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "mp3", 128, 0)
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "mp3", 128, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns the requested format if requested BitRate is lower than original", func() {
|
||||
mf.Suffix = "mp3"
|
||||
mf.BitRate = 320
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 192, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 192, 0, 0, 0)
|
||||
Expect(format).To(Equal("mp3"))
|
||||
Expect(bitRate).To(Equal(192))
|
||||
})
|
||||
It("returns raw if requested format is the same as the original, but requested BitRate is 0", func() {
|
||||
mf.Suffix = "mp3"
|
||||
mf.BitRate = 320
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
Expect(bitRate).To(Equal(320))
|
||||
})
|
||||
@ -66,7 +66,7 @@ var _ = Describe("MediaStreamer", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 2118
|
||||
mf.SampleRate = 96000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 48000)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 48000, 0, 0)
|
||||
Expect(format).To(Equal("flac"))
|
||||
Expect(bitRate).To(Equal(0))
|
||||
})
|
||||
@ -74,14 +74,44 @@ var _ = Describe("MediaStreamer", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
mf.SampleRate = 48000
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 48000)
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 48000, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns raw when same format is requested with no sample rate constraint", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
mf.SampleRate = 96000
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 0)
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns the format when same format is requested but with lower bit depth", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 2118
|
||||
mf.BitDepth = 24
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 0, 16, 0)
|
||||
Expect(format).To(Equal("flac"))
|
||||
Expect(bitRate).To(Equal(0))
|
||||
})
|
||||
It("returns raw when same format is requested with same bit depth", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
mf.BitDepth = 16
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 0, 16, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns the format when same format is requested but with fewer channels", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 2118
|
||||
mf.Channels = 6
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 0, 0, 2)
|
||||
Expect(format).To(Equal("flac"))
|
||||
Expect(bitRate).To(Equal(0))
|
||||
})
|
||||
It("returns raw when same format is requested with same channels", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
mf.Channels = 2
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "flac", 0, 0, 0, 2)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
Context("Downsampling", func() {
|
||||
@ -91,13 +121,13 @@ var _ = Describe("MediaStreamer", func() {
|
||||
mf.BitRate = 960
|
||||
})
|
||||
It("returns the DefaultDownsamplingFormat if a maxBitrate is requested but not the format", func() {
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 128, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 128, 0, 0, 0)
|
||||
Expect(format).To(Equal("opus"))
|
||||
Expect(bitRate).To(Equal(128))
|
||||
})
|
||||
It("returns raw if maxBitrate is equal or greater than original", func() {
|
||||
// This happens with DSub (and maybe other clients?). See https://github.com/navidrome/navidrome/issues/2066
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 960, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 960, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
Expect(bitRate).To(Equal(0))
|
||||
})
|
||||
@ -112,34 +142,34 @@ var _ = Describe("MediaStreamer", func() {
|
||||
It("returns raw if raw is requested", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "raw", 0, 0)
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "raw", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns configured format/bitrate as default", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 0, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("oga"))
|
||||
Expect(bitRate).To(Equal(96))
|
||||
})
|
||||
It("returns requested format", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("mp3"))
|
||||
Expect(bitRate).To(Equal(160)) // Default Bit Rate
|
||||
})
|
||||
It("returns requested bitrate", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 80, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 80, 0, 0, 0)
|
||||
Expect(format).To(Equal("oga"))
|
||||
Expect(bitRate).To(Equal(80))
|
||||
})
|
||||
It("returns raw if selected bitrate and format is the same as original", func() {
|
||||
mf.Suffix = "mp3"
|
||||
mf.BitRate = 192
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 192, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 192, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
Expect(bitRate).To(Equal(0))
|
||||
})
|
||||
@ -155,27 +185,27 @@ var _ = Describe("MediaStreamer", func() {
|
||||
It("returns raw if raw is requested", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "raw", 0, 0)
|
||||
format, _ := selectTranscodingOptions(ctx, ds, mf, "raw", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("raw"))
|
||||
})
|
||||
It("returns configured format/bitrate as default", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 0, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("oga"))
|
||||
Expect(bitRate).To(Equal(192))
|
||||
})
|
||||
It("returns requested format", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "mp3", 0, 0, 0, 0)
|
||||
Expect(format).To(Equal("mp3"))
|
||||
Expect(bitRate).To(Equal(160)) // Default Bit Rate
|
||||
})
|
||||
It("returns requested bitrate", func() {
|
||||
mf.Suffix = "flac"
|
||||
mf.BitRate = 1000
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 160, 0)
|
||||
format, bitRate := selectTranscodingOptions(ctx, ds, mf, "", 160, 0, 0, 0)
|
||||
Expect(format).To(Equal("oga"))
|
||||
Expect(bitRate).To(Equal(160))
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user