diff --git a/conf/configuration.go b/conf/configuration.go
index e50d86eed..8830218df 100644
--- a/conf/configuration.go
+++ b/conf/configuration.go
@@ -22,10 +22,11 @@ type nd struct {
 	IgnoredArticles string `default:"The El La Los Las Le Les Os As O A"`
 	IndexGroups     string `default:"A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)"`
 
-	DisableDownsampling bool   `default:"false"`
-	DownsampleCommand   string `default:"ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -f mp3 -"`
-	ProbeCommand        string `default:"ffmpeg %s -f ffmetadata"`
-	ScanInterval        string `default:"1m"`
+	EnableDownsampling bool   `default:"false"`
+	MaxBitRate         int    `default:"0"`
+	DownsampleCommand  string `default:"ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -f mp3 -"`
+	ProbeCommand       string `default:"ffmpeg %s -f ffmetadata"`
+	ScanInterval       string `default:"1m"`
 
 	// DevFlags. These are used to enable/disable debugging and incomplete features
 	DevDisableBanner bool `default:"false"`
diff --git a/engine/engine_suite_test.go b/engine/engine_suite_test.go
index 72ec93112..bd2e50649 100644
--- a/engine/engine_suite_test.go
+++ b/engine/engine_suite_test.go
@@ -4,11 +4,13 @@ import (
 	"testing"
 
 	"github.com/deluan/navidrome/log"
+	"github.com/deluan/navidrome/tests"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 )
 
 func TestEngine(t *testing.T) {
+	tests.Init(t, false)
 	log.SetLevel(log.LevelCritical)
 	RegisterFailHandler(Fail)
 	RunSpecs(t, "Engine Suite")
diff --git a/engine/media_streamer.go b/engine/media_streamer.go
new file mode 100644
index 000000000..4c04bba52
--- /dev/null
+++ b/engine/media_streamer.go
@@ -0,0 +1,205 @@
+package engine
+
+import (
+	"context"
+	"io"
+	"io/ioutil"
+	"mime"
+	"os"
+	"os/exec"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/deluan/navidrome/conf"
+	"github.com/deluan/navidrome/log"
+	"github.com/deluan/navidrome/model"
+	"github.com/deluan/navidrome/utils"
+)
+
+type MediaStreamer interface {
+	NewStream(ctx context.Context, id string, maxBitRate int, format string) (mediaStream, error)
+}
+
+func NewMediaStreamer(ds model.DataStore) MediaStreamer {
+	return &mediaStreamer{ds: ds}
+}
+
+type mediaStream interface {
+	io.ReadSeeker
+	ContentType() string
+	Name() string
+	ModTime() time.Time
+	Close() error
+}
+
+type mediaStreamer struct {
+	ds model.DataStore
+}
+
+func (ms *mediaStreamer) NewStream(ctx context.Context, id string, maxBitRate int, format string) (mediaStream, error) {
+	mf, err := ms.ds.MediaFile(ctx).Get(id)
+	if err != nil {
+		return nil, err
+	}
+
+	var bitRate int
+
+	if format == "raw" || !conf.Server.EnableDownsampling {
+		bitRate = mf.BitRate
+		format = mf.Suffix
+	} else {
+		if maxBitRate == 0 {
+			bitRate = mf.BitRate
+		} else {
+			bitRate = utils.MinInt(mf.BitRate, maxBitRate)
+		}
+		format = mf.Suffix
+	}
+	if conf.Server.MaxBitRate != 0 {
+		bitRate = utils.MinInt(bitRate, conf.Server.MaxBitRate)
+	}
+
+	var stream mediaStream
+
+	if bitRate == mf.BitRate && mime.TypeByExtension("."+format) == mf.ContentType() {
+		log.Debug(ctx, "Streaming raw file", "id", mf.ID, "path", mf.Path,
+			"originalBitrate", mf.BitRate, "originalFormat", mf.Suffix)
+
+		f, err := os.Open(mf.Path)
+		if err != nil {
+			return nil, err
+		}
+		stream = &rawMediaStream{ctx: ctx, mf: mf, file: f}
+		return stream, nil
+	}
+
+	log.Debug(ctx, "Streaming transcoded file", "id", mf.ID, "path", mf.Path,
+		"requestBitrate", bitRate, "requestFormat", format,
+		"originalBitrate", mf.BitRate, "originalFormat", mf.Suffix)
+
+	f := &transcodedMediaStream{ctx: ctx, mf: mf, bitRate: bitRate, format: format}
+	return f, err
+}
+
+type rawMediaStream struct {
+	file *os.File
+	ctx  context.Context
+	mf   *model.MediaFile
+}
+
+func (m *rawMediaStream) Read(p []byte) (n int, err error) {
+	return m.file.Read(p)
+}
+
+func (m *rawMediaStream) Seek(offset int64, whence int) (int64, error) {
+	return m.file.Seek(offset, whence)
+}
+
+func (m *rawMediaStream) ContentType() string {
+	return m.mf.ContentType()
+}
+
+func (m *rawMediaStream) Name() string {
+	return m.mf.Path
+}
+
+func (m *rawMediaStream) ModTime() time.Time {
+	return m.mf.UpdatedAt
+}
+
+func (m *rawMediaStream) Close() error {
+	log.Trace(m.ctx, "Closing file", "id", m.mf.ID, "path", m.mf.Path)
+	return m.file.Close()
+}
+
+type transcodedMediaStream struct {
+	ctx     context.Context
+	mf      *model.MediaFile
+	pipe    io.ReadCloser
+	bitRate int
+	format  string
+	skip    int64
+}
+
+func (m *transcodedMediaStream) Read(p []byte) (n int, err error) {
+	if m.pipe == nil {
+		m.pipe, err = newTranscode(m.ctx, m.mf.Path, m.bitRate, m.format)
+		if err != nil {
+			return 0, err
+		}
+		if m.skip > 0 {
+			_, err := io.CopyN(ioutil.Discard, m.pipe, m.skip)
+			if err != nil {
+				return 0, err
+			}
+		}
+	}
+	n, err = m.pipe.Read(p)
+	if err == io.EOF {
+		m.Close()
+	}
+	return
+}
+
+// This Seek function assumes internal details of http.ServeContent's implementation
+// A better approach would be to implement a http.FileSystem and use http.FileServer
+func (m *transcodedMediaStream) Seek(offset int64, whence int) (int64, error) {
+	if whence == io.SeekEnd {
+		if offset == 0 {
+			size := (m.mf.Duration) * m.bitRate * 1000
+			return int64(size / 8), nil
+		}
+		panic("seeking stream backwards not supported")
+	}
+	m.skip = offset
+	var err error
+	if m.pipe != nil {
+		err = m.Close()
+	}
+	return offset, err
+}
+
+func (m *transcodedMediaStream) ContentType() string {
+	return mime.TypeByExtension(".mp3")
+}
+
+func (m *transcodedMediaStream) Name() string {
+	return m.mf.Path
+}
+
+func (m *transcodedMediaStream) ModTime() time.Time {
+	return m.mf.UpdatedAt
+}
+
+func (m *transcodedMediaStream) Close() error {
+	log.Trace(m.ctx, "Closing stream", "id", m.mf.ID, "path", m.mf.Path)
+	err := m.pipe.Close()
+	m.pipe = nil
+	return err
+}
+
+func newTranscode(ctx context.Context, path string, maxBitRate int, format string) (f io.ReadCloser, err error) {
+	cmdLine, args := createTranscodeCommand(path, maxBitRate, format)
+
+	log.Trace(ctx, "Executing ffmpeg command", "arg0", cmdLine, "args", args)
+	cmd := exec.Command(cmdLine, args...)
+	cmd.Stderr = os.Stderr
+	if f, err = cmd.StdoutPipe(); err != nil {
+		return f, err
+	}
+	return f, cmd.Start()
+}
+
+func createTranscodeCommand(path string, maxBitRate int, format string) (string, []string) {
+	cmd := conf.Server.DownsampleCommand
+
+	split := strings.Split(cmd, " ")
+	for i, s := range split {
+		s = strings.Replace(s, "%s", path, -1)
+		s = strings.Replace(s, "%b", strconv.Itoa(maxBitRate), -1)
+		split[i] = s
+	}
+
+	return split[0], split[1:]
+}
diff --git a/engine/media_streamer_test.go b/engine/media_streamer_test.go
new file mode 100644
index 000000000..92330e8c5
--- /dev/null
+++ b/engine/media_streamer_test.go
@@ -0,0 +1,68 @@
+package engine
+
+import (
+	"os"
+	"time"
+
+	"github.com/deluan/navidrome/conf"
+	"github.com/deluan/navidrome/log"
+	"github.com/deluan/navidrome/model"
+	"github.com/deluan/navidrome/persistence"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("MediaStreamer", func() {
+
+	var streamer MediaStreamer
+	var ds model.DataStore
+	ctx := log.NewContext(nil)
+
+	BeforeEach(func() {
+		conf.Server.EnableDownsampling = true
+		ds = &persistence.MockDataStore{}
+		ds.MediaFile(ctx).(*persistence.MockMediaFile).SetData(`[{"id": "123", "path": "tests/fixtures/test.mp3", "bitRate": 128}]`, 1)
+		streamer = NewMediaStreamer(ds)
+	})
+
+	Context("NewStream", func() {
+		It("returns a rawMediaStream if format is 'raw'", func() {
+			Expect(streamer.NewStream(ctx, "123", 0, "raw")).To(BeAssignableToTypeOf(&rawMediaStream{}))
+		})
+		It("returns a rawMediaStream if maxBitRate is 0", func() {
+			Expect(streamer.NewStream(ctx, "123", 0, "mp3")).To(BeAssignableToTypeOf(&rawMediaStream{}))
+		})
+		It("returns a rawMediaStream if maxBitRate is higher than file bitRate", func() {
+			Expect(streamer.NewStream(ctx, "123", 256, "mp3")).To(BeAssignableToTypeOf(&rawMediaStream{}))
+		})
+		It("returns a transcodedMediaStream if maxBitRate is lower than file bitRate", func() {
+			s, err := streamer.NewStream(ctx, "123", 64, "mp3")
+			Expect(err).To(BeNil())
+			Expect(s).To(BeAssignableToTypeOf(&transcodedMediaStream{}))
+			Expect(s.(*transcodedMediaStream).bitRate).To(Equal(64))
+		})
+	})
+
+	Context("rawMediaStream", func() {
+		var rawStream mediaStream
+		var modTime time.Time
+
+		BeforeEach(func() {
+			modTime = time.Now()
+			mf := &model.MediaFile{ID: "123", Path: "test.mp3", UpdatedAt: modTime, Suffix: "mp3"}
+			file, err := os.Open("tests/fixtures/test.mp3")
+			if err != nil {
+				panic(err)
+			}
+			rawStream = &rawMediaStream{mf: mf, file: file, ctx: ctx}
+		})
+
+		It("returns the ContentType", func() {
+			Expect(rawStream.ContentType()).To(Equal("audio/mpeg"))
+		})
+
+		It("returns the ModTime", func() {
+			Expect(rawStream.ModTime()).To(Equal(modTime))
+		})
+	})
+})
diff --git a/engine/stream.go b/engine/stream.go
deleted file mode 100644
index fa12c9a72..000000000
--- a/engine/stream.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package engine
-
-import (
-	"context"
-	"io"
-	"os"
-	"os/exec"
-	"strconv"
-	"strings"
-
-	"github.com/deluan/navidrome/conf"
-	"github.com/deluan/navidrome/log"
-)
-
-// TODO Encapsulate as a io.Reader
-func Stream(ctx context.Context, path string, bitRate int, maxBitRate int, w io.Writer) error {
-	var f io.Reader
-	var err error
-	enabled := !conf.Server.DisableDownsampling
-	if enabled && maxBitRate > 0 && bitRate > maxBitRate {
-		f, err = downsample(ctx, path, maxBitRate)
-	} else {
-		f, err = os.Open(path)
-	}
-	if err != nil {
-		log.Error(ctx, "Error opening file", "path", path, err)
-		return err
-	}
-	if _, err = io.Copy(w, f); err != nil {
-		log.Error(ctx, "Error copying file", "path", path, err)
-		return err
-	}
-	return err
-}
-
-func downsample(ctx context.Context, path string, maxBitRate int) (f io.Reader, err error) {
-	cmdLine, args := createDownsamplingCommand(path, maxBitRate)
-
-	log.Debug(ctx, "Executing command", "cmdLine", cmdLine, "args", args)
-	cmd := exec.Command(cmdLine, args...)
-	cmd.Stderr = os.Stderr
-	if f, err = cmd.StdoutPipe(); err != nil {
-		return f, err
-	}
-	return f, cmd.Start()
-}
-
-func createDownsamplingCommand(path string, maxBitRate int) (string, []string) {
-	cmd := conf.Server.DownsampleCommand
-
-	split := strings.Split(cmd, " ")
-	for i, s := range split {
-		s = strings.Replace(s, "%s", path, -1)
-		s = strings.Replace(s, "%b", strconv.Itoa(maxBitRate), -1)
-		split[i] = s
-	}
-
-	return split[0], split[1:]
-}
diff --git a/engine/stream_test.go b/engine/stream_test.go
deleted file mode 100644
index 953b8ecb4..000000000
--- a/engine/stream_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package engine
-
-import (
-	"testing"
-
-	. "github.com/deluan/navidrome/tests"
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-func TestDownsampling(t *testing.T) {
-
-	Init(t, false)
-
-	Convey("Subject: createDownsamplingCommand", t, func() {
-
-		Convey("It should create a valid command line", func() {
-			cmd, args := createDownsamplingCommand("/music library/file.mp3", 128)
-
-			So(cmd, ShouldEqual, "ffmpeg")
-			So(args[0], ShouldEqual, "-i")
-			So(args[1], ShouldEqual, "/music library/file.mp3")
-			So(args[2], ShouldEqual, "-b:a")
-			So(args[3], ShouldEqual, "128k")
-			So(args[4], ShouldEqual, "mp3")
-			So(args[5], ShouldEqual, "-")
-		})
-
-	})
-
-}
diff --git a/engine/wire_providers.go b/engine/wire_providers.go
index caf85a03d..6b570b9d7 100644
--- a/engine/wire_providers.go
+++ b/engine/wire_providers.go
@@ -12,4 +12,5 @@ var Set = wire.NewSet(
 	NewSearch,
 	NewNowPlayingRepository,
 	NewUsers,
+	NewMediaStreamer,
 )
diff --git a/server/subsonic/api.go b/server/subsonic/api.go
index 421e9d5b8..1baa8b633 100644
--- a/server/subsonic/api.go
+++ b/server/subsonic/api.go
@@ -25,15 +25,17 @@ type Router struct {
 	Scrobbler     engine.Scrobbler
 	Search        engine.Search
 	Users         engine.Users
+	Streamer      engine.MediaStreamer
 
 	mux http.Handler
 }
 
 func New(browser engine.Browser, cover engine.Cover, listGenerator engine.ListGenerator, users engine.Users,
-	playlists engine.Playlists, ratings engine.Ratings, scrobbler engine.Scrobbler, search engine.Search) *Router {
+	playlists engine.Playlists, ratings engine.Ratings, scrobbler engine.Scrobbler, search engine.Search,
+	streamer engine.MediaStreamer) *Router {
 
 	r := &Router{Browser: browser, Cover: cover, ListGenerator: listGenerator, Playlists: playlists,
-		Ratings: ratings, Scrobbler: scrobbler, Search: search, Users: users}
+		Ratings: ratings, Scrobbler: scrobbler, Search: search, Users: users, Streamer: streamer}
 	r.mux = r.routes()
 	return r
 }
diff --git a/server/subsonic/stream.go b/server/subsonic/stream.go
index d1580f6e9..1682a9cde 100644
--- a/server/subsonic/stream.go
+++ b/server/subsonic/stream.go
@@ -2,92 +2,51 @@ package subsonic
 
 import (
 	"net/http"
-	"strconv"
 
 	"github.com/deluan/navidrome/engine"
-	"github.com/deluan/navidrome/log"
-	"github.com/deluan/navidrome/model"
 	"github.com/deluan/navidrome/server/subsonic/responses"
-	"github.com/deluan/navidrome/utils"
 )
 
 type StreamController struct {
-	browser engine.Browser
+	streamer engine.MediaStreamer
 }
 
-func NewStreamController(browser engine.Browser) *StreamController {
-	return &StreamController{browser: browser}
+func NewStreamController(streamer engine.MediaStreamer) *StreamController {
+	return &StreamController{streamer: streamer}
 }
 
-func (c *StreamController) getMediaFile(r *http.Request) (mf *engine.Entry, err error) {
+func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
+	id, err := RequiredParamString(r, "id", "id parameter required")
+	if err != nil {
+		return nil, err
+	}
+	maxBitRate := ParamInt(r, "maxBitRate", 0)
+	format := ParamString(r, "format")
+
+	ms, err := c.streamer.NewStream(r.Context(), id, maxBitRate, format)
+	if err != nil {
+		return nil, err
+	}
+
+	// Override Content-Type detected by http.FileServer
+	w.Header().Set("Content-Type", ms.ContentType())
+	http.ServeContent(w, r, ms.Name(), ms.ModTime(), ms)
+	return nil, nil
+}
+
+func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
 	id, err := RequiredParamString(r, "id", "id parameter required")
 	if err != nil {
 		return nil, err
 	}
 
-	mf, err = c.browser.GetSong(r.Context(), id)
-	switch {
-	case err == model.ErrNotFound:
-		log.Error(r, "Mediafile not found", "id", id)
-		return nil, NewError(responses.ErrorDataNotFound)
-	case err != nil:
-		log.Error(r, "Error reading mediafile from DB", "id", id, err)
-		return nil, NewError(responses.ErrorGeneric, "Internal error")
-	}
-	return
-}
-
-// TODO Still getting the "Conn.Write wrote more than the declared Content-Length" error.
-// Don't know if this causes any issues
-func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
-	mf, err := c.getMediaFile(r)
+	ms, err := c.streamer.NewStream(r.Context(), id, 0, "raw")
 	if err != nil {
 		return nil, err
 	}
-	maxBitRate := ParamInt(r, "maxBitRate", 0)
-	maxBitRate = utils.MinInt(mf.BitRate, maxBitRate)
-
-	log.Debug(r, "Streaming file", "id", mf.Id, "path", mf.AbsolutePath, "bitrate", mf.BitRate, "maxBitRate", maxBitRate)
-
-	// TODO Send proper estimated content-length
-	//contentLength := mf.Size
-	//if maxBitRate > 0 {
-	//	contentLength = strconv.Itoa((mf.Duration + 1) * maxBitRate * 1000 / 8)
-	//}
-	h := w.Header()
-	h.Set("Content-Length", strconv.Itoa(mf.Size))
-	h.Set("Content-Type", "audio/mpeg")
-	h.Set("Expires", "0")
-	h.Set("Cache-Control", "must-revalidate")
-	h.Set("Pragma", "public")
-
-	if r.Method == "HEAD" {
-		log.Debug(r, "Just a HEAD. Not streaming", "path", mf.AbsolutePath)
-		return nil, nil
-	}
-
-	err = engine.Stream(r.Context(), mf.AbsolutePath, mf.BitRate, maxBitRate, w)
-	if err != nil {
-		log.Error(r, "Error streaming file", "id", mf.Id, err)
-	}
-
-	log.Debug(r, "Finished streaming", "path", mf.AbsolutePath)
-	return nil, nil
-}
-
-func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
-	mf, err := c.getMediaFile(r)
-	if err != nil {
-		return nil, err
-	}
-	log.Debug(r, "Sending file", "path", mf.AbsolutePath)
-
-	err = engine.Stream(r.Context(), mf.AbsolutePath, 0, 0, w)
-	if err != nil {
-		log.Error(r, "Error downloading file", "path", mf.AbsolutePath, err)
-	}
-
-	log.Debug(r, "Finished sending", "path", mf.AbsolutePath)
 
+	// Override Content-Type detected by http.FileServer
+	w.Header().Set("Content-Type", ms.ContentType())
+	http.ServeContent(w, r, ms.Name(), ms.ModTime(), ms)
 	return nil, nil
 }
diff --git a/server/subsonic/wire_gen.go b/server/subsonic/wire_gen.go
index 28d05923f..d67eb43de 100644
--- a/server/subsonic/wire_gen.go
+++ b/server/subsonic/wire_gen.go
@@ -59,8 +59,8 @@ func initMediaRetrievalController(router *Router) *MediaRetrievalController {
 }
 
 func initStreamController(router *Router) *StreamController {
-	browser := router.Browser
-	streamController := NewStreamController(browser)
+	mediaStreamer := router.Streamer
+	streamController := NewStreamController(mediaStreamer)
 	return streamController
 }
 
@@ -75,5 +75,5 @@ var allProviders = wire.NewSet(
 	NewSearchingController,
 	NewUsersController,
 	NewMediaRetrievalController,
-	NewStreamController, wire.FieldsOf(new(*Router), "Browser", "Cover", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search"),
+	NewStreamController, wire.FieldsOf(new(*Router), "Browser", "Cover", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search", "Streamer"),
 )
diff --git a/server/subsonic/wire_injectors.go b/server/subsonic/wire_injectors.go
index e39dde24e..7956b50df 100644
--- a/server/subsonic/wire_injectors.go
+++ b/server/subsonic/wire_injectors.go
@@ -16,7 +16,7 @@ var allProviders = wire.NewSet(
 	NewUsersController,
 	NewMediaRetrievalController,
 	NewStreamController,
-	wire.FieldsOf(new(*Router), "Browser", "Cover", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search"),
+	wire.FieldsOf(new(*Router), "Browser", "Cover", "ListGenerator", "Playlists", "Ratings", "Scrobbler", "Search", "Streamer"),
 )
 
 func initSystemController(router *Router) *SystemController {
diff --git a/wire_gen.go b/wire_gen.go
index 53be3ff70..c6a7e877a 100644
--- a/wire_gen.go
+++ b/wire_gen.go
@@ -41,7 +41,8 @@ func CreateSubsonicAPIRouter() *subsonic.Router {
 	ratings := engine.NewRatings(dataStore)
 	scrobbler := engine.NewScrobbler(dataStore, nowPlayingRepository)
 	search := engine.NewSearch(dataStore)
-	router := subsonic.New(browser, cover, listGenerator, users, playlists, ratings, scrobbler, search)
+	mediaStreamer := engine.NewMediaStreamer(dataStore)
+	router := subsonic.New(browser, cover, listGenerator, users, playlists, ratings, scrobbler, search, mediaStreamer)
 	return router
 }