mirror of
https://github.com/xfrr/goffmpeg.git
synced 2025-10-22 07:20:13 +08:00
Workaround transcoder Stop function, Fixed transcoder configuration allowing to set the config before initializing the process, Minor fixes
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -20,4 +20,6 @@ main-test.go
|
|||||||
goffmpeg
|
goffmpeg
|
||||||
coverage.out
|
coverage.out
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.vscode
|
||||||
.idea
|
.idea
|
@@ -1,6 +1,6 @@
|
|||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.9
|
- "1.9"
|
||||||
- master
|
- master
|
||||||
|
|
||||||
install: true
|
install: true
|
||||||
|
@@ -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()
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
|
@@ -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) {
|
||||||
|
Reference in New Issue
Block a user