Workaround transcoder Stop function, Fixed transcoder configuration allowing to set the config before initializing the process, Minor fixes

This commit is contained in:
frr
2019-01-15 13:36:26 +01:00
parent 00c255eb2d
commit e29085bab4
5 changed files with 85 additions and 31 deletions

2
.gitignore vendored
View File

@@ -20,4 +20,6 @@ main-test.go
goffmpeg goffmpeg
coverage.out coverage.out
.DS_Store
.vscode
.idea .idea

View File

@@ -1,6 +1,6 @@
language: go language: go
go: go:
- 1.9 - "1.9"
- master - master
install: true install: true

View File

@@ -2,19 +2,23 @@ package ffmpeg
import ( import (
"bytes" "bytes"
"github.com/xfrr/goffmpeg/utils"
"os/exec" "os/exec"
"strings" "strings"
"github.com/xfrr/goffmpeg/utils"
) )
// Configuration ...
type Configuration struct { type Configuration struct {
FfmpegBin string FfmpegBin string
FfprobeBin string FfprobeBin string
} }
// Configure Get and set FFmpeg and FFprobe bin paths
func Configure() (Configuration, error) { func Configure() (Configuration, error) {
var outFFmpeg bytes.Buffer var outFFmpeg bytes.Buffer
var outProbe bytes.Buffer var outProbe bytes.Buffer
execFFmpegCommand := utils.GetFFmpegExec() execFFmpegCommand := utils.GetFFmpegExec()
execFFprobeCommand := utils.GetFFprobeExec() execFFprobeCommand := utils.GetFFprobeExec()

View File

@@ -1,14 +1,14 @@
package test package test
import ( import (
"fmt"
"github.com/xfrr/goffmpeg/transcoder"
"testing" "testing"
"github.com/xfrr/goffmpeg/transcoder"
) )
func TestInputNotFound(t *testing.T) { func TestInputNotFound(t *testing.T) {
var inputPath = "/data/testmk" var inputPath = "/data/testmkv"
var outputPath = "/data/testmp4.mp4" var outputPath = "/data/testmp4.mp4"
trans := new(transcoder.Transcoder) trans := new(transcoder.Transcoder)
@@ -231,7 +231,7 @@ func TestTranscodingWMV(t *testing.T) {
func TestTranscodingProgress(t *testing.T) { func TestTranscodingProgress(t *testing.T) {
var inputPath = "/data/test.mp4" var inputPath = "/data/testavi"
var outputPath = "/data/testmp4.mp4" var outputPath = "/data/testmp4.mp4"
trans := new(transcoder.Transcoder) trans := new(transcoder.Transcoder)
@@ -244,8 +244,11 @@ func TestTranscodingProgress(t *testing.T) {
done := trans.Run(true) done := trans.Run(true)
for val := range trans.Output() { for val := range trans.Output() {
fmt.Printf("%+v\n", val) if &val != nil {
break
}
} }
err = <-done err = <-done
if err != nil { if err != nil {
t.Error(err) t.Error(err)

View File

@@ -6,87 +6,107 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/xfrr/goffmpeg/ffmpeg"
"github.com/xfrr/goffmpeg/models"
"github.com/xfrr/goffmpeg/utils"
"io" "io"
"os" "os"
"os/exec" "os/exec"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/xfrr/goffmpeg/ffmpeg"
"github.com/xfrr/goffmpeg/models"
"github.com/xfrr/goffmpeg/utils"
) )
// Transcoder Main struct
type Transcoder struct { type Transcoder struct {
stdErrPipe io.ReadCloser stdErrPipe io.ReadCloser
stdStdinPipe io.WriteCloser
process *exec.Cmd process *exec.Cmd
mediafile *models.Mediafile mediafile *models.Mediafile
configuration ffmpeg.Configuration configuration ffmpeg.Configuration
} }
// SetProcessStderrPipe Set the STDERR pipe
func (t *Transcoder) SetProcessStderrPipe(v io.ReadCloser) { func (t *Transcoder) SetProcessStderrPipe(v io.ReadCloser) {
t.stdErrPipe = v t.stdErrPipe = v
} }
// SetProcessStdinPipe Set the STDIN pipe
func (t *Transcoder) SetProcessStdinPipe(v io.WriteCloser) {
t.stdStdinPipe = v
}
// SetProcess Set the transcoding process
func (t *Transcoder) SetProcess(cmd *exec.Cmd) { func (t *Transcoder) SetProcess(cmd *exec.Cmd) {
t.process = cmd t.process = cmd
} }
// SetMediaFile Set the media file
func (t *Transcoder) SetMediaFile(v *models.Mediafile) { func (t *Transcoder) SetMediaFile(v *models.Mediafile) {
t.mediafile = v t.mediafile = v
} }
// SetConfiguration Set the transcoding configuration
func (t *Transcoder) SetConfiguration(v ffmpeg.Configuration) { func (t *Transcoder) SetConfiguration(v ffmpeg.Configuration) {
t.configuration = v t.configuration = v
} }
/*** GETTERS ***/ // Process Get transcoding process
func (t Transcoder) Process() *exec.Cmd { func (t Transcoder) Process() *exec.Cmd {
return t.process return t.process
} }
// MediaFile Get the ttranscoding media file.
func (t Transcoder) MediaFile() *models.Mediafile { func (t Transcoder) MediaFile() *models.Mediafile {
return t.mediafile return t.mediafile
} }
// FFmpegExec Get FFmpeg Bin path
func (t Transcoder) FFmpegExec() string { func (t Transcoder) FFmpegExec() string {
return t.configuration.FfmpegBin return t.configuration.FfmpegBin
} }
// FFprobeExec Get FFprobe Bin path
func (t Transcoder) FFprobeExec() string { func (t Transcoder) FFprobeExec() string {
return t.configuration.FfprobeBin return t.configuration.FfprobeBin
} }
// GetCommand Build and get command
func (t Transcoder) GetCommand() []string { func (t Transcoder) GetCommand() []string {
media := t.mediafile media := t.mediafile
rcommand := append([]string{"-y"}, media.ToStrCommand()...) rcommand := append([]string{"-y"}, media.ToStrCommand()...)
return rcommand return rcommand
} }
/*** FUNCTIONS ***/ // Initialize Init the transcoding process
func (t *Transcoder) Initialize(inputPath string, outputPath string) error { func (t *Transcoder) Initialize(inputPath string, outputPath string) error {
var err error
var out bytes.Buffer
var Metadata models.Metadata
configuration, err := ffmpeg.Configure() cfg := t.configuration
if err != nil {
fmt.Println(err) if len(cfg.FfmpegBin) == 0 || len(cfg.FfprobeBin) == 0 {
return err cfg, err = ffmpeg.Configure()
if err != nil {
fmt.Println(err)
return err
}
} }
if inputPath == "" { if inputPath == "" {
return errors.New("error: transcoder.Initialize -> inputPath missing") return errors.New("error on transcoder.Initialize: inputPath missing")
} }
_, err = os.Stat(inputPath) _, err = os.Stat(inputPath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
return errors.New("error: transcoder.Initialize -> input file not found") return errors.New("error on transcoder.Initialize: input file not found")
} }
command := []string{"-i", inputPath, "-print_format", "json", "-show_format", "-show_streams", "-show_error"} command := []string{"-i", inputPath, "-print_format", "json", "-show_format", "-show_streams", "-show_error"}
cmd := exec.Command(configuration.FfprobeBin, command...) cmd := exec.Command(cfg.FfprobeBin, command...)
var out bytes.Buffer
cmd.Stdout = &out cmd.Stdout = &out
err = cmd.Run() err = cmd.Run()
@@ -94,8 +114,6 @@ func (t *Transcoder) Initialize(inputPath string, outputPath string) error {
return fmt.Errorf("Failed FFPROBE (%s) with %s, message %s", command, err, out.String()) return fmt.Errorf("Failed FFPROBE (%s) with %s, message %s", command, err, out.String())
} }
var Metadata models.Metadata
if err = json.Unmarshal([]byte(out.String()), &Metadata); err != nil { if err = json.Unmarshal([]byte(out.String()), &Metadata); err != nil {
return err return err
} }
@@ -108,32 +126,45 @@ func (t *Transcoder) Initialize(inputPath string, outputPath string) error {
// Set transcoder configuration // Set transcoder configuration
t.SetMediaFile(MediaFile) t.SetMediaFile(MediaFile)
t.SetConfiguration(configuration) t.SetConfiguration(cfg)
return nil return nil
} }
// Run Starts the transcoding process
func (t *Transcoder) Run(progress bool) <-chan error { func (t *Transcoder) Run(progress bool) <-chan error {
done := make(chan error) done := make(chan error)
command := t.GetCommand() command := t.GetCommand()
if !progress { if !progress {
command = append([]string{"-nostats", "-loglevel", "0"}, command...) command = append([]string{"-nostats", "-loglevel", "0"}, command...)
} }
proc := exec.Command(t.configuration.FfmpegBin, command...) proc := exec.Command(t.configuration.FfmpegBin, command...)
if progress { if progress {
errStream, err := proc.StderrPipe() errStream, err := proc.StderrPipe()
if err != nil { if err != nil {
fmt.Println("Progress not available: " + err.Error()) fmt.Println("Progress not available: " + err.Error())
} else { } else {
t.SetProcessStderrPipe(errStream) t.stdErrPipe = errStream
} }
} }
out := &bytes.Buffer{} stdin, err := proc.StdinPipe()
proc.Stdout = out if nil != err {
fmt.Println("Stdin not available: " + err.Error())
}
t.stdStdinPipe = stdin
out := &bytes.Buffer{}
if progress {
proc.Stdout = out
}
err = proc.Start()
err := proc.Start()
t.SetProcess(proc) t.SetProcess(proc)
go func(err error, out *bytes.Buffer) { go func(err error, out *bytes.Buffer) {
if err != nil { if err != nil {
@@ -152,6 +183,19 @@ func (t *Transcoder) Run(progress bool) <-chan error {
return done return done
} }
// Stop Ends the transcoding process
func (t *Transcoder) Stop() error {
if t.process != nil {
stdin := t.stdStdinPipe
if stdin != nil {
stdin.Write([]byte("q\n"))
}
}
return nil
}
// Output Returns the transcoding progress channel
func (t Transcoder) Output() <-chan models.Progress { func (t Transcoder) Output() <-chan models.Progress {
out := make(chan models.Progress) out := make(chan models.Progress)
@@ -160,9 +204,10 @@ func (t Transcoder) Output() <-chan models.Progress {
if t.stdErrPipe == nil { if t.stdErrPipe == nil {
out <- models.Progress{} out <- models.Progress{}
return return
} else {
defer t.stdErrPipe.Close()
} }
defer t.stdErrPipe.Close()
scanner := bufio.NewScanner(t.stdErrPipe) scanner := bufio.NewScanner(t.stdErrPipe)
split := func(data []byte, atEOF bool) (advance int, token []byte, spliterror error) { split := func(data []byte, atEOF bool) (advance int, token []byte, spliterror error) {