Initial commit
This commit is contained in:
185
tasks/black-border-analyze.go
Normal file
185
tasks/black-border-analyze.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/roemer/goext"
|
||||
)
|
||||
|
||||
type BlackBorderAnalyzeTask struct {
|
||||
logFilePath string
|
||||
cropDetectOutputRegex *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewBlackBorderAnalyzeTask(logFilePath string) *BlackBorderAnalyzeTask {
|
||||
return &BlackBorderAnalyzeTask{
|
||||
logFilePath: logFilePath,
|
||||
cropDetectOutputRegex: regexp.MustCompile(`.*Parsed_cropdetect_0.* x1:([0-9]+) x2:([0-9]+) y1:([0-9]+) y2:([0-9]+) w:([0-9]+) h:([0-9]+) x:([0-9]+) y:([0-9]+) pts:([0-9]+) t:([0-9\.]+) limit:([0-9\.]+) crop=([0-9\:]+)`),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BlackBorderAnalyzeTask) Run(folderPath string) error {
|
||||
files, err := os.ReadDir(folderPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read directory: %w", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
// Skip directories
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
// Skip reencoded files
|
||||
if strings.HasSuffix(file.Name(), "_2.mkv") {
|
||||
continue
|
||||
}
|
||||
t.output(fmt.Sprintf("=== %s", file.Name()))
|
||||
if err := t.processFile(filepath.Join(folderPath, file.Name())); err != nil {
|
||||
return fmt.Errorf("failed to process file %s: %w", file.Name(), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlackBorderAnalyzeTask) processFile(filePath string) error {
|
||||
// Get the resolution of the video
|
||||
resOutput, _, err := goext.CmdRunners.Default.RunGetOutput("ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "json", filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run ffprobe: %w", err)
|
||||
}
|
||||
decoder := json.NewDecoder(strings.NewReader(resOutput))
|
||||
var result struct {
|
||||
Streams []struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
if err := decoder.Decode(&result); err != nil {
|
||||
return fmt.Errorf("failed to decode ffprobe output: %w", err)
|
||||
}
|
||||
videoWidth := result.Streams[0].Width
|
||||
videoHeight := result.Streams[0].Height
|
||||
|
||||
// Execute a command and process output in real time
|
||||
cmd := exec.Command("ffmpeg", "-v", "debug", "-i", filePath, "-vf", "cropdetect", "-f", "null", "-")
|
||||
|
||||
// Create a single pipe that combines both stdout and stderr
|
||||
combinedOutput, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create stdout pipe: %w", err)
|
||||
}
|
||||
|
||||
// Redirect stderr to stdout so both streams are handled together
|
||||
cmd.Stderr = cmd.Stdout
|
||||
|
||||
// Start the command
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start command: %w", err)
|
||||
}
|
||||
|
||||
// Process combined output in real time with a single goroutine
|
||||
go func() {
|
||||
lastValue := &blackBorderAnalyzeData{}
|
||||
scanner := bufio.NewScanner(combinedOutput)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// Process each line of combined output here
|
||||
value := t.processOutputLine(line)
|
||||
if value != nil {
|
||||
if value.x1 != lastValue.x1 || value.x2 != lastValue.x2 || value.y1 != lastValue.y1 || value.y2 != lastValue.y2 || value.w != lastValue.w || value.h != lastValue.h || value.x != lastValue.x || value.y != lastValue.y {
|
||||
// output(value.String())
|
||||
t.output(fmt.Sprintf("left=%d, right=%d, top=%d, bottom=%d, time:%s",
|
||||
value.x1, videoWidth-value.x2, value.y1, videoHeight-value.y2, value.timeStamp))
|
||||
}
|
||||
lastValue = value
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error reading combined output: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for the command to complete
|
||||
return cmd.Wait()
|
||||
}
|
||||
|
||||
func (t *BlackBorderAnalyzeTask) processOutputLine(line string) *blackBorderAnalyzeData {
|
||||
match := t.cropDetectOutputRegex.FindStringSubmatch(line)
|
||||
if match != nil {
|
||||
//fmt.Println(line)
|
||||
// Extracted values can be processed further
|
||||
x1 := match[1]
|
||||
x2 := match[2]
|
||||
y1 := match[3]
|
||||
y2 := match[4]
|
||||
w := match[5]
|
||||
h := match[6]
|
||||
x := match[7]
|
||||
y := match[8]
|
||||
//pts := match[9]
|
||||
t := match[10]
|
||||
//limit := match[11]
|
||||
//crop := match[12]
|
||||
|
||||
x1v, _ := strconv.Atoi(x1)
|
||||
x2v, _ := strconv.Atoi(x2)
|
||||
y1v, _ := strconv.Atoi(y1)
|
||||
y2v, _ := strconv.Atoi(y2)
|
||||
wv, _ := strconv.Atoi(w)
|
||||
hv, _ := strconv.Atoi(h)
|
||||
xv, _ := strconv.Atoi(x)
|
||||
yv, _ := strconv.Atoi(y)
|
||||
timeStamp, _ := strconv.ParseFloat(t, 64)
|
||||
return &blackBorderAnalyzeData{
|
||||
x1: x1v,
|
||||
x2: x2v,
|
||||
y1: y1v,
|
||||
y2: y2v,
|
||||
w: wv,
|
||||
h: hv,
|
||||
x: xv,
|
||||
y: yv,
|
||||
timeStamp: time.Duration(timeStamp * float64(time.Second)),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlackBorderAnalyzeTask) output(value string) {
|
||||
fmt.Println(value)
|
||||
// Append to file
|
||||
f, err := os.OpenFile(t.logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error opening file for writing: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.WriteString(value + "\n"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing to file: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
type blackBorderAnalyzeData struct {
|
||||
x1 int
|
||||
x2 int
|
||||
y1 int
|
||||
y2 int
|
||||
w int
|
||||
h int
|
||||
x int
|
||||
y int
|
||||
timeStamp time.Duration
|
||||
}
|
||||
|
||||
func (c blackBorderAnalyzeData) String() string {
|
||||
return fmt.Sprintf("x1: %d, x2: %d, y1: %d, y2: %d, w: %d, h: %d, x: %d, y: %d, timeStamp: %s", c.x1, c.x2, c.y1, c.y2, c.w, c.h, c.x, c.y, c.timeStamp)
|
||||
}
|
||||
Reference in New Issue
Block a user