mirror of
https://github.com/stashapp/stash.git
synced 2025-12-16 20:07:05 +03:00
Reorg
This commit is contained in:
152
pkg/ffmpeg/ffprobe.go
Normal file
152
pkg/ffmpeg/ffprobe.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package ffmpeg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ValidCodecs = []string{"h264", "h265", "vp8", "vp9"}
|
||||
|
||||
func IsValidCodec(codecName string) bool {
|
||||
for _, c := range ValidCodecs {
|
||||
if c == codecName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type VideoFile struct {
|
||||
JSON FFProbeJSON
|
||||
AudioStream *FFProbeStream
|
||||
VideoStream *FFProbeStream
|
||||
|
||||
Path string
|
||||
Container string
|
||||
Duration float64
|
||||
StartTime float64
|
||||
Bitrate int64
|
||||
Size int64
|
||||
CreationTime time.Time
|
||||
|
||||
VideoCodec string
|
||||
VideoBitrate int64
|
||||
Width int
|
||||
Height int
|
||||
FrameRate float64
|
||||
Rotation int64
|
||||
|
||||
AudioCodec string
|
||||
}
|
||||
|
||||
// Execute exec command and bind result to struct.
|
||||
func NewVideoFile(ffprobePath string, videoPath string) (*VideoFile, error) {
|
||||
args := []string{"-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", "-show_error", videoPath}
|
||||
//// Extremely slow on windows for some reason
|
||||
//if runtime.GOOS != "windows" {
|
||||
// args = append(args, "-count_frames")
|
||||
//}
|
||||
out, err := exec.Command(ffprobePath, args...).Output()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FFProbe encountered an error with <%s>.\nError JSON:\n%s\nError: %s", videoPath, string(out), err.Error())
|
||||
}
|
||||
|
||||
probeJSON := &FFProbeJSON{}
|
||||
if err := json.Unmarshal(out, probeJSON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parse(videoPath, probeJSON)
|
||||
}
|
||||
|
||||
func parse(filePath string, probeJSON *FFProbeJSON) (*VideoFile, error) {
|
||||
if probeJSON == nil {
|
||||
return nil, fmt.Errorf("failed to get ffprobe json")
|
||||
}
|
||||
|
||||
result := &VideoFile{}
|
||||
result.JSON = *probeJSON
|
||||
|
||||
if result.JSON.Error.Code != 0 {
|
||||
return nil, fmt.Errorf("ffprobe error code %d: %s", result.JSON.Error.Code, result.JSON.Error.String)
|
||||
}
|
||||
//} else if (ffprobeResult.stderr.includes("could not find codec parameters")) {
|
||||
// throw new Error(`FFProbe [${filePath}] -> Could not find codec parameters`);
|
||||
//} // TODO nil_or_unsupported.(video_stream) && nil_or_unsupported.(audio_stream)
|
||||
|
||||
result.Path = filePath
|
||||
|
||||
result.Bitrate, _ = strconv.ParseInt(probeJSON.Format.BitRate, 10, 64)
|
||||
result.Container = probeJSON.Format.FormatName
|
||||
duration, _ := strconv.ParseFloat(probeJSON.Format.Duration, 64)
|
||||
result.Duration = math.Round(duration*100) / 100
|
||||
fileStat, _ := os.Stat(filePath)
|
||||
result.Size = fileStat.Size()
|
||||
result.StartTime, _ = strconv.ParseFloat(probeJSON.Format.StartTime, 64)
|
||||
result.CreationTime = probeJSON.Format.Tags.CreationTime
|
||||
|
||||
audioStream := result.GetAudioStream()
|
||||
if audioStream != nil {
|
||||
result.AudioCodec = audioStream.CodecName
|
||||
result.AudioStream = audioStream
|
||||
}
|
||||
|
||||
videoStream := result.GetVideoStream()
|
||||
if videoStream != nil {
|
||||
result.VideoStream = videoStream
|
||||
result.VideoCodec = videoStream.CodecName
|
||||
result.VideoBitrate, _ = strconv.ParseInt(videoStream.BitRate, 10, 64)
|
||||
var framerate float64
|
||||
if strings.Contains(videoStream.AvgFrameRate, "/") {
|
||||
frameRateSplit := strings.Split(videoStream.AvgFrameRate, "/")
|
||||
numerator, _ := strconv.ParseFloat(frameRateSplit[0], 64)
|
||||
denominator, _ := strconv.ParseFloat(frameRateSplit[1], 64)
|
||||
framerate = numerator / denominator
|
||||
} else {
|
||||
framerate, _ = strconv.ParseFloat(videoStream.AvgFrameRate, 64)
|
||||
}
|
||||
result.FrameRate = math.Round(framerate*100) / 100
|
||||
if rotate, err := strconv.ParseInt(videoStream.Tags.Rotate, 10, 64); err == nil && rotate != 180 {
|
||||
result.Width = videoStream.Height
|
||||
result.Height = videoStream.Width
|
||||
} else {
|
||||
result.Width = videoStream.Width
|
||||
result.Height = videoStream.Height
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (v *VideoFile) GetAudioStream() *FFProbeStream {
|
||||
index := v.getStreamIndex("audio", v.JSON)
|
||||
if index != -1 {
|
||||
return &v.JSON.Streams[index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VideoFile) GetVideoStream() *FFProbeStream {
|
||||
index := v.getStreamIndex("video", v.JSON)
|
||||
if index != -1 {
|
||||
return &v.JSON.Streams[index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VideoFile) getStreamIndex(fileType string, probeJSON FFProbeJSON) int {
|
||||
for i, stream := range probeJSON.Streams {
|
||||
if stream.CodecType == fileType {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
Reference in New Issue
Block a user