mirror of https://github.com/stashapp/stash.git
Scan video orientation (#5189)
* Adjust video dimensions for side data rotation * Warn user when ffprobe version < 5. Only get rotation data on version >= 5
This commit is contained in:
parent
899ee713ab
commit
c21ded028a
|
@ -18,7 +18,7 @@ func customUsage() {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPhash(ff *ffmpeg.FFMpeg, ffp ffmpeg.FFProbe, inputfile string, quiet *bool) error {
|
func printPhash(ff *ffmpeg.FFMpeg, ffp *ffmpeg.FFProbe, inputfile string, quiet *bool) error {
|
||||||
ffvideoFile, err := ffp.NewVideoFile(inputfile)
|
ffvideoFile, err := ffp.NewVideoFile(inputfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -80,7 +80,7 @@ func main() {
|
||||||
ffmpegPath, ffprobePath := getPaths()
|
ffmpegPath, ffprobePath := getPaths()
|
||||||
encoder := ffmpeg.NewEncoder(ffmpegPath)
|
encoder := ffmpeg.NewEncoder(ffmpegPath)
|
||||||
// don't need to InitHWSupport, phashing doesn't use hw acceleration
|
// don't need to InitHWSupport, phashing doesn't use hw acceleration
|
||||||
ffprobe := ffmpeg.FFProbe(ffprobePath)
|
ffprobe := ffmpeg.NewFFProbe(ffprobePath)
|
||||||
|
|
||||||
for _, item := range args {
|
for _, item := range args {
|
||||||
if err := printPhash(encoder, ffprobe, item, quiet); err != nil {
|
if err := printPhash(encoder, ffprobe, item, quiet); err != nil {
|
||||||
|
|
|
@ -311,7 +311,7 @@ func (s *Manager) RefreshFFMpeg(ctx context.Context) {
|
||||||
logger.Debugf("using ffprobe: %s", ffprobePath)
|
logger.Debugf("using ffprobe: %s", ffprobePath)
|
||||||
|
|
||||||
s.FFMpeg = ffmpeg.NewEncoder(ffmpegPath)
|
s.FFMpeg = ffmpeg.NewEncoder(ffmpegPath)
|
||||||
s.FFProbe = ffmpeg.FFProbe(ffprobePath)
|
s.FFProbe = ffmpeg.NewFFProbe(ffprobePath)
|
||||||
|
|
||||||
s.FFMpeg.InitHWSupport(ctx)
|
s.FFMpeg.InitHWSupport(ctx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ type Manager struct {
|
||||||
Paths *paths.Paths
|
Paths *paths.Paths
|
||||||
|
|
||||||
FFMpeg *ffmpeg.FFMpeg
|
FFMpeg *ffmpeg.FFMpeg
|
||||||
FFProbe ffmpeg.FFProbe
|
FFProbe *ffmpeg.FFProbe
|
||||||
StreamManager *ffmpeg.StreamManager
|
StreamManager *ffmpeg.StreamManager
|
||||||
|
|
||||||
JobManager *job.Manager
|
JobManager *job.Manager
|
||||||
|
@ -300,7 +300,7 @@ func (s *Manager) Setup(ctx context.Context, input SetupInput) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Manager) validateFFmpeg() error {
|
func (s *Manager) validateFFmpeg() error {
|
||||||
if s.FFMpeg == nil || s.FFProbe == "" {
|
if s.FFMpeg == nil || s.FFProbe == nil {
|
||||||
return errors.New("missing ffmpeg and/or ffprobe")
|
return errors.New("missing ffmpeg and/or ffprobe")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -400,7 +400,7 @@ func (s *Manager) GetSystemStatus() *SystemStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
ffprobePath := ""
|
ffprobePath := ""
|
||||||
if s.FFProbe != "" {
|
if s.FFProbe != nil {
|
||||||
ffprobePath = s.FFProbe.Path()
|
ffprobePath = s.FFProbe.Path()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -277,15 +277,15 @@ func (f *FFMpeg) hwCodecFilter(args VideoFilter, codec VideoCodec, vf *models.Vi
|
||||||
func (f *FFMpeg) hwApplyFullHWFilter(args VideoFilter, codec VideoCodec, fullhw bool) VideoFilter {
|
func (f *FFMpeg) hwApplyFullHWFilter(args VideoFilter, codec VideoCodec, fullhw bool) VideoFilter {
|
||||||
switch codec {
|
switch codec {
|
||||||
case VideoCodecN264, VideoCodecN264H:
|
case VideoCodecN264, VideoCodecN264H:
|
||||||
if fullhw && f.version.Gteq(FFMpegVersion{major: 5}) { // Added in FFMpeg 5
|
if fullhw && f.version.Gteq(Version{major: 5}) { // Added in FFMpeg 5
|
||||||
args = args.Append("scale_cuda=format=yuv420p")
|
args = args.Append("scale_cuda=format=yuv420p")
|
||||||
}
|
}
|
||||||
case VideoCodecV264, VideoCodecVVP9:
|
case VideoCodecV264, VideoCodecVVP9:
|
||||||
if fullhw && f.version.Gteq(FFMpegVersion{major: 3, minor: 1}) { // Added in FFMpeg 3.1
|
if fullhw && f.version.Gteq(Version{major: 3, minor: 1}) { // Added in FFMpeg 3.1
|
||||||
args = args.Append("scale_vaapi=format=nv12")
|
args = args.Append("scale_vaapi=format=nv12")
|
||||||
}
|
}
|
||||||
case VideoCodecI264, VideoCodecI264C, VideoCodecIVP9:
|
case VideoCodecI264, VideoCodecI264C, VideoCodecIVP9:
|
||||||
if fullhw && f.version.Gteq(FFMpegVersion{major: 3, minor: 3}) { // Added in FFMpeg 3.3
|
if fullhw && f.version.Gteq(Version{major: 3, minor: 3}) { // Added in FFMpeg 3.3
|
||||||
args = args.Append("scale_qsv=format=nv12")
|
args = args.Append("scale_qsv=format=nv12")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,17 +300,17 @@ func (f *FFMpeg) hwApplyScaleTemplate(sargs string, codec VideoCodec, match []in
|
||||||
switch codec {
|
switch codec {
|
||||||
case VideoCodecN264, VideoCodecN264H:
|
case VideoCodecN264, VideoCodecN264H:
|
||||||
template = "scale_cuda=$value"
|
template = "scale_cuda=$value"
|
||||||
if fullhw && f.version.Gteq(FFMpegVersion{major: 5}) { // Added in FFMpeg 5
|
if fullhw && f.version.Gteq(Version{major: 5}) { // Added in FFMpeg 5
|
||||||
template += ":format=yuv420p"
|
template += ":format=yuv420p"
|
||||||
}
|
}
|
||||||
case VideoCodecV264, VideoCodecVVP9:
|
case VideoCodecV264, VideoCodecVVP9:
|
||||||
template = "scale_vaapi=$value"
|
template = "scale_vaapi=$value"
|
||||||
if fullhw && f.version.Gteq(FFMpegVersion{major: 3, minor: 1}) { // Added in FFMpeg 3.1
|
if fullhw && f.version.Gteq(Version{major: 3, minor: 1}) { // Added in FFMpeg 3.1
|
||||||
template += ":format=nv12"
|
template += ":format=nv12"
|
||||||
}
|
}
|
||||||
case VideoCodecI264, VideoCodecI264C, VideoCodecIVP9:
|
case VideoCodecI264, VideoCodecI264C, VideoCodecIVP9:
|
||||||
template = "scale_qsv=$value"
|
template = "scale_qsv=$value"
|
||||||
if fullhw && f.version.Gteq(FFMpegVersion{major: 3, minor: 3}) { // Added in FFMpeg 3.3
|
if fullhw && f.version.Gteq(Version{major: 3, minor: 3}) { // Added in FFMpeg 3.3
|
||||||
template += ":format=nv12"
|
template += ":format=nv12"
|
||||||
}
|
}
|
||||||
case VideoCodecM264:
|
case VideoCodecM264:
|
||||||
|
|
|
@ -145,6 +145,8 @@ func ResolveFFMpeg(path string, fallbackPath string) string {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var version_re = regexp.MustCompile(`ffmpeg version n?((\d+)\.(\d+)(?:\.(\d+))?)`)
|
||||||
|
|
||||||
func (f *FFMpeg) getVersion() error {
|
func (f *FFMpeg) getVersion() error {
|
||||||
var args Args
|
var args Args
|
||||||
args = append(args, "-version")
|
args = append(args, "-version")
|
||||||
|
@ -158,7 +160,6 @@ func (f *FFMpeg) getVersion() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
version_re := regexp.MustCompile(`ffmpeg version n?((\d+)\.(\d+)(?:\.(\d+))?)`)
|
|
||||||
stdoutStr := stdout.String()
|
stdoutStr := stdout.String()
|
||||||
match := version_re.FindStringSubmatchIndex(stdoutStr)
|
match := version_re.FindStringSubmatchIndex(stdoutStr)
|
||||||
if match == nil {
|
if match == nil {
|
||||||
|
@ -183,20 +184,20 @@ func (f *FFMpeg) getVersion() error {
|
||||||
if i, err := strconv.Atoi(patchS); err == nil {
|
if i, err := strconv.Atoi(patchS); err == nil {
|
||||||
f.version.patch = i
|
f.version.patch = i
|
||||||
}
|
}
|
||||||
logger.Debugf("FFMpeg version %d.%d.%d detected", f.version.major, f.version.minor, f.version.patch)
|
logger.Debugf("FFMpeg version %s detected", f.version.String())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FFMpeg version params
|
// FFMpeg version params
|
||||||
type FFMpegVersion struct {
|
type Version struct {
|
||||||
major int
|
major int
|
||||||
minor int
|
minor int
|
||||||
patch int
|
patch int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gteq returns true if the version is greater than or equal to the other version.
|
// Gteq returns true if the version is greater than or equal to the other version.
|
||||||
func (v FFMpegVersion) Gteq(other FFMpegVersion) bool {
|
func (v Version) Gteq(other Version) bool {
|
||||||
if v.major > other.major {
|
if v.major > other.major {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -209,10 +210,14 @@ func (v FFMpegVersion) Gteq(other FFMpegVersion) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v Version) String() string {
|
||||||
|
return fmt.Sprintf("%d.%d.%d", v.major, v.minor, v.patch)
|
||||||
|
}
|
||||||
|
|
||||||
// FFMpeg provides an interface to ffmpeg.
|
// FFMpeg provides an interface to ffmpeg.
|
||||||
type FFMpeg struct {
|
type FFMpeg struct {
|
||||||
ffmpeg string
|
ffmpeg string
|
||||||
version FFMpegVersion
|
version Version
|
||||||
hwCodecSupport []VideoCodec
|
hwCodecSupport []VideoCodec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,62 +6,62 @@ import "testing"
|
||||||
func TestFFMpegVersion_GreaterThan(t *testing.T) {
|
func TestFFMpegVersion_GreaterThan(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
this FFMpegVersion
|
this Version
|
||||||
other FFMpegVersion
|
other Version
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"major greater, minor equal, patch equal",
|
"major greater, minor equal, patch equal",
|
||||||
FFMpegVersion{2, 0, 0},
|
Version{2, 0, 0},
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major greater, minor less, patch less",
|
"major greater, minor less, patch less",
|
||||||
FFMpegVersion{2, 1, 1},
|
Version{2, 1, 1},
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major equal, minor greater, patch equal",
|
"major equal, minor greater, patch equal",
|
||||||
FFMpegVersion{1, 1, 0},
|
Version{1, 1, 0},
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major equal, minor equal, patch greater",
|
"major equal, minor equal, patch greater",
|
||||||
FFMpegVersion{1, 0, 1},
|
Version{1, 0, 1},
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major equal, minor equal, patch equal",
|
"major equal, minor equal, patch equal",
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major less, minor equal, patch equal",
|
"major less, minor equal, patch equal",
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
FFMpegVersion{2, 0, 0},
|
Version{2, 0, 0},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major equal, minor less, patch equal",
|
"major equal, minor less, patch equal",
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
FFMpegVersion{1, 1, 0},
|
Version{1, 1, 0},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major equal, minor equal, patch less",
|
"major equal, minor equal, patch less",
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
FFMpegVersion{1, 0, 1},
|
Version{1, 0, 1},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"major less, minor less, patch less",
|
"major less, minor less, patch less",
|
||||||
FFMpegVersion{1, 0, 0},
|
Version{1, 0, 0},
|
||||||
FFMpegVersion{2, 1, 1},
|
Version{2, 1, 1},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package ffmpeg
|
package ffmpeg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -16,6 +18,8 @@ import (
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const minimumFFProbeVersion = 5
|
||||||
|
|
||||||
func ValidateFFProbe(ffprobePath string) error {
|
func ValidateFFProbe(ffprobePath string) error {
|
||||||
cmd := stashExec.Command(ffprobePath, "-h")
|
cmd := stashExec.Command(ffprobePath, "-h")
|
||||||
bytes, err := cmd.CombinedOutput()
|
bytes, err := cmd.CombinedOutput()
|
||||||
|
@ -139,16 +143,94 @@ func (v *VideoFile) TranscodeScale(maxSize int) (int, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FFProbe provides an interface to the ffprobe executable.
|
// FFProbe provides an interface to the ffprobe executable.
|
||||||
type FFProbe string
|
type FFProbe struct {
|
||||||
|
path string
|
||||||
|
version Version
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FFProbe) Path() string {
|
func (f *FFProbe) Path() string {
|
||||||
return string(*f)
|
return f.path
|
||||||
|
}
|
||||||
|
|
||||||
|
var ffprobeVersionRE = regexp.MustCompile(`ffprobe version n?((\d+)\.(\d+)(?:\.(\d+))?)`)
|
||||||
|
|
||||||
|
func (f *FFProbe) getVersion() error {
|
||||||
|
var args []string
|
||||||
|
args = append(args, "-version")
|
||||||
|
cmd := stashExec.Command(f.path, args...)
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if err = cmd.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdoutStr := stdout.String()
|
||||||
|
match := ffprobeVersionRE.FindStringSubmatchIndex(stdoutStr)
|
||||||
|
if match == nil {
|
||||||
|
return errors.New("version string malformed")
|
||||||
|
}
|
||||||
|
|
||||||
|
majorS := stdoutStr[match[4]:match[5]]
|
||||||
|
minorS := stdoutStr[match[6]:match[7]]
|
||||||
|
|
||||||
|
// patch is optional
|
||||||
|
var patchS string
|
||||||
|
if match[8] != -1 && match[9] != -1 {
|
||||||
|
patchS = stdoutStr[match[8]:match[9]]
|
||||||
|
}
|
||||||
|
|
||||||
|
if i, err := strconv.Atoi(majorS); err == nil {
|
||||||
|
f.version.major = i
|
||||||
|
}
|
||||||
|
if i, err := strconv.Atoi(minorS); err == nil {
|
||||||
|
f.version.minor = i
|
||||||
|
}
|
||||||
|
if i, err := strconv.Atoi(patchS); err == nil {
|
||||||
|
f.version.patch = i
|
||||||
|
}
|
||||||
|
logger.Debugf("FFProbe version %s detected", f.version.String())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new FFProbe instance.
|
||||||
|
func NewFFProbe(path string) *FFProbe {
|
||||||
|
ret := &FFProbe{
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
if err := ret.getVersion(); err != nil {
|
||||||
|
logger.Warnf("FFProbe version not detected %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.version.major != 0 && ret.version.major < minimumFFProbeVersion {
|
||||||
|
logger.Warnf("FFProbe version %d.%d.%d detected, but %d.x or later is required", ret.version.major, ret.version.minor, ret.version.patch, minimumFFProbeVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVideoFile runs ffprobe on the given path and returns a VideoFile.
|
// NewVideoFile runs ffprobe on the given path and returns a VideoFile.
|
||||||
func (f *FFProbe) NewVideoFile(videoPath string) (*VideoFile, error) {
|
func (f *FFProbe) NewVideoFile(videoPath string) (*VideoFile, error) {
|
||||||
args := []string{"-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "-show_error", videoPath}
|
args := []string{
|
||||||
cmd := stashExec.Command(string(*f), args...)
|
"-v",
|
||||||
|
"quiet",
|
||||||
|
"-print_format", "json",
|
||||||
|
"-show_format",
|
||||||
|
"-show_streams",
|
||||||
|
"-show_error",
|
||||||
|
}
|
||||||
|
|
||||||
|
// show_entries stream_side_data=rotation requires 5.x or later ffprobe
|
||||||
|
if f.version.major >= 5 {
|
||||||
|
args = append(args, "-show_entries", "stream_side_data=rotation")
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, videoPath)
|
||||||
|
|
||||||
|
cmd := stashExec.Command(f.path, args...)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -167,7 +249,7 @@ func (f *FFProbe) NewVideoFile(videoPath string) (*VideoFile, error) {
|
||||||
// Used when the frame count is missing or incorrect.
|
// Used when the frame count is missing or incorrect.
|
||||||
func (f *FFProbe) GetReadFrameCount(path string) (int64, error) {
|
func (f *FFProbe) GetReadFrameCount(path string) (int64, error) {
|
||||||
args := []string{"-v", "quiet", "-print_format", "json", "-count_frames", "-show_format", "-show_streams", "-show_error", path}
|
args := []string{"-v", "quiet", "-print_format", "json", "-count_frames", "-show_format", "-show_streams", "-show_error", path}
|
||||||
out, err := stashExec.Command(string(*f), args...).Output()
|
out, err := stashExec.Command(f.path, args...).Output()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("FFProbe encountered an error with <%s>.\nError JSON:\n%s\nError: %s", path, string(out), err.Error())
|
return 0, fmt.Errorf("FFProbe encountered an error with <%s>.\nError JSON:\n%s\nError: %s", path, string(out), err.Error())
|
||||||
|
@ -246,13 +328,14 @@ func parse(filePath string, probeJSON *FFProbeJSON) (*VideoFile, error) {
|
||||||
framerate = 0
|
framerate = 0
|
||||||
}
|
}
|
||||||
result.FrameRate = math.Round(framerate*100) / 100
|
result.FrameRate = math.Round(framerate*100) / 100
|
||||||
if rotate, err := strconv.ParseInt(videoStream.Tags.Rotate, 10, 64); err == nil && rotate != 180 {
|
result.Width = videoStream.Width
|
||||||
|
result.Height = videoStream.Height
|
||||||
|
|
||||||
|
if isRotated(videoStream) {
|
||||||
result.Width = videoStream.Height
|
result.Width = videoStream.Height
|
||||||
result.Height = videoStream.Width
|
result.Height = videoStream.Width
|
||||||
} else {
|
|
||||||
result.Width = videoStream.Width
|
|
||||||
result.Height = videoStream.Height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.VideoStreamDuration, err = strconv.ParseFloat(videoStream.Duration, 64)
|
result.VideoStreamDuration, err = strconv.ParseFloat(videoStream.Duration, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Revert to the historical behaviour, which is still correct in the vast majority of cases.
|
// Revert to the historical behaviour, which is still correct in the vast majority of cases.
|
||||||
|
@ -263,6 +346,25 @@ func parse(filePath string, probeJSON *FFProbeJSON) (*VideoFile, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isRotated(s *FFProbeStream) bool {
|
||||||
|
rotate, _ := strconv.ParseInt(s.Tags.Rotate, 10, 64)
|
||||||
|
if rotate != 180 && rotate != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sd := range s.SideDataList {
|
||||||
|
r := sd.Rotation
|
||||||
|
if r < 0 {
|
||||||
|
r = -r
|
||||||
|
}
|
||||||
|
if r != 0 && r != 180 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (v *VideoFile) getAudioStream() *FFProbeStream {
|
func (v *VideoFile) getAudioStream() *FFProbeStream {
|
||||||
index := v.getStreamIndex("audio", v.JSON)
|
index := v.getStreamIndex("audio", v.JSON)
|
||||||
if index != -1 {
|
if index != -1 {
|
||||||
|
|
|
@ -23,7 +23,7 @@ const (
|
||||||
type StreamManager struct {
|
type StreamManager struct {
|
||||||
cacheDir string
|
cacheDir string
|
||||||
encoder *FFMpeg
|
encoder *FFMpeg
|
||||||
ffprobe FFProbe
|
ffprobe *FFProbe
|
||||||
|
|
||||||
config StreamManagerConfig
|
config StreamManagerConfig
|
||||||
lockManager *fsutil.ReadLockManager
|
lockManager *fsutil.ReadLockManager
|
||||||
|
@ -42,7 +42,7 @@ type StreamManagerConfig interface {
|
||||||
GetTranscodeHardwareAcceleration() bool
|
GetTranscodeHardwareAcceleration() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStreamManager(cacheDir string, encoder *FFMpeg, ffprobe FFProbe, config StreamManagerConfig, lockManager *fsutil.ReadLockManager) *StreamManager {
|
func NewStreamManager(cacheDir string, encoder *FFMpeg, ffprobe *FFProbe, config StreamManagerConfig, lockManager *fsutil.ReadLockManager) *StreamManager {
|
||||||
if cacheDir == "" {
|
if cacheDir == "" {
|
||||||
logger.Warn("cache directory is not set. Live HLS/DASH transcoding will be disabled")
|
logger.Warn("cache directory is not set. Live HLS/DASH transcoding will be disabled")
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,4 +94,7 @@ type FFProbeStream struct {
|
||||||
MaxBitRate string `json:"max_bit_rate,omitempty"`
|
MaxBitRate string `json:"max_bit_rate,omitempty"`
|
||||||
SampleFmt string `json:"sample_fmt,omitempty"`
|
SampleFmt string `json:"sample_fmt,omitempty"`
|
||||||
SampleRate string `json:"sample_rate,omitempty"`
|
SampleRate string `json:"sample_rate,omitempty"`
|
||||||
|
SideDataList []struct {
|
||||||
|
Rotation int `json:"rotation"`
|
||||||
|
} `json:"side_data_list"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
|
|
||||||
// Decorator adds image specific fields to a File.
|
// Decorator adds image specific fields to a File.
|
||||||
type Decorator struct {
|
type Decorator struct {
|
||||||
FFProbe ffmpeg.FFProbe
|
FFProbe *ffmpeg.FFProbe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decorator) Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
func (d *Decorator) Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
||||||
|
|
|
@ -12,11 +12,11 @@ import (
|
||||||
|
|
||||||
// Decorator adds video specific fields to a File.
|
// Decorator adds video specific fields to a File.
|
||||||
type Decorator struct {
|
type Decorator struct {
|
||||||
FFProbe ffmpeg.FFProbe
|
FFProbe *ffmpeg.FFProbe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decorator) Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
func (d *Decorator) Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
||||||
if d.FFProbe == "" {
|
if d.FFProbe == nil {
|
||||||
return f, errors.New("ffprobe not configured")
|
return f, errors.New("ffprobe not configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ var (
|
||||||
|
|
||||||
type ThumbnailEncoder struct {
|
type ThumbnailEncoder struct {
|
||||||
FFMpeg *ffmpeg.FFMpeg
|
FFMpeg *ffmpeg.FFMpeg
|
||||||
FFProbe ffmpeg.FFProbe
|
FFProbe *ffmpeg.FFProbe
|
||||||
ClipPreviewOptions ClipPreviewOptions
|
ClipPreviewOptions ClipPreviewOptions
|
||||||
vips *vipsEncoder
|
vips *vipsEncoder
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ func GetVipsPath() string {
|
||||||
return vipsPath
|
return vipsPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewThumbnailEncoder(ffmpegEncoder *ffmpeg.FFMpeg, ffProbe ffmpeg.FFProbe, clipPreviewOptions ClipPreviewOptions) ThumbnailEncoder {
|
func NewThumbnailEncoder(ffmpegEncoder *ffmpeg.FFMpeg, ffProbe *ffmpeg.FFProbe, clipPreviewOptions ClipPreviewOptions) ThumbnailEncoder {
|
||||||
ret := ThumbnailEncoder{
|
ret := ThumbnailEncoder{
|
||||||
FFMpeg: ffmpegEncoder,
|
FFMpeg: ffmpegEncoder,
|
||||||
FFProbe: ffProbe,
|
FFProbe: ffProbe,
|
||||||
|
|
Loading…
Reference in New Issue