-
Notifications
You must be signed in to change notification settings - Fork 0
/
moviebarcode.go
130 lines (105 loc) · 3.04 KB
/
moviebarcode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main
import (
"bytes"
"flag"
"fmt"
"image"
"log"
"os/exec"
"strconv"
"strings"
"sync"
"github.com/en3wton/movie-barcode-generator/imageprocess"
"golang.org/x/image/bmp"
)
const numWorkers = 8
var finalPixels [][]imageprocess.Pixel
var numFrames int
var framesCompleted int
func main() {
image.RegisterFormat("bmp", "bmp", bmp.Decode, bmp.DecodeConfig)
srcFile := flag.String("filename", "", "video file to generate barcode from")
flag.IntVar(&numFrames, "numframes", 1920, "number of frames to sample - effectively image width")
flag.Parse()
// TODO Check number of frames does not exceed total number of frames
_, height := getVideoResolution(*srcFile)
finalPixels = make([][]imageprocess.Pixel, height)
for i := range finalPixels {
finalPixels[i] = make([]imageprocess.Pixel, numFrames)
}
length := getVideoLength(*srcFile)
interval := length / float64(numFrames)
period := length / float64(numWorkers)
var wg sync.WaitGroup
wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
go calculateColumns(*srcFile, float64(i)*period, float64(i+1)*period, interval, &wg)
}
wg.Wait()
imageprocess.CreateImage(finalPixels)
}
func calculateColumns(filename string, start float64, end float64, interval float64, wg *sync.WaitGroup) {
for i := start; i < end; i += interval {
pixels := getFrame(filename, i)
for j := range pixels {
finalPixels[j][int(i/interval)] = imageprocess.AveragePixels(pixels[j])
}
updateProgress()
}
wg.Done()
}
func getFrame(filename string, time float64) [][]imageprocess.Pixel {
t := fmt.Sprintf("%f", time)
cmd := exec.Command("ffmpeg", "-accurate_seek", "-ss", t, "-i",
filename, "-frames:v", "1", "-hide_banner", "-loglevel", "0", "pipe:.bmp")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
pixels, err := imageprocess.GetPixels(&out)
if err != nil {
fmt.Println("Error at:" + t)
log.Fatal(err)
}
return pixels
}
func getVideoLength(filename string) float64 {
cmd := exec.Command("ffprobe", "-i", filename, "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
length, err := strconv.ParseFloat(strings.TrimRight(out.String(), "\r\n"), 64)
if err != nil {
log.Fatal(err)
}
return length
}
func getVideoResolution(filename string) (wdith int, height int) {
cmd := exec.Command("ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
"stream=width,height", "-of", "csv=s=x:p=0", filename)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
text := strings.TrimRight(out.String(), "\r\n")
widthString := strings.Split(text, "x")[0]
heightString := strings.Split(text, "x")[1]
w, err := strconv.Atoi(widthString)
h, err := strconv.Atoi(heightString)
if err != nil {
log.Fatal(err)
}
return w, h
}
func updateProgress() {
framesCompleted++
var percentage = float32(framesCompleted) / float32(numFrames) * 100
fmt.Printf("\rAnalysing frames: %4.1f%%", percentage)
}