diff --git a/.circleci/config.yml b/.circleci/config.yml
index 77eb9bd..08c3a89 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -3,7 +3,7 @@ jobs:
build:
docker:
# specify the version
- - image: circleci/golang:1.14-buster
+ - image: circleci/golang:1.17-buster
environment:
GO111MODULE: "on"
diff --git a/LICENSE b/LICENSE
index 2ad87fe..365e617 100644
--- a/LICENSE
+++ b/LICENSE
@@ -2,6 +2,8 @@ MIT License
Copyright (c) 2020 FlooStack
+Copyright (c) 2021 Legion-Zver (MACROX & ITRabbit)
+
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
diff --git a/README.md b/README.md
index 651b613..b731fe7 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,6 @@
# Golang Transcoding Library
-
-
-
-
-
-
-
+Exec ffmpeg and ffprobe
## Features
@@ -34,49 +15,5 @@
## Download from Github
```shell
-go get github.com/floostack/transcoder
-```
-
-## Example
-
-```go
-package main
-
-import (
- "log"
-
- ffmpeg "github.com/floostack/transcoder/ffmpeg"
-)
-
-func main() {
-
- format := "mp4"
- overwrite := true
-
- opts := ffmpeg.Options{
- OutputFormat: &format,
- Overwrite: &overwrite,
- }
-
- ffmpegConf := &ffmpeg.Config{
- FfmpegBinPath: "/usr/local/bin/ffmpeg",
- FfprobeBinPath: "/usr/local/bin/ffprobe",
- ProgressEnabled: true,
- }
-
- progress, err := ffmpeg.
- New(ffmpegConf).
- Input("/tmp/avi").
- Output("/tmp/mp4").
- WithOptions(opts).
- Start(opts)
-
- if err != nil {
- log.Fatal(err)
- }
-
- for msg := range progress {
- log.Printf("%+v", msg)
- }
-}
-```
+go get github.com/legion-zver/transcoder
+```
\ No newline at end of file
diff --git a/ffmpeg/config.go b/ffmpeg/config.go
index 05eb7ff..7ccba5f 100644
--- a/ffmpeg/config.go
+++ b/ffmpeg/config.go
@@ -1,9 +1,61 @@
package ffmpeg
+import (
+ "bytes"
+ "errors"
+ "os/exec"
+ "strings"
+)
+
+// Flags ...
+type Flags struct {
+ Progress bool `json:"progress,omitempty"`
+ Verbose bool `json:"verbose,omitempty"`
+ Debug bool `json:"debug,omitempty"`
+}
+
// Config ...
type Config struct {
- FfmpegBinPath string
- FfprobeBinPath string
- ProgressEnabled bool
- Verbose bool
+ Flags
+
+ // Paths
+ FfmpegBinPath string `json:"ffmpeg_bin_path"`
+ FfprobeBinPath string `json:"ffprobe_bin_path"`
+}
+
+func mergeFlags(flags ...Flags) (result Flags) {
+ for _, v := range flags {
+ result.Progress = result.Progress || v.Progress
+ result.Verbose = result.Verbose || v.Verbose
+ result.Debug = result.Debug || v.Debug
+ }
+ return
+}
+
+func NewAutoConfig(flags ...Flags) (*Config, error) {
+ out, which := bytes.Buffer{}, exec.Command("which", "ffmpeg")
+ which.Stdout = &out
+ if err := which.Run(); err != nil {
+ return nil, err
+ }
+ ffmpegBinPath := strings.TrimSpace(out.String())
+ out.Reset()
+ if len(ffmpegBinPath) < 1 {
+ return nil, errors.New("ffmpeg binary path not found")
+ }
+ which = exec.Command("which", "ffprobe")
+ which.Stdout = &out
+ if err := which.Run(); err != nil {
+ return nil, err
+ }
+ ffprobeBinPath := strings.TrimSpace(out.String())
+ out.Reset()
+ if len(ffprobeBinPath) < 1 {
+ return nil, errors.New("ffprobe binary path not found")
+ }
+ return &Config{
+ Flags: mergeFlags(flags...),
+ FfmpegBinPath: ffmpegBinPath,
+ FfprobeBinPath: ffprobeBinPath,
+ }, nil
}
diff --git a/ffmpeg/errors.go b/ffmpeg/errors.go
new file mode 100644
index 0000000..95f8877
--- /dev/null
+++ b/ffmpeg/errors.go
@@ -0,0 +1 @@
+package ffmpeg
diff --git a/ffmpeg/ffmpeg.go b/ffmpeg/ffmpeg.go
index ce262f8..e2e721d 100644
--- a/ffmpeg/ffmpeg.go
+++ b/ffmpeg/ffmpeg.go
@@ -14,15 +14,17 @@ import (
"strconv"
"strings"
- "github.com/floostack/transcoder"
- "github.com/floostack/transcoder/utils"
+ "github.com/legion-zver/transcoder"
+ "github.com/legion-zver/transcoder/utils"
)
// Transcoder ...
type Transcoder struct {
config *Config
input string
+ start []string
output []string
+ errors []string
options [][]string
metadata transcoder.Metadata
inputPipeReader *io.ReadCloser
@@ -30,40 +32,71 @@ type Transcoder struct {
inputPipeWriter *io.WriteCloser
outputPipeWriter *io.WriteCloser
commandContext *context.Context
+ skipGetMetadata bool
}
// New ...
func New(cfg *Config) transcoder.Transcoder {
- return &Transcoder{config: cfg}
+ return &Transcoder{config: cfg, errors: []string{}, start: []string{}}
}
-// Start ...
-func (t *Transcoder) Start(opts transcoder.Options) (<-chan transcoder.Progress, error) {
+// Errors ...
+func (t *Transcoder) Error() (err error) {
+ if t.errors == nil || len(t.errors) < 1 {
+ return
+ }
+ return errors.New(strings.Join(t.errors, " |--> "))
+}
- var stderrIn io.ReadCloser
+// SkipMetadata ...
+func (t *Transcoder) SkipMetadata() transcoder.Transcoder {
+ t.skipGetMetadata = true
+ return t
+}
- out := make(chan transcoder.Progress)
+// WithMetadata ...
+func (t *Transcoder) WithMetadata(metadata transcoder.Metadata) transcoder.Transcoder {
+ t.skipGetMetadata = true
+ t.metadata = metadata
+ return t
+}
+// Start ...
+func (t *Transcoder) Start(opts transcoder.Options) (<-chan transcoder.Progress, error) {
+ if opts == nil {
+ opts = Options{}
+ }
defer t.closePipes()
+ // Clear errors
+ t.errors = []string{}
+
// Validates config
if err := t.validate(); err != nil {
+ t.errors = append(t.errors, err.Error())
return nil, err
}
// Get file metadata
- _, err := t.GetMetadata()
- if err != nil {
- return nil, err
+ if !t.skipGetMetadata {
+ if _, err := t.GetMetadata(); err != nil {
+ t.errors = append(t.errors, err.Error())
+ return nil, err
+ }
}
// Append input file and standard options
- args := append([]string{"-i", t.input}, opts.GetStrArguments()...)
- outputLength := len(t.output)
- optionsLength := len(t.options)
-
+ args, outputLength, optionsLength := append(
+ append(
+ append(
+ []string{}, t.start...,
+ ),
+ []string{"-hide_banner", "-i", t.input}...,
+ ), opts.GetStrArguments()...,
+ ), len(t.output), len(t.options)
+
+ // Just append the 1 output file we've got
if outputLength == 1 && optionsLength == 0 {
- // Just append the 1 output file we've got
args = append(args, t.output[0])
} else {
for index, out := range t.output {
@@ -73,11 +106,10 @@ func (t *Transcoder) Start(opts transcoder.Options) (<-chan transcoder.Progress,
for i := index; i < len(t.options); i++ {
args = append(args, t.options[i]...)
}
- // Otherwise just append the current options
+ // Otherwise, just append the current options
} else {
args = append(args, t.options[index]...)
}
-
// Append output flag
args = append(args, out)
}
@@ -93,39 +125,46 @@ func (t *Transcoder) Start(opts transcoder.Options) (<-chan transcoder.Progress,
} else {
cmd = exec.CommandContext(*t.commandContext, t.config.FfmpegBinPath, args...)
}
-
- // If progresss enabled, get stderr pipe and start progress process
- if t.config.ProgressEnabled && !t.config.Verbose {
+ // Print debug
+ if t.config.Debug {
+ fmt.Println("transcoder.Start", t.config.FfmpegBinPath, args)
+ }
+ // If progress enabled, get stderr pipe and start progress process
+ var (
+ stderrIn io.ReadCloser
+ err error
+ )
+ if t.config.Progress && !t.config.Verbose {
stderrIn, err = cmd.StderrPipe()
if err != nil {
- return nil, fmt.Errorf("Failed getting transcoding progress (%s) with args (%s) with error %s", t.config.FfmpegBinPath, args, err)
+ t.errors = append(t.errors, err.Error())
+ return nil, fmt.Errorf("failed getting transcoding progress (%s) with args (%s) with error %s", t.config.FfmpegBinPath, args, err)
}
}
-
if t.config.Verbose {
cmd.Stderr = os.Stdout
}
-
// Start process
- err = cmd.Start()
- if err != nil {
- return nil, fmt.Errorf("Failed starting transcoding (%s) with args (%s) with error %s", t.config.FfmpegBinPath, args, err)
+ if err = cmd.Start(); err != nil {
+ return nil, fmt.Errorf("failed starting transcoding (%s) with args (%s) with error %s", t.config.FfmpegBinPath, args, err)
}
-
- if t.config.ProgressEnabled && !t.config.Verbose {
- go func() {
- t.progress(stderrIn, out)
- }()
-
- go func() {
- defer close(out)
- err = cmd.Wait()
- }()
- } else {
- err = cmd.Wait()
+ if !t.config.Progress || t.config.Verbose {
+ if err = cmd.Wait(); err != nil {
+ t.errors = append(t.errors, err.Error())
+ }
+ return nil, err
}
-
- return out, nil
+ out := make(chan transcoder.Progress)
+ go func() {
+ defer close(out)
+ t.progress(stderrIn, out)
+ }()
+ go func() {
+ if err := cmd.Wait(); err != nil {
+ t.errors = append(t.errors, err.Error())
+ }
+ }()
+ return out, err
}
// Input ...
@@ -158,6 +197,12 @@ func (t *Transcoder) OutputPipe(w *io.WriteCloser, r *io.ReadCloser) transcoder.
return t
}
+// WithStartOptions Sets the start options object
+func (t *Transcoder) WithStartOptions(opts transcoder.Options) transcoder.Transcoder {
+ t.start = opts.GetStrArguments()
+ return t
+}
+
// WithOptions Sets the options object
func (t *Transcoder) WithOptions(opts transcoder.Options) transcoder.Transcoder {
t.options = [][]string{opts.GetStrArguments()}
@@ -166,10 +211,22 @@ func (t *Transcoder) WithOptions(opts transcoder.Options) transcoder.Transcoder
// WithAdditionalOptions Appends an additional options object
func (t *Transcoder) WithAdditionalOptions(opts transcoder.Options) transcoder.Transcoder {
+ if t.options == nil {
+ return t.WithOptions(opts)
+ }
t.options = append(t.options, opts.GetStrArguments())
return t
}
+// WithAdditionalStartOptions Appends an additional start options object
+func (t *Transcoder) WithAdditionalStartOptions(opts transcoder.Options) transcoder.Transcoder {
+ if t.start == nil {
+ return t.WithStartOptions(opts)
+ }
+ t.start = append(t.start, opts.GetStrArguments()...)
+ return t
+}
+
// WithContext is to be used on a Transcoder *before Starting* to
// pass in a context.Context object that can be used to kill
// a running transcoder process. Usage of this method is optional
@@ -183,13 +240,10 @@ func (t *Transcoder) validate() error {
if t.config.FfmpegBinPath == "" {
return errors.New("ffmpeg binary path not found")
}
-
if t.input == "" {
return errors.New("missing input option")
}
-
outputLength := len(t.output)
-
if outputLength == 0 {
return errors.New("missing output option")
}
@@ -199,58 +253,55 @@ func (t *Transcoder) validate() error {
if outputLength > len(t.options) && outputLength != 1 {
return errors.New("number of options and output files does not match")
}
-
for index, output := range t.output {
if output == "" {
return fmt.Errorf("output at index %d is an empty string", index)
}
}
-
return nil
}
// GetMetadata Returns metadata for the specified input file
func (t *Transcoder) GetMetadata() (transcoder.Metadata, error) {
-
- if t.config.FfprobeBinPath != "" {
- var outb, errb bytes.Buffer
-
- input := t.input
-
- if t.inputPipeReader != nil {
- input = "pipe:"
- }
-
- args := []string{"-i", input, "-print_format", "json", "-show_format", "-show_streams", "-show_error"}
-
- cmd := exec.Command(t.config.FfprobeBinPath, args...)
- cmd.Stdout = &outb
- cmd.Stderr = &errb
-
- err := cmd.Run()
- if err != nil {
- return nil, fmt.Errorf("error executing (%s) with args (%s) | error: %s | message: %s %s", t.config.FfprobeBinPath, args, err, outb.String(), errb.String())
- }
-
- var metadata Metadata
-
- if err = json.Unmarshal([]byte(outb.String()), &metadata); err != nil {
- return nil, err
+ if len(t.config.FfprobeBinPath) < 1 {
+ return nil, errors.New("ffprobe binary not found")
+ }
+ input := t.input
+ if t.inputPipeReader != nil {
+ input = "pipe:"
+ }
+ var stdOut, stdErr bytes.Buffer
+ args := []string{"-hide_banner", "-i", input, "-print_format", "json", "-show_format", "-show_streams", "-show_error"}
+ cmd := exec.Command(t.config.FfprobeBinPath, args...)
+ cmd.Stdout = &stdOut
+ cmd.Stderr = &stdErr
+ // Print debug info
+ if t.config.Debug {
+ fmt.Println("transcoder.GetMetadata", t.config.FfprobeBinPath, args)
+ }
+ if err := cmd.Run(); err != nil {
+ if t.config.Debug {
+ fmt.Println(stdOut.String())
+ fmt.Println(stdErr.String())
}
-
- t.metadata = metadata
-
- return metadata, nil
+ return nil, fmt.Errorf("error executing (%s) with args (%s) | error: %s | message: %s %s", t.config.FfprobeBinPath, args, err, stdOut.String(), stdErr.String())
}
-
- return nil, errors.New("ffprobe binary not found")
+ var metadata Metadata
+ if err := json.Unmarshal([]byte(stdOut.String()), &metadata); err != nil {
+ return nil, err
+ }
+ t.metadata = metadata
+ return metadata, nil
}
// progress sends through given channel the transcoding status
func (t *Transcoder) progress(stream io.ReadCloser, out chan transcoder.Progress) {
-
- defer stream.Close()
-
+ if stream == nil {
+ return
+ }
+ defer func(stream io.ReadCloser) {
+ _ = stream.Close()
+ }(stream)
split := func(data []byte, atEOF bool) (advance int, token []byte, spliterror error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
@@ -266,7 +317,6 @@ func (t *Transcoder) progress(stream io.ReadCloser, out chan transcoder.Progress
if atEOF {
return len(data), data, nil
}
-
return 0, nil, nil
}
@@ -276,58 +326,62 @@ func (t *Transcoder) progress(stream io.ReadCloser, out chan transcoder.Progress
buf := make([]byte, 2)
scanner.Buffer(buf, bufio.MaxScanTokenSize)
+ re := regexp.MustCompile(`=\s+`)
for scanner.Scan() {
- Progress := new(Progress)
line := scanner.Text()
-
+ if len(line) < 1 {
+ continue
+ }
+ if t.config.Debug {
+ fmt.Println(line)
+ }
if strings.Contains(line, "time=") && strings.Contains(line, "bitrate=") {
- var re = regexp.MustCompile(`=\s+`)
- st := re.ReplaceAllString(line, `=`)
-
- f := strings.Fields(st)
-
- var framesProcessed string
- var currentTime string
- var currentBitrate string
- var currentSpeed string
-
+ f := strings.Fields(re.ReplaceAllString(line, `=`))
+ var (
+ framesProcessed string
+ currentBitrate string
+ currentSpeed string
+ currentTime string
+ )
for j := 0; j < len(f); j++ {
field := f[j]
fieldSplit := strings.Split(field, "=")
-
if len(fieldSplit) > 1 {
- fieldname := strings.Split(field, "=")[0]
- fieldvalue := strings.Split(field, "=")[1]
-
- if fieldname == "frame" {
- framesProcessed = fieldvalue
+ fieldName, fieldValue := strings.Split(field, "=")[0], strings.Split(field, "=")[1]
+ if fieldName == "frame" {
+ framesProcessed = fieldValue
}
-
- if fieldname == "time" {
- currentTime = fieldvalue
+ if fieldName == "time" {
+ currentTime = fieldValue
}
-
- if fieldname == "bitrate" {
- currentBitrate = fieldvalue
+ if fieldName == "bitrate" {
+ currentBitrate = fieldValue
}
- if fieldname == "speed" {
- currentSpeed = fieldvalue
+ if fieldName == "speed" {
+ currentSpeed = fieldValue
}
}
}
-
- timesec := utils.DurToSec(currentTime)
- dursec, _ := strconv.ParseFloat(t.metadata.GetFormat().GetDuration(), 64)
-
- progress := (timesec * 100) / dursec
- Progress.Progress = progress
-
- Progress.CurrentBitrate = currentBitrate
- Progress.FramesProcessed = framesProcessed
- Progress.CurrentTime = currentTime
- Progress.Speed = currentSpeed
-
- out <- *Progress
+ timeSec := utils.DurToSec(currentTime)
+ progress := timeSec
+ if t.metadata != nil {
+ if durSec, _ := strconv.ParseFloat(t.metadata.GetFormat().GetDuration(), 64); durSec != 0 {
+ progress = (timeSec * 100) / durSec
+ }
+ }
+ out <- Progress{
+ Progress: progress,
+ CurrentBitrate: currentBitrate,
+ FramesProcessed: framesProcessed,
+ CurrentTime: currentTime,
+ Speed: currentSpeed,
+ }
+ continue
+ } else if strings.HasPrefix(line, "Error") ||
+ strings.HasPrefix(line, "Unrecognized option") ||
+ (strings.Contains(line, "[y/N]") &&
+ strings.Contains(line, "exiting")) {
+ t.errors = append(t.errors, strings.TrimSuffix(line, "."))
}
}
}
@@ -336,11 +390,10 @@ func (t *Transcoder) progress(stream io.ReadCloser, out chan transcoder.Progress
func (t *Transcoder) closePipes() {
if t.inputPipeReader != nil {
ipr := *t.inputPipeReader
- ipr.Close()
+ _ = ipr.Close()
}
-
if t.outputPipeWriter != nil {
opr := *t.outputPipeWriter
- opr.Close()
+ _ = opr.Close()
}
}
diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go
new file mode 100644
index 0000000..4c30e27
--- /dev/null
+++ b/ffmpeg/ffmpeg_test.go
@@ -0,0 +1,29 @@
+package ffmpeg
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestFightingErrors(t *testing.T) {
+ cfg, err := NewAutoConfig(Flags{Progress: true})
+ if err != nil {
+ t.Error(err)
+ }
+ // Create instance and fill
+ instance := New(cfg).Input("test").Output("test").SkipMetadata()
+ // Start
+ p, err := instance.Start(Options{"-y"})
+ if err != nil {
+ t.Error(err)
+ }
+ if p == nil {
+ t.Error("waiting progress chain!")
+ }
+ for tick := range p {
+ fmt.Println(tick)
+ }
+ if err = instance.Error(); err == nil {
+ t.Error("waiting for error!")
+ }
+}
diff --git a/ffmpeg/metadata.go b/ffmpeg/metadata.go
index 4e81910..f192ac9 100644
--- a/ffmpeg/metadata.go
+++ b/ffmpeg/metadata.go
@@ -1,6 +1,6 @@
package ffmpeg
-import "github.com/floostack/transcoder"
+import "github.com/legion-zver/transcoder"
// Metadata ...
type Metadata struct {
@@ -11,53 +11,49 @@ type Metadata struct {
// Format ...
type Format struct {
Filename string
- NbStreams int `json:"nb_streams"`
- NbPrograms int `json:"nb_programs"`
- FormatName string `json:"format_name"`
- FormatLongName string `json:"format_long_name"`
- Duration string `json:"duration"`
- Size string `json:"size"`
- BitRate string `json:"bit_rate"`
- ProbeScore int `json:"probe_score"`
- Tags Tags `json:"tags"`
+ NbStreams int `json:"nb_streams"`
+ NbPrograms int `json:"nb_programs"`
+ FormatName string `json:"format_name"`
+ FormatLongName string `json:"format_long_name"`
+ Duration string `json:"duration"`
+ Size string `json:"size"`
+ BitRate string `json:"bit_rate"`
+ ProbeScore int `json:"probe_score,omitempty"`
+ Tags map[string]string `json:"tags,omitempty"`
}
// Streams ...
type Streams struct {
Index int
- ID string `json:"id"`
- CodecName string `json:"codec_name"`
- CodecLongName string `json:"codec_long_name"`
- Profile string `json:"profile"`
- CodecType string `json:"codec_type"`
- CodecTimeBase string `json:"codec_time_base"`
- CodecTagString string `json:"codec_tag_string"`
- CodecTag string `json:"codec_tag"`
- Width int `json:"width"`
- Height int `json:"height"`
- CodedWidth int `json:"coded_width"`
- CodedHeight int `json:"coded_height"`
- HasBFrames int `json:"has_b_frames"`
- SampleAspectRatio string `json:"sample_aspect_ratio"`
- DisplayAspectRatio string `json:"display_aspect_ratio"`
- PixFmt string `json:"pix_fmt"`
- Level int `json:"level"`
- ChromaLocation string `json:"chroma_location"`
- Refs int `json:"refs"`
- QuarterSample string `json:"quarter_sample"`
- DivxPacked string `json:"divx_packed"`
- RFrameRrate string `json:"r_frame_rate"`
- AvgFrameRate string `json:"avg_frame_rate"`
- TimeBase string `json:"time_base"`
- DurationTs int `json:"duration_ts"`
- Duration string `json:"duration"`
- Disposition Disposition `json:"disposition"`
- BitRate string `json:"bit_rate"`
-}
-
-// Tags ...
-type Tags struct {
- Encoder string `json:"ENCODER"`
+ ID string `json:"id"`
+ CodecName string `json:"codec_name"`
+ CodecLongName string `json:"codec_long_name"`
+ Profile string `json:"profile"`
+ CodecType string `json:"codec_type"`
+ CodecTimeBase string `json:"codec_time_base"`
+ CodecTagString string `json:"codec_tag_string"`
+ CodecTag string `json:"codec_tag"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+ CodedWidth int `json:"coded_width"`
+ CodedHeight int `json:"coded_height"`
+ HasBFrames int `json:"has_b_frames"`
+ SampleAspectRatio string `json:"sample_aspect_ratio"`
+ DisplayAspectRatio string `json:"display_aspect_ratio"`
+ PixFmt string `json:"pix_fmt"`
+ Level int `json:"level"`
+ ChromaLocation string `json:"chroma_location"`
+ Refs int `json:"refs"`
+ QuarterSample string `json:"quarter_sample"`
+ DivxPacked string `json:"divx_packed"`
+ RFrameRrate string `json:"r_frame_rate"`
+ AvgFrameRate string `json:"avg_frame_rate"`
+ TimeBase string `json:"time_base"`
+ DurationTs int `json:"duration_ts"`
+ Duration string `json:"duration"`
+ Disposition Disposition `json:"disposition"`
+ BitRate string `json:"bit_rate"`
+ Tags map[string]string `json:"tags,omitempty"`
}
// Disposition ...
@@ -133,15 +129,10 @@ func (f Format) GetProbeScore() int {
}
// GetTags ...
-func (f Format) GetTags() transcoder.Tags {
+func (f Format) GetTags() map[string]string {
return f.Tags
}
-// GetEncoder ...
-func (t Tags) GetEncoder() string {
- return t.Encoder
-}
-
//GetIndex ...
func (s Streams) GetIndex() int {
return s.Index
@@ -287,6 +278,11 @@ func (s Streams) GetBitRate() string {
return s.BitRate
}
+// GetTags ...
+func (s Streams) GetTags() map[string]string {
+ return s.Tags
+}
+
//GetDefault ...
func (d Disposition) GetDefault() int {
return d.Default
diff --git a/ffmpeg/options.go b/ffmpeg/options.go
index 323d4b3..f19b289 100644
--- a/ffmpeg/options.go
+++ b/ffmpeg/options.go
@@ -1,114 +1,9 @@
package ffmpeg
-import (
- "fmt"
- "reflect"
-)
-
// Options defines allowed FFmpeg arguments
-type Options struct {
- Aspect *string `flag:"-aspect"`
- Resolution *string `flag:"-s"`
- VideoBitRate *string `flag:"-b:v"`
- VideoBitRateTolerance *int `flag:"-bt"`
- VideoMaxBitRate *int `flag:"-maxrate"`
- VideoMinBitrate *int `flag:"-minrate"`
- VideoCodec *string `flag:"-c:v"`
- Vframes *int `flag:"-vframes"`
- FrameRate *int `flag:"-r"`
- AudioRate *int `flag:"-ar"`
- KeyframeInterval *int `flag:"-g"`
- AudioCodec *string `flag:"-c:a"`
- AudioBitrate *string `flag:"-ab"`
- AudioChannels *int `flag:"-ac"`
- AudioVariableBitrate *bool `flag:"-q:a"`
- BufferSize *int `flag:"-bufsize"`
- Threadset *bool `flag:"-threads"`
- Threads *int `flag:"-threads"`
- Preset *string `flag:"-preset"`
- Tune *string `flag:"-tune"`
- AudioProfile *string `flag:"-profile:a"`
- VideoProfile *string `flag:"-profile:v"`
- Target *string `flag:"-target"`
- Duration *string `flag:"-t"`
- Qscale *uint32 `flag:"-qscale"`
- Crf *uint32 `flag:"-crf"`
- Strict *int `flag:"-strict"`
- MuxDelay *string `flag:"-muxdelay"`
- SeekTime *string `flag:"-ss"`
- SeekUsingTimestamp *bool `flag:"-seek_timestamp"`
- MovFlags *string `flag:"-movflags"`
- HideBanner *bool `flag:"-hide_banner"`
- OutputFormat *string `flag:"-f"`
- CopyTs *bool `flag:"-copyts"`
- NativeFramerateInput *bool `flag:"-re"`
- InputInitialOffset *string `flag:"-itsoffset"`
- RtmpLive *string `flag:"-rtmp_live"`
- HlsPlaylistType *string `flag:"-hls_playlist_type"`
- HlsListSize *int `flag:"-hls_list_size"`
- HlsSegmentDuration *int `flag:"-hls_time"`
- HlsMasterPlaylistName *string `flag:"-master_pl_name"`
- HlsSegmentFilename *string `flag:"-hls_segment_filename"`
- HTTPMethod *string `flag:"-method"`
- HTTPKeepAlive *bool `flag:"-multiple_requests"`
- Hwaccel *string `flag:"-hwaccel"`
- StreamIds map[string]string `flag:"-streamid"`
- VideoFilter *string `flag:"-vf"`
- AudioFilter *string `flag:"-af"`
- SkipVideo *bool `flag:"-vn"`
- SkipAudio *bool `flag:"-an"`
- CompressionLevel *int `flag:"-compression_level"`
- MapMetadata *string `flag:"-map_metadata"`
- Metadata map[string]string `flag:"-metadata"`
- EncryptionKey *string `flag:"-hls_key_info_file"`
- Bframe *int `flag:"-bf"`
- PixFmt *string `flag:"-pix_fmt"`
- WhiteListProtocols []string `flag:"-protocol_whitelist"`
- Overwrite *bool `flag:"-y"`
- ExtraArgs map[string]interface{}
-}
+type Options []string
// GetStrArguments ...
func (opts Options) GetStrArguments() []string {
- f := reflect.TypeOf(opts)
- v := reflect.ValueOf(opts)
-
- values := []string{}
-
- for i := 0; i < f.NumField(); i++ {
- flag := f.Field(i).Tag.Get("flag")
- value := v.Field(i).Interface()
-
- if !v.Field(i).IsNil() {
-
- if _, ok := value.(*bool); ok {
- values = append(values, flag)
- }
-
- if vs, ok := value.(*string); ok {
- values = append(values, flag, *vs)
- }
-
- if va, ok := value.([]string); ok {
-
- for i := 0; i < len(va); i++ {
- item := va[i]
- values = append(values, flag, item)
- }
- }
-
- if vm, ok := value.(map[string]interface{}); ok {
- for k, v := range vm {
- values = append(values, k, fmt.Sprintf("%v", v))
- }
- }
-
- if vi, ok := value.(*int); ok {
- values = append(values, flag, fmt.Sprintf("%d", *vi))
- }
-
- }
- }
-
- return values
+ return opts
}
diff --git a/ffmpeg/progress.go b/ffmpeg/progress.go
index 62594c6..2593ce6 100644
--- a/ffmpeg/progress.go
+++ b/ffmpeg/progress.go
@@ -1,12 +1,16 @@
package ffmpeg
+import (
+ "encoding/json"
+)
+
// Progress ...
type Progress struct {
- FramesProcessed string
- CurrentTime string
- CurrentBitrate string
- Progress float64
- Speed string
+ FramesProcessed string `json:"f"`
+ CurrentTime string `json:"t"`
+ CurrentBitrate string `json:"b"`
+ Speed string `json:"s"`
+ Progress float64 `json:"p"`
}
// GetFramesProcessed ...
@@ -33,3 +37,11 @@ func (p Progress) GetProgress() float64 {
func (p Progress) GetSpeed() string {
return p.Speed
}
+
+func (p Progress) String() string {
+ data, _ := json.Marshal(&p)
+ if data == nil {
+ return "{}"
+ }
+ return string(data)
+}
diff --git a/go.mod b/go.mod
index 4c9ade4..cbfa351 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module github.com/floostack/transcoder
+module github.com/legion-zver/transcoder
-go 1.13
+go 1.17
diff --git a/metadata.go b/metadata.go
index d03241d..aee0f43 100644
--- a/metadata.go
+++ b/metadata.go
@@ -17,7 +17,7 @@ type Format interface {
GetSize() string
GetBitRate() string
GetProbeScore() int
- GetTags() Tags
+ GetTags() map[string]string
}
// Streams ...
@@ -51,11 +51,7 @@ type Streams interface {
GetDuration() string
GetDisposition() Disposition
GetBitRate() string
-}
-
-// Tags ...
-type Tags interface {
- GetEncoder() string
+ GetTags() map[string]string
}
// Disposition ...
diff --git a/transcoder.go b/transcoder.go
index c93bc4a..e4606de 100644
--- a/transcoder.go
+++ b/transcoder.go
@@ -13,7 +13,12 @@ type Transcoder interface {
Output(o string) Transcoder
OutputPipe(w *io.WriteCloser, r *io.ReadCloser) Transcoder
WithOptions(opts Options) Transcoder
+ WithStartOptions(opts Options) Transcoder
WithAdditionalOptions(opts Options) Transcoder
+ WithAdditionalStartOptions(opts Options) Transcoder
WithContext(ctx *context.Context) Transcoder
+ WithMetadata(metadata Metadata) Transcoder
+ SkipMetadata() Transcoder
GetMetadata() (Metadata, error)
+ Error() error
}
diff --git a/utils/utils.go b/utils/utils.go
index 95e9eab..473562e 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -8,15 +8,14 @@ import (
// DurToSec ...
func DurToSec(dur string) (sec float64) {
durAry := strings.Split(dur, ":")
- var secs float64
if len(durAry) != 3 {
return
}
hr, _ := strconv.ParseFloat(durAry[0], 64)
- secs = hr * (60 * 60)
+ sec = hr * (60 * 60)
min, _ := strconv.ParseFloat(durAry[1], 64)
- secs += min * (60)
+ sec += min * (60)
second, _ := strconv.ParseFloat(durAry[2], 64)
- secs += second
- return secs
+ sec += second
+ return
}