From 53adafbc40329442fd16121d457b56a0742111c9 Mon Sep 17 00:00:00 2001 From: Esa Kataja Date: Sat, 4 Apr 2026 18:08:25 +0300 Subject: [PATCH] add: add interlaced detection and bwdif deinterlacing --- cmd/videnc/main.go | 4 ++-- internal/encoder/encoder.go | 24 ++++++++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/cmd/videnc/main.go b/cmd/videnc/main.go index 416765a..a9e5963 100644 --- a/cmd/videnc/main.go +++ b/cmd/videnc/main.go @@ -82,7 +82,7 @@ func processFile(inputPath string, cfg *types.Config, enc *encoder.Encoder, meta filename := filepath.Base(inputPath) isSeries, imdbID, tvmazeID, season, episode := metadata.ParseFilename(filename) - width, height, err := enc.GetMediaInfo(inputPath) + width, height, interlaced, err := enc.GetMediaInfo(inputPath) if err != nil { log.ErrorFile(inputPath, "Getting media info", err.Error()) mover.MoveToFailed(inputPath, cfg.Paths.Failed) @@ -128,7 +128,7 @@ func processFile(inputPath string, cfg *types.Config, enc *encoder.Encoder, meta DeleteOrigin: deleteOrigin, } - if err := enc.Transcode(inputPath, job, meta); err != nil { + if err := enc.Transcode(inputPath, job, meta, interlaced); err != nil { log.ErrorFile(inputPath, "Transcoding", err.Error()) mover.MoveToFailed(inputPath, cfg.Paths.Failed) outputPath := filepath.Join(filepath.Dir(inputPath), "output.mkv") diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index d3f92e8..434e23f 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -33,21 +33,24 @@ func (e *Encoder) CheckDeps() error { return nil } -func (e *Encoder) GetMediaInfo(path string) (width, height int, err error) { - cmd := exec.Command(e.ffmpegPath, "-i", path, "-hide_banner") +func (e *Encoder) GetMediaInfo(path string) (width, height int, interlaced bool, err error) { + cmd := exec.Command(e.ffmpegPath, "-i", path, "-hide_banner", "-vf", "idet") output, _ := cmd.StderrPipe() cmd.Start() - buf := make([]byte, 8192) + buf := make([]byte, 16384) n, _ := output.Read(buf) output.Close() var w, h int fmt.Sscanf(string(buf[:n]), "%dx%d", &w, &h) - return w, h, nil + + interlaced = strings.Contains(string(buf[:n]), "TFF") || strings.Contains(string(buf[:n]), "BFF") + + return w, h, interlaced, nil } -func (e *Encoder) Transcode(input string, job *types.Job, metadata *types.Metadata) error { +func (e *Encoder) Transcode(input string, job *types.Job, metadata *types.Metadata, interlaced bool) error { dir := filepath.Dir(input) audioWavs, err := e.extractAudio(input, dir) if err != nil { @@ -61,7 +64,7 @@ func (e *Encoder) Transcode(input string, job *types.Job, metadata *types.Metada } defer e.cleanupOpus(opusFiles) - if err := e.encodeVideo(input, opusFiles, job, metadata); err != nil { + if err := e.encodeVideo(input, opusFiles, job, metadata, interlaced); err != nil { return fmt.Errorf("encoding video: %w", err) } @@ -93,12 +96,17 @@ func (e *Encoder) encodeOpus(wavs []string, dir string) ([]string, error) { return opusFiles, nil } -func (e *Encoder) encodeVideo(input string, opusFiles []string, job *types.Job, metadata *types.Metadata) error { +func (e *Encoder) encodeVideo(input string, opusFiles []string, job *types.Job, metadata *types.Metadata, interlaced bool) error { dir := filepath.Dir(input) outFile := filepath.Join(dir, "output.mkv") svtParams := fmt.Sprintf("film-grain=10:film-grain-denoise=1:scd=1:qm-min=4:qm-max=15:preset=%d", job.Preset) + vf := "zscale=filter=spline36:mode=16:9" + if interlaced { + vf = "bwdif=mode=0:par=-1:-1," + vf + } + args := []string{ "-y", "-i", input, @@ -106,7 +114,7 @@ func (e *Encoder) encodeVideo(input string, opusFiles []string, job *types.Job, "-crf", strconv.Itoa(job.CRF), "-svtav1-params", svtParams, "-pix_fmt", "yuv420p10le", - "-vf", "zscale=filter=spline36:mode=16:9", + "-vf", vf, "-c:a", "copy", "-c:s", "copy", }