Merge pull request #79 from WithoutPants/live_transcode

Add basic live transcoding for non-streamable files
This commit is contained in:
StashAppDev 2019-07-27 10:42:42 -07:00 committed by GitHub
commit eb5970b850
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 2 deletions

View File

@ -1,12 +1,14 @@
package api
import (
"io"
"context"
"github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/manager"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils"
"github.com/stashapp/stash/pkg/ffmpeg"
"net/http"
"strconv"
"strings"
@ -39,8 +41,49 @@ func (rs sceneRoutes) Routes() chi.Router {
func (rs sceneRoutes) Stream(w http.ResponseWriter, r *http.Request) {
scene := r.Context().Value(sceneKey).(*models.Scene)
// detect if not a streamable file and try to transcode it instead
filepath := manager.GetInstance().Paths.Scene.GetStreamPath(scene.Path, scene.Checksum)
http.ServeFile(w, r, filepath)
videoCodec := scene.VideoCodec.String
hasTranscode, _ := manager.HasTranscode(scene)
if ffmpeg.IsValidCodec(videoCodec) || hasTranscode {
http.ServeFile(w, r, filepath)
return
}
// needs to be transcoded
videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path)
if err != nil {
logger.Errorf("[stream] error reading video file: %s", err.Error())
return
}
encoder := ffmpeg.NewEncoder(manager.GetInstance().FFMPEGPath)
stream, process, err := encoder.StreamTranscode(*videoFile)
if err != nil {
logger.Errorf("[stream] error transcoding video file: %s", err.Error())
return
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "video/webm")
logger.Info("[stream] transcoding video file")
// handle if client closes the connection
notify := r.Context().Done()
go func() {
<-notify
logger.Info("[stream] client closed the connection. Killing stream process.")
process.Kill()
}()
_, err = io.Copy(w, stream)
if err != nil {
logger.Errorf("[stream] error serving transcoded video file: %s", err.Error())
}
}
func (rs sceneRoutes) Screenshot(w http.ResponseWriter, r *http.Request) {

View File

@ -1,10 +1,13 @@
package ffmpeg
import (
"github.com/stashapp/stash/pkg/logger"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/stashapp/stash/pkg/logger"
)
type Encoder struct {
@ -60,3 +63,18 @@ func (e *Encoder) run(probeResult VideoFile, args []string) (string, error) {
return stdoutString, nil
}
func (e *Encoder) stream(probeResult VideoFile, args []string) (io.ReadCloser, *os.Process, error) {
cmd := exec.Command(e.Path, args...)
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error("FFMPEG stdout not available: " + err.Error())
}
if err = cmd.Start(); err != nil {
return nil, nil, err
}
return stdout, cmd.Process, nil
}

View File

@ -1,5 +1,10 @@
package ffmpeg
import (
"io"
"os"
)
type TranscodeOptions struct {
OutputPath string
}
@ -20,3 +25,18 @@ func (e *Encoder) Transcode(probeResult VideoFile, options TranscodeOptions) {
}
_, _ = e.run(probeResult, args)
}
func (e *Encoder) StreamTranscode(probeResult VideoFile) (io.ReadCloser, *os.Process, error) {
args := []string{
"-i", probeResult.Path,
"-c:v", "libvpx-vp9",
"-vf", "scale=iw:-2",
"-deadline", "realtime",
"-cpu-used", "5",
"-crf", "30",
"-b:v", "0",
"-f", "webm",
"pipe:",
}
return e.stream(probeResult, args)
}