mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24: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:
167
pkg/scene/generate/transcode.go
Normal file
167
pkg/scene/generate/transcode.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/ffmpeg/transcoder"
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
)
|
||||
|
||||
type TranscodeOptions struct {
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
func (g Generator) Transcode(ctx context.Context, input string, hash string, options TranscodeOptions) error {
|
||||
lockCtx := g.LockManager.ReadLock(ctx, input)
|
||||
defer lockCtx.Cancel()
|
||||
|
||||
return g.makeTranscode(lockCtx, hash, g.transcode(input, options))
|
||||
}
|
||||
|
||||
// TranscodeVideo transcodes the video, and removes the audio.
|
||||
// In some videos where the audio codec is not supported by ffmpeg,
|
||||
// ffmpeg fails if you try to transcode the audio
|
||||
func (g Generator) TranscodeVideo(ctx context.Context, input string, hash string, options TranscodeOptions) error {
|
||||
lockCtx := g.LockManager.ReadLock(ctx, input)
|
||||
defer lockCtx.Cancel()
|
||||
|
||||
return g.makeTranscode(lockCtx, hash, g.transcodeVideo(input, options))
|
||||
}
|
||||
|
||||
// TranscodeAudio will copy the video stream as is, and transcode audio.
|
||||
func (g Generator) TranscodeAudio(ctx context.Context, input string, hash string, options TranscodeOptions) error {
|
||||
lockCtx := g.LockManager.ReadLock(ctx, input)
|
||||
defer lockCtx.Cancel()
|
||||
|
||||
return g.makeTranscode(lockCtx, hash, g.transcodeAudio(input, options))
|
||||
}
|
||||
|
||||
// TranscodeCopyVideo will copy the video stream as is, and drop the audio stream.
|
||||
func (g Generator) TranscodeCopyVideo(ctx context.Context, input string, hash string, options TranscodeOptions) error {
|
||||
lockCtx := g.LockManager.ReadLock(ctx, input)
|
||||
defer lockCtx.Cancel()
|
||||
|
||||
return g.makeTranscode(lockCtx, hash, g.transcodeCopyVideo(input, options))
|
||||
}
|
||||
|
||||
func (g Generator) makeTranscode(lockCtx *fsutil.LockContext, hash string, generateFn generateFn) error {
|
||||
output := g.ScenePaths.GetTranscodePath(hash)
|
||||
if !g.Overwrite {
|
||||
if exists, _ := fsutil.FileExists(output); exists {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := g.generateFile(lockCtx, g.ScenePaths, mp4Pattern, output, generateFn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Debug("created transcode: ", output)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g Generator) transcode(input string, options TranscodeOptions) generateFn {
|
||||
return func(lockCtx *fsutil.LockContext, tmpFn string) error {
|
||||
var videoArgs ffmpeg.Args
|
||||
if options.Width != 0 && options.Height != 0 {
|
||||
var videoFilter ffmpeg.VideoFilter
|
||||
videoFilter = videoFilter.ScaleDimensions(options.Width, options.Height)
|
||||
videoArgs = videoArgs.VideoFilter(videoFilter)
|
||||
}
|
||||
|
||||
videoArgs = append(videoArgs,
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-profile:v", "high",
|
||||
"-level", "4.2",
|
||||
"-preset", "superfast",
|
||||
"-crf", "23",
|
||||
)
|
||||
|
||||
args := transcoder.Transcode(input, transcoder.TranscodeOptions{
|
||||
OutputPath: tmpFn,
|
||||
VideoCodec: ffmpeg.VideoCodecLibX264,
|
||||
VideoArgs: videoArgs,
|
||||
AudioCodec: ffmpeg.AudioCodecAAC,
|
||||
})
|
||||
|
||||
return g.generate(lockCtx, args)
|
||||
}
|
||||
}
|
||||
|
||||
func (g Generator) transcodeVideo(input string, options TranscodeOptions) generateFn {
|
||||
return func(lockCtx *fsutil.LockContext, tmpFn string) error {
|
||||
var videoArgs ffmpeg.Args
|
||||
if options.Width != 0 && options.Height != 0 {
|
||||
var videoFilter ffmpeg.VideoFilter
|
||||
videoFilter = videoFilter.ScaleDimensions(options.Width, options.Height)
|
||||
videoArgs = videoArgs.VideoFilter(videoFilter)
|
||||
}
|
||||
|
||||
videoArgs = append(videoArgs,
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-profile:v", "high",
|
||||
"-level", "4.2",
|
||||
"-preset", "superfast",
|
||||
"-crf", "23",
|
||||
)
|
||||
|
||||
var audioArgs ffmpeg.Args
|
||||
audioArgs = audioArgs.SkipAudio()
|
||||
|
||||
args := transcoder.Transcode(input, transcoder.TranscodeOptions{
|
||||
OutputPath: tmpFn,
|
||||
VideoCodec: ffmpeg.VideoCodecLibX264,
|
||||
VideoArgs: videoArgs,
|
||||
AudioArgs: audioArgs,
|
||||
})
|
||||
|
||||
return g.generate(lockCtx, args)
|
||||
}
|
||||
}
|
||||
|
||||
func (g Generator) transcodeAudio(input string, options TranscodeOptions) generateFn {
|
||||
return func(lockCtx *fsutil.LockContext, tmpFn string) error {
|
||||
var videoArgs ffmpeg.Args
|
||||
if options.Width != 0 && options.Height != 0 {
|
||||
var videoFilter ffmpeg.VideoFilter
|
||||
videoFilter = videoFilter.ScaleDimensions(options.Width, options.Height)
|
||||
videoArgs = videoArgs.VideoFilter(videoFilter)
|
||||
}
|
||||
|
||||
args := transcoder.Transcode(input, transcoder.TranscodeOptions{
|
||||
OutputPath: tmpFn,
|
||||
VideoCodec: ffmpeg.VideoCodecCopy,
|
||||
VideoArgs: videoArgs,
|
||||
AudioCodec: ffmpeg.AudioCodecAAC,
|
||||
})
|
||||
|
||||
return g.generate(lockCtx, args)
|
||||
}
|
||||
}
|
||||
|
||||
func (g Generator) transcodeCopyVideo(input string, options TranscodeOptions) generateFn {
|
||||
return func(lockCtx *fsutil.LockContext, tmpFn string) error {
|
||||
var videoArgs ffmpeg.Args
|
||||
if options.Width != 0 && options.Height != 0 {
|
||||
var videoFilter ffmpeg.VideoFilter
|
||||
videoFilter = videoFilter.ScaleDimensions(options.Width, options.Height)
|
||||
videoArgs = videoArgs.VideoFilter(videoFilter)
|
||||
}
|
||||
|
||||
var audioArgs ffmpeg.Args
|
||||
audioArgs = audioArgs.SkipAudio()
|
||||
|
||||
args := transcoder.Transcode(input, transcoder.TranscodeOptions{
|
||||
OutputPath: tmpFn,
|
||||
VideoCodec: ffmpeg.VideoCodecCopy,
|
||||
VideoArgs: videoArgs,
|
||||
AudioArgs: audioArgs,
|
||||
})
|
||||
|
||||
return g.generate(lockCtx, args)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user