Now splittable into segments with variable intervals, and more Cli options

This commit is contained in:
Pablu23
2024-06-11 17:39:30 +02:00
parent a1129de2d6
commit c9dc448cb8
2 changed files with 53 additions and 34 deletions

View File

@@ -3,6 +3,8 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"os"
"path/filepath"
thumbnailgen "github.com/pablu23/thumbnail-gen" thumbnailgen "github.com/pablu23/thumbnail-gen"
) )
@@ -11,37 +13,27 @@ var (
pathFlag = flag.String("path", "", "path to video") pathFlag = flag.String("path", "", "path to video")
intervalFlag = flag.Int("interval", 20, "Interval in seconds between thumbnails") intervalFlag = flag.Int("interval", 20, "Interval in seconds between thumbnails")
maxThumbnailsFlag = flag.Int("max", 0, "Max Thumbnails, default as much as possible with interval") maxThumbnailsFlag = flag.Int("max", 0, "Max Thumbnails, default as much as possible with interval")
blackFilterFlag = flag.Bool("filter", true, "Try to filter out black frames, might be an expensive operation")
) )
func main() { func main() {
flag.Parse() flag.Parse()
if *pathFlag != "" { if *pathFlag != "" {
_, err := thumbnailgen.GetFramerate(*pathFlag) // thumbnails, num, err := thumbnailgen.GetThumbnail(*pathFlag, *intervalFlag, *maxThumbnailsFlag, *blackFilterFlag)
thumbnails, num, err := thumbnailgen.GetThumbnailSegments(*pathFlag, 12, *blackFilterFlag)
if err != nil { if err != nil {
fmt.Printf("Framerate: %s\n", err) panic(err)
} }
_, err = thumbnailgen.GetVideoLength(*pathFlag) fmt.Printf("Extracted %d thumbnails\n", num)
if err != nil {
fmt.Printf("Video Length: %s\n", err)
}
_, err = thumbnailgen.GetFilter(*pathFlag) name := filepath.Base(*pathFlag)
if err != nil { for i, thumbnail := range thumbnails[:num] {
fmt.Printf("Filter: %s\n", err) err := os.WriteFile(fmt.Sprintf("%s-%d.png", name, i), thumbnail, 0600)
if err != nil {
panic(err)
}
} }
// thumbnails, err := thumbnailgen.GetThumbnail(*pathFlag, *intervalFlag, *maxThumbnailsFlag)
// if err != nil {
// panic(err)
// }
//
// name := filepath.Base(*pathFlag)
// for i, thumbnail := range thumbnails {
// err := os.WriteFile(fmt.Sprintf("%s-%d.png", name, i), thumbnail, 0600)
// if err != nil {
// panic(err)
// }
// }
} }
} }

View File

@@ -16,6 +16,26 @@ type TimeFilter struct {
End float64 End float64
} }
func GetThumbnailSegments(path string, segments int, enableFilter bool) ([][]byte, int, error) {
var filters []TimeFilter
if enableFilter {
f, err := GetFilter(path)
if err != nil {
return nil, 0, err
}
filters = f
}
length, err := GetVideoLength(path)
if err != nil {
return nil, 0, err
}
interval := length / float64(segments)
return getThumbnailUnderlying(path, 0, filters, length, int(interval), enableFilter)
}
func GetThumbnail(path string, intervalSeconds int, maxThumbnails int, enableFilter bool) ([][]byte, int, error) { func GetThumbnail(path string, intervalSeconds int, maxThumbnails int, enableFilter bool) ([][]byte, int, error) {
var filters []TimeFilter var filters []TimeFilter
if enableFilter { if enableFilter {
@@ -26,18 +46,16 @@ func GetThumbnail(path string, intervalSeconds int, maxThumbnails int, enableFil
filters = f filters = f
} }
fps, err := GetFramerate(path)
if err != nil {
return nil, 0, err
}
length, err := GetVideoLength(path) length, err := GetVideoLength(path)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
return getThumbnailUnderlying(path, maxThumbnails, filters, length, intervalSeconds, enableFilter)
}
func getThumbnailUnderlying(path string, maxThumbnails int, filters []TimeFilter, length float64, intervalSeconds int, enableFilter bool) ([][]byte, int, error) {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
currFrame := 0
framesExtracted := 0 framesExtracted := 0
var out [][]byte var out [][]byte
@@ -47,15 +65,21 @@ func GetThumbnail(path string, intervalSeconds int, maxThumbnails int, enableFil
out = make([][]byte, 0) out = make([][]byte, 0)
} }
// fmt.Printf("FPS: %f, Lenght: %f, Interval: %d, Filters: %t\n", fps, length, intervalSeconds, enableFilter)
var time float64 = 1
for { for {
time := float64(currFrame) / fps fmt.Printf("Time: %f\n", time)
if (maxThumbnails > 0 && framesExtracted >= maxThumbnails) || time >= length { if (maxThumbnails > 0 && framesExtracted >= maxThumbnails) || time >= length {
break break
} }
currFrame += int(fps) * intervalSeconds if enableFilter {
if enableFilter && FrameLiesWithinFilter(time, filters) { if ok, next := FrameLiesWithinFilter(time, filters); ok {
continue fmt.Printf("Black Frame, skipping to: %f\n", next)
time = next
continue
}
} }
err := GetImage(buf, path, int(time), "png") err := GetImage(buf, path, int(time), "png")
@@ -71,20 +95,23 @@ func GetThumbnail(path string, intervalSeconds int, maxThumbnails int, enableFil
out = append(out, b) out = append(out, b)
} }
time += float64(intervalSeconds)
framesExtracted += 1 framesExtracted += 1
buf.Reset() buf.Reset()
} }
return out, framesExtracted, nil return out, framesExtracted, nil
} }
func FrameLiesWithinFilter(time float64, filters []TimeFilter) bool { // Return true, if time is inside filter, also returns next time, when not in filter anymore.
// If False it returns input time
func FrameLiesWithinFilter(time float64, filters []TimeFilter) (bool, float64) {
for _, filter := range filters { for _, filter := range filters {
if time >= filter.Start && time <= filter.End { if time >= filter.Start && time <= filter.End {
return true return true, filter.End
} }
} }
return false return false, time
} }
func GetImage(buf *bytes.Buffer, path string, timestamp int, format string) error { func GetImage(buf *bytes.Buffer, path string, timestamp int, format string) error {