mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 12:54:38 +03:00
Restructure ffmpeg (#2392)
* Refactor transcode generation * Move phash generation into separate package * Refactor image thumbnail generation * Move JSONTime to separate package * Ffmpeg refactoring * Refactor live transcoding * Refactor scene marker preview generation * Refactor preview generation * Refactor screenshot generation * Refactor sprite generation * Change ffmpeg.IsStreamable to return error * Move frame rate calculation into ffmpeg * Refactor file locking * Refactor title set during scan * Add missing lockmanager instance * Return error instead of logging in MatchContainer
This commit is contained in:
136
pkg/ffmpeg/browser.go
Normal file
136
pkg/ffmpeg/browser.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package ffmpeg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// only support H264 by default, since Safari does not support VP8/VP9
|
||||
var defaultSupportedCodecs = []string{H264, H265}
|
||||
|
||||
var validForH264Mkv = []Container{Mp4, Matroska}
|
||||
var validForH264 = []Container{Mp4}
|
||||
var validForH265Mkv = []Container{Mp4, Matroska}
|
||||
var validForH265 = []Container{Mp4}
|
||||
var validForVp8 = []Container{Webm}
|
||||
var validForVp9Mkv = []Container{Webm, Matroska}
|
||||
var validForVp9 = []Container{Webm}
|
||||
var validForHevcMkv = []Container{Mp4, Matroska}
|
||||
var validForHevc = []Container{Mp4}
|
||||
|
||||
var validAudioForMkv = []ProbeAudioCodec{Aac, Mp3, Vorbis, Opus}
|
||||
var validAudioForWebm = []ProbeAudioCodec{Vorbis, Opus}
|
||||
var validAudioForMp4 = []ProbeAudioCodec{Aac, Mp3}
|
||||
|
||||
var (
|
||||
// ErrUnsupportedVideoCodecForBrowser is returned when the video codec is not supported for browser streaming.
|
||||
ErrUnsupportedVideoCodecForBrowser = errors.New("unsupported video codec for browser")
|
||||
|
||||
// ErrUnsupportedVideoCodecContainer is returned when the video codec/container combination is not supported for browser streaming.
|
||||
ErrUnsupportedVideoCodecContainer = errors.New("video codec/container combination is unsupported for browser streaming")
|
||||
|
||||
// ErrUnsupportedAudioCodecContainer is returned when the audio codec/container combination is not supported for browser streaming.
|
||||
ErrUnsupportedAudioCodecContainer = errors.New("audio codec/container combination is unsupported for browser streaming")
|
||||
)
|
||||
|
||||
// IsStreamable returns nil if the file is streamable, or an error if it is not.
|
||||
func IsStreamable(videoCodec string, audioCodec ProbeAudioCodec, container Container) error {
|
||||
supportedVideoCodecs := defaultSupportedCodecs
|
||||
|
||||
// check if the video codec matches the supported codecs
|
||||
if !isValidCodec(videoCodec, supportedVideoCodecs) {
|
||||
return fmt.Errorf("%w: %s", ErrUnsupportedVideoCodecForBrowser, videoCodec)
|
||||
}
|
||||
|
||||
if !isValidCombo(videoCodec, container, supportedVideoCodecs) {
|
||||
return fmt.Errorf("%w: %s/%s", ErrUnsupportedVideoCodecContainer, videoCodec, container)
|
||||
}
|
||||
|
||||
if !IsValidAudioForContainer(audioCodec, container) {
|
||||
return fmt.Errorf("%w: %s/%s", ErrUnsupportedAudioCodecContainer, audioCodec, container)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidCodec(codecName string, supportedCodecs []string) bool {
|
||||
for _, c := range supportedCodecs {
|
||||
if c == codecName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isValidAudio(audio ProbeAudioCodec, validCodecs []ProbeAudioCodec) bool {
|
||||
// if audio codec is missing or unsupported by ffmpeg we can't do anything about it
|
||||
// report it as valid so that the file can at least be streamed directly if the video codec is supported
|
||||
if audio == MissingUnsupported {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, c := range validCodecs {
|
||||
if c == audio {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValidAudioForContainer returns true if the audio codec is valid for the container.
|
||||
func IsValidAudioForContainer(audio ProbeAudioCodec, format Container) bool {
|
||||
switch format {
|
||||
case Matroska:
|
||||
return isValidAudio(audio, validAudioForMkv)
|
||||
case Webm:
|
||||
return isValidAudio(audio, validAudioForWebm)
|
||||
case Mp4:
|
||||
return isValidAudio(audio, validAudioForMp4)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isValidCombo checks if a codec/container combination is valid.
|
||||
// Returns true on validity, false otherwise
|
||||
func isValidCombo(codecName string, format Container, supportedVideoCodecs []string) bool {
|
||||
supportMKV := isValidCodec(Mkv, supportedVideoCodecs)
|
||||
supportHEVC := isValidCodec(Hevc, supportedVideoCodecs)
|
||||
|
||||
switch codecName {
|
||||
case H264:
|
||||
if supportMKV {
|
||||
return isValidForContainer(format, validForH264Mkv)
|
||||
}
|
||||
return isValidForContainer(format, validForH264)
|
||||
case H265:
|
||||
if supportMKV {
|
||||
return isValidForContainer(format, validForH265Mkv)
|
||||
}
|
||||
return isValidForContainer(format, validForH265)
|
||||
case Vp8:
|
||||
return isValidForContainer(format, validForVp8)
|
||||
case Vp9:
|
||||
if supportMKV {
|
||||
return isValidForContainer(format, validForVp9Mkv)
|
||||
}
|
||||
return isValidForContainer(format, validForVp9)
|
||||
case Hevc:
|
||||
if supportHEVC {
|
||||
if supportMKV {
|
||||
return isValidForContainer(format, validForHevcMkv)
|
||||
}
|
||||
return isValidForContainer(format, validForHevc)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isValidForContainer(format Container, validContainers []Container) bool {
|
||||
for _, fmt := range validContainers {
|
||||
if fmt == format {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user