mirror of
https://github.com/xfrr/goffmpeg.git
synced 2025-09-26 19:01:19 +08:00
cleanup & fix ci (#82)
* cleanup readme & examples * upgrade go version * add makefile with basic commands * add e2e test * add gha * update .gitignore
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# specify the version
|
||||
- image: circleci/golang:1.14-buster
|
||||
environment:
|
||||
GO111MODULE: "on"
|
||||
|
||||
working_directory: /go/src/github.com/{{ORG_NAME}}/{{REPO_NAME}}
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# specify any bash command here prefixed with `run: `
|
||||
- run: sudo apt-get update && sudo apt-get install ffmpeg
|
||||
- run: go mod download
|
||||
- run: go test -failfast -v -run=. ./...
|
25
.github/workflows/build_and_test.yml
vendored
Normal file
25
.github/workflows/build_and_test.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Go
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.21.x"
|
||||
- name: Install dependencies
|
||||
run: go get .
|
||||
- name: Install FFmpeg
|
||||
run: sudo apt-get update && sudo apt-get install ffmpeg
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
- name: Test with the Go CLI
|
||||
run: go test --failfast -v ./...
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -20,6 +20,10 @@ main-test.go
|
||||
goffmpeg
|
||||
coverage.out
|
||||
|
||||
# IDE
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
.idea
|
||||
|
||||
# test results
|
||||
test_results
|
||||
|
7
Makefile
Normal file
7
Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
test:
|
||||
go test -v ./... -coverprofile=coverage.out -covermode=atomic -coverpkg=./...
|
||||
|
||||
coverage-html:
|
||||
go tool cover -html=coverage.out
|
278
README.md
278
README.md
@@ -1,273 +1,41 @@
|
||||
# Goffmpeg
|
||||
[](https://www.codacy.com/app/francisco.romero/goffmpeg?utm_source=github.com&utm_medium=referral&utm_content=xfrr/goffmpeg&utm_campaign=Badge_Grade)
|
||||
[](https://dl.circleci.com/status-badge/redirect/gh/xfrr/goffmpeg/tree/master)
|
||||
[](https://goreportcard.com/report/github.com/xfrr/goffmpeg)
|
||||
[](https://godoc.org/github.com/xfrr/goffmpeg)
|
||||
[](
|
||||
|
||||
FFMPEG wrapper written in GO which allows to obtain the progress.
|
||||
FFMPEG wrapper written in GO
|
||||
|
||||
## V2
|
||||
New implementation with an easy-to-use API and interfaces to extend the transcoding capabilities.
|
||||
> New implementation with an easy-to-use API and interfaces to extend the transcoding capabilities.
|
||||
> https://github.com/floostack/transcoder
|
||||
|
||||
# Dependencies
|
||||
## Features
|
||||
|
||||
- [x] Transcoding
|
||||
- [x] Streaming
|
||||
- [x] Progress
|
||||
- [x] Filters
|
||||
- [x] Thumbnails
|
||||
- [x] Watermark
|
||||
- [ ] Concatenation
|
||||
- [ ] Subtitles
|
||||
|
||||
## Dependencies
|
||||
- [FFmpeg](https://www.ffmpeg.org/)
|
||||
- [FFProbe](https://www.ffmpeg.org/ffprobe.html)
|
||||
|
||||
# Supported platforms
|
||||
## Supported platforms
|
||||
|
||||
- Linux
|
||||
- OS X
|
||||
- Windows
|
||||
|
||||
# Getting started
|
||||
## How to transcode a media file
|
||||
## Installation
|
||||
Install the package with the following command:
|
||||
```shell
|
||||
go get github.com/xfrr/goffmpeg
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/xfrr/goffmpeg/transcoder"
|
||||
)
|
||||
|
||||
var inputPath = "/data/testmov"
|
||||
var outputPath = "/data/testmp4.mp4"
|
||||
|
||||
func main() {
|
||||
|
||||
// Create new instance of transcoder
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
// Initialize transcoder passing the input file path and output file path
|
||||
err := trans.Initialize( inputPath, outputPath )
|
||||
// Handle error...
|
||||
|
||||
// Start transcoder process without checking progress
|
||||
done := trans.Run(false)
|
||||
|
||||
// This channel is used to wait for the process to end
|
||||
err = <-done
|
||||
// Handle error...
|
||||
|
||||
}
|
||||
```
|
||||
## How to get the transcoding progress
|
||||
```go
|
||||
...
|
||||
func main() {
|
||||
|
||||
// Create new instance of transcoder
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
// Initialize transcoder passing the input file path and output file path
|
||||
err := trans.Initialize( inputPath, outputPath )
|
||||
// Handle error...
|
||||
|
||||
// Start transcoder process with progress checking
|
||||
done := trans.Run(true)
|
||||
|
||||
// Returns a channel to get the transcoding progress
|
||||
progress := trans.Output()
|
||||
|
||||
// Example of printing transcoding progress
|
||||
for msg := range progress {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
// This channel is used to wait for the transcoding process to end
|
||||
err = <-done
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## How to pipe in data using the [pipe protocol](https://ffmpeg.org/ffmpeg-protocols.html#pipe)
|
||||
Creating an input pipe will return [\*io.PipeReader](https://golang.org/pkg/io/#PipeReader), and creating an output pipe will return [\*io.PipeWriter](https://golang.org/pkg/io/#PipeWriter). An example is shown which uses `cat` to pipe in data, and [ioutil.ReadAll](https://golang.org/pkg/io/ioutil/#ReadAll) to read data as bytes from the pipe.
|
||||
```go
|
||||
func main() {
|
||||
|
||||
// Create new instance of transcoder
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
// Initialize an empty transcoder
|
||||
err := trans.InitializeEmptyTranscoder()
|
||||
// Handle error...
|
||||
|
||||
// Create a command such that its output should be passed as stdin to ffmpeg
|
||||
cmd := exec.Command("cat", "/path/to/file")
|
||||
|
||||
// Create an input pipe to write to, which will return *io.PipeWriter
|
||||
w, err := trans.CreateInputPipe()
|
||||
|
||||
cmd.Stdout = w
|
||||
|
||||
// Create an output pipe to read from, which will return *io.PipeReader.
|
||||
// Must also specify the output container format
|
||||
r, err := trans.CreateOutputPipe("mp4")
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer r.Close()
|
||||
defer wg.Done()
|
||||
|
||||
// Read data from output pipe
|
||||
data, err := ioutil.ReadAll(r)
|
||||
// Handle error and data...
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer w.Close()
|
||||
err := cmd.Run()
|
||||
// Handle error...
|
||||
}()
|
||||
|
||||
// Start transcoder process without checking progress
|
||||
done := trans.Run(false)
|
||||
|
||||
// This channel is used to wait for the transcoding process to end
|
||||
err = <-done
|
||||
// Handle error...
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
```
|
||||
|
||||
# Progress properties
|
||||
```go
|
||||
type Progress struct {
|
||||
FramesProcessed string
|
||||
CurrentTime string
|
||||
CurrentBitrate string
|
||||
Progress float64
|
||||
Speed string
|
||||
}
|
||||
```
|
||||
# Media setters
|
||||
Those options can be set before starting the transcoding.
|
||||
```js
|
||||
SetAspect
|
||||
SetResolution
|
||||
SetVideoBitRate
|
||||
SetVideoBitRateTolerance
|
||||
SetVideoMaxBitrate
|
||||
SetVideoMinBitRate
|
||||
SetVideoCodec
|
||||
SetVframes
|
||||
SetFrameRate
|
||||
SetAudioRate
|
||||
SetSkipAudio
|
||||
SetSkipVideo
|
||||
SetMaxKeyFrame
|
||||
SetMinKeyFrame
|
||||
SetKeyframeInterval
|
||||
SetAudioCodec
|
||||
SetAudioBitRate
|
||||
SetAudioChannels
|
||||
SetBufferSize
|
||||
SetThreads
|
||||
SetPreset
|
||||
SetTune
|
||||
SetAudioProfile
|
||||
SetVideoProfile
|
||||
SetDuration
|
||||
SetDurationInput
|
||||
SetSeekTime
|
||||
SetSeekTimeInput
|
||||
SetSeekUsingTsInput
|
||||
SetQuality
|
||||
SetStrict
|
||||
SetSingleFile
|
||||
SetCopyTs
|
||||
SetMuxDelay
|
||||
SetHideBanner
|
||||
SetInputPath
|
||||
SetNativeFramerateInput
|
||||
SetRtmpLive
|
||||
SetHlsListSize
|
||||
SetHlsSegmentDuration
|
||||
SetHlsPlaylistType
|
||||
SetHlsMasterPlaylistName
|
||||
SetHlsSegmentFilename
|
||||
SetHttpMethod
|
||||
SetHttpKeepAlive
|
||||
SetOutputPath
|
||||
SetOutputFormat
|
||||
SetAudioFilter
|
||||
SetAudioVariableBitrate
|
||||
SetCompressionLevel
|
||||
SetFilter
|
||||
SetInputInitialOffset
|
||||
SetInputPipeCommand
|
||||
SetMapMetadata
|
||||
SetMetadata
|
||||
SetStreamIds
|
||||
SetTags
|
||||
SetVideoFilter
|
||||
```
|
||||
Example
|
||||
```golang
|
||||
func main() {
|
||||
|
||||
// Create new instance of transcoder
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
// Initialize transcoder passing the input file path and output file path
|
||||
err := trans.Initialize( inputPath, outputPath )
|
||||
// Handle error...
|
||||
|
||||
// SET FRAME RATE TO MEDIAFILE
|
||||
trans.MediaFile().SetFrameRate(70)
|
||||
// SET ULTRAFAST PRESET TO MEDIAFILE
|
||||
trans.MediaFile().SetPreset("ultrafast")
|
||||
|
||||
// Start transcoder process to check progress
|
||||
done := trans.Run(true)
|
||||
|
||||
// Returns a channel to get the transcoding progress
|
||||
progress := trans.Output()
|
||||
|
||||
// Example of printing transcoding progress
|
||||
for msg := range progress {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
// This channel is used to wait for the transcoding process to end
|
||||
err = <-done
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Example with AES encryption :
|
||||
|
||||
More information about [HLS encryption with FFMPEG](https://hlsbook.net/how-to-encrypt-hls-video-with-ffmpeg/)
|
||||
|
||||
```bash
|
||||
# Generate key
|
||||
openssl rand 16 > enc.key
|
||||
```
|
||||
|
||||
Create key file info :
|
||||
|
||||
```enc.keyinfo
|
||||
Key URI
|
||||
Path to key file
|
||||
```
|
||||
|
||||
```golang
|
||||
func main() {
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
|
||||
trans.MediaFile().SetVideoCodec("libx264")
|
||||
|
||||
trans.MediaFile().SetHlsSegmentDuration(4)
|
||||
|
||||
trans.MediaFile().SetEncryptionKey(keyinfoPath)
|
||||
|
||||
progress := trans.Output()
|
||||
|
||||
err = <-done
|
||||
}
|
||||
```
|
||||
## Usage
|
||||
Check the [examples](./examples)
|
67
config.go
Normal file
67
config.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package goffmpeg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/xfrr/goffmpeg/pkg/cmd"
|
||||
)
|
||||
|
||||
const (
|
||||
ffmpegCommand = "ffmpeg"
|
||||
ffprobeCommand = "ffprobe"
|
||||
)
|
||||
|
||||
type Configuration struct {
|
||||
ffprobeBinPath string
|
||||
ffmpegBinPath string
|
||||
}
|
||||
|
||||
func (cfg Configuration) FFmpegBinPath() string {
|
||||
return cfg.ffmpegBinPath
|
||||
}
|
||||
|
||||
func (cfg Configuration) FFprobeBinPath() string {
|
||||
return cfg.ffprobeBinPath
|
||||
}
|
||||
|
||||
func Configure(ctx context.Context) (Configuration, error) {
|
||||
ffmpegBin, err := cmd.FindBinPath(ctx, ffmpegCommand)
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
if ffmpegBin == "" {
|
||||
return Configuration{}, errors.New("ffmpeg not found, please install it before using goffmpeg")
|
||||
}
|
||||
|
||||
ffprobeBin, err := cmd.FindBinPath(ctx, ffprobeCommand)
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
if ffprobeBin == "" {
|
||||
return Configuration{}, errors.New("ffprobe not found, please install it before using goffmpeg")
|
||||
}
|
||||
|
||||
return Configuration{
|
||||
ffmpegBinPath: normalizeBinPath(ffmpegBin),
|
||||
ffprobeBinPath: normalizeBinPath(ffprobeBin),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func normalizeBinPath(binPath string) string {
|
||||
binPath = strings.ReplaceAll(binPath, lineSeparator(), " ")
|
||||
return strings.TrimSpace(binPath)
|
||||
}
|
||||
|
||||
func lineSeparator() string {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return "\r\n"
|
||||
default:
|
||||
return "\n"
|
||||
}
|
||||
}
|
18
config_test.go
Normal file
18
config_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package goffmpeg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConfigure(t *testing.T) {
|
||||
t.Run("Should set the correct command", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cfg, err := Configure(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.NotEmpty(t, cfg.FFmpegBinPath())
|
||||
assert.NotEmpty(t, cfg.FFprobeBinPath())
|
||||
})
|
||||
}
|
BIN
e2e/fixtures/input.3gp
Normal file
BIN
e2e/fixtures/input.3gp
Normal file
Binary file not shown.
104
e2e/transcoding_test.go
Executable file
104
e2e/transcoding_test.go
Executable file
@@ -0,0 +1,104 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/xfrr/goffmpeg/transcoder"
|
||||
)
|
||||
|
||||
const (
|
||||
fixturePath = "./fixtures"
|
||||
resultsPath = "./test_results"
|
||||
)
|
||||
|
||||
var (
|
||||
// Input files
|
||||
input3gp = path.Join(fixturePath, "input.3gp")
|
||||
)
|
||||
|
||||
func TestInputNotFound(t *testing.T) {
|
||||
createResultsDir(t)
|
||||
var outputPath = path.Join(resultsPath, "notfound.mp4")
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize("notfound.3gp", outputPath)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingProgress(t *testing.T) {
|
||||
createResultsDir(t)
|
||||
|
||||
outputPath := path.Join(resultsPath, "progress.mp4")
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(input3gp, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
errCh := trans.Run(true)
|
||||
|
||||
progress := []transcoder.Progress{}
|
||||
for val := range trans.Output() {
|
||||
progress = append(progress, val)
|
||||
}
|
||||
err = <-errCh
|
||||
assert.Nil(t, err)
|
||||
assert.GreaterOrEqual(t, len(progress), 1)
|
||||
checkFileExists(t, outputPath)
|
||||
}
|
||||
|
||||
func TestTranscodePipes(t *testing.T) {
|
||||
createResultsDir(t)
|
||||
|
||||
c1 := exec.Command("cat", input3gp)
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.InitializeEmptyTranscoder()
|
||||
assert.Nil(t, err)
|
||||
|
||||
w, err := trans.CreateInputPipe()
|
||||
assert.Nil(t, err)
|
||||
c1.Stdout = w
|
||||
|
||||
r, err := trans.CreateOutputPipe("mp4")
|
||||
assert.Nil(t, err)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
_, err := ioutil.ReadAll(r)
|
||||
assert.Nil(t, err)
|
||||
|
||||
r.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
err := c1.Run()
|
||||
assert.Nil(t, err)
|
||||
w.Close()
|
||||
}()
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func createResultsDir(t *testing.T) {
|
||||
err := exec.Command("mkdir", "-p", resultsPath).Run()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func checkFileExists(t *testing.T, filepath string) {
|
||||
res, err := exec.Command("cat", filepath).Output()
|
||||
assert.Nil(t, err)
|
||||
assert.Greater(t, len(res), 0)
|
||||
}
|
BIN
examples/fixtures/input.3gp
Normal file
BIN
examples/fixtures/input.3gp
Normal file
Binary file not shown.
34
examples/hls/main.go
Normal file
34
examples/hls/main.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/xfrr/goffmpeg/transcoder"
|
||||
)
|
||||
|
||||
const (
|
||||
inputPath = "../fixtures/input.3gp"
|
||||
outputPath = "../test_results/hls-output.mp4"
|
||||
keyinfoPath = "keyinfo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
trans.MediaFile().SetVideoCodec("libx264")
|
||||
trans.MediaFile().SetHlsSegmentDuration(4)
|
||||
trans.MediaFile().SetEncryptionKey(keyinfoPath)
|
||||
|
||||
done := trans.Run(true)
|
||||
progress := trans.Output()
|
||||
for p := range progress {
|
||||
fmt.Println(p)
|
||||
}
|
||||
|
||||
fmt.Println(<-done)
|
||||
}
|
31
examples/ultrafast-preset/main.go
Normal file
31
examples/ultrafast-preset/main.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/xfrr/goffmpeg/transcoder"
|
||||
)
|
||||
|
||||
const (
|
||||
inputPath = "../fixtures/input.3gp"
|
||||
outputPath = "../test_results/ultrafast-output.mp4"
|
||||
)
|
||||
|
||||
func main() {
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
trans.MediaFile().SetPreset("ultrafast")
|
||||
|
||||
done := trans.Run(true)
|
||||
progress := trans.Output()
|
||||
for p := range progress {
|
||||
fmt.Println(p)
|
||||
}
|
||||
|
||||
fmt.Println(<-done)
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
package ffmpeg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/xfrr/goffmpeg/utils"
|
||||
)
|
||||
|
||||
// Configuration ...
|
||||
type Configuration struct {
|
||||
FfmpegBin string
|
||||
FfprobeBin string
|
||||
}
|
||||
|
||||
// Configure Get and set FFmpeg and FFprobe bin paths
|
||||
func Configure() (Configuration, error) {
|
||||
var outFFmpeg bytes.Buffer
|
||||
var outProbe bytes.Buffer
|
||||
|
||||
execFFmpegCommand := utils.GetFFmpegExec()
|
||||
execFFprobeCommand := utils.GetFFprobeExec()
|
||||
|
||||
outFFmpeg, err := utils.TestCmd(execFFmpegCommand[0], execFFmpegCommand[1])
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
outProbe, err = utils.TestCmd(execFFprobeCommand[0], execFFprobeCommand[1])
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
ffmpeg := strings.Replace(strings.Split(outFFmpeg.String(), "\n")[0], utils.LineSeparator(), "", -1)
|
||||
ffprobe := strings.Replace(strings.Split(outProbe.String(), "\n")[0], utils.LineSeparator(), "", -1)
|
||||
|
||||
cnf := Configuration{ffmpeg, ffprobe}
|
||||
return cnf, nil
|
||||
}
|
8
go.mod
8
go.mod
@@ -1,5 +1,11 @@
|
||||
module github.com/xfrr/goffmpeg
|
||||
|
||||
go 1.14
|
||||
go 1.20
|
||||
|
||||
require github.com/stretchr/testify v1.5.1
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
18
media/format.go
Normal file
18
media/format.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package media
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type Tags struct {
|
||||
Encoder string `json:"ENCODER"`
|
||||
}
|
6
media/metadata.go
Normal file
6
media/metadata.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package media
|
||||
|
||||
type Metadata struct {
|
||||
Streams []Streams `json:"streams"`
|
||||
Format Format `json:"format"`
|
||||
}
|
@@ -1,14 +1,4 @@
|
||||
package models
|
||||
|
||||
type Ffmpeg struct {
|
||||
FfmpegBinPath string
|
||||
FfprobeBinPath string
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Streams []Streams `json:"streams"`
|
||||
Format Format `json:"format"`
|
||||
}
|
||||
package media
|
||||
|
||||
type Streams struct {
|
||||
Index int
|
||||
@@ -54,28 +44,3 @@ type Disposition struct {
|
||||
VisualImpaired int `json:"visual_impaired"`
|
||||
CleanEffects int `json:"clean_effects"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type Progress struct {
|
||||
FramesProcessed string
|
||||
CurrentTime string
|
||||
CurrentBitrate string
|
||||
Progress float64
|
||||
Speed string
|
||||
}
|
||||
|
||||
type Tags struct {
|
||||
Encoder string `json:"ENCODER"`
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMedia(t *testing.T) {
|
||||
t.Run("#ObtainEncryptionKey", func(t *testing.T) {
|
||||
t.Run("Should get nil if encryptionKey is not set", func(t *testing.T) {
|
||||
mediaFile := Mediafile{}
|
||||
|
||||
require.Nil(t, mediaFile.ObtainEncryptionKey())
|
||||
})
|
||||
|
||||
t.Run("Should return file.keyinfo if it's set", func(t *testing.T) {
|
||||
mediaFile := Mediafile{encryptionKey: "file.keyinfo"}
|
||||
require.Equal(t, []string{"-hls_key_info_file", "file.keyinfo"}, mediaFile.ObtainEncryptionKey())
|
||||
})
|
||||
})
|
||||
}
|
35
pkg/cmd/exec.go
Normal file
35
pkg/cmd/exec.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func FindBinPath(ctx context.Context, command string) (string, error) {
|
||||
if command == "" {
|
||||
return "", fmt.Errorf("command cannot be empty")
|
||||
}
|
||||
|
||||
path, err := execBufferOutput(ctx, getFindCommand(), command)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func execBufferOutput(ctx context.Context, command string, args ...string) (string, error) {
|
||||
var out bytes.Buffer
|
||||
|
||||
c := exec.CommandContext(ctx, command, args...)
|
||||
c.Stdout = &out
|
||||
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s: %s", c.String(), err)
|
||||
}
|
||||
|
||||
return out.String(), nil
|
||||
}
|
16
pkg/cmd/find.go
Normal file
16
pkg/cmd/find.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package cmd
|
||||
|
||||
import "runtime"
|
||||
|
||||
var (
|
||||
platform = runtime.GOOS
|
||||
)
|
||||
|
||||
func getFindCommand() string {
|
||||
switch platform {
|
||||
case "windows":
|
||||
return "where"
|
||||
default:
|
||||
return "which"
|
||||
}
|
||||
}
|
21
pkg/duration/duration.go
Normal file
21
pkg/duration/duration.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package duration
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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)
|
||||
min, _ := strconv.ParseFloat(durAry[1], 64)
|
||||
secs += min * (60)
|
||||
second, _ := strconv.ParseFloat(durAry[2], 64)
|
||||
secs += second
|
||||
return secs
|
||||
}
|
@@ -1,231 +0,0 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/xfrr/goffmpeg/transcoder"
|
||||
)
|
||||
|
||||
func TestInputNotFound(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/nf"
|
||||
var outputPath = "/tmp/ffmpeg/out/nf.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscoding3GP(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/3gp"
|
||||
var outputPath = "/tmp/ffmpeg/out/3gp.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingAVI(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/avi"
|
||||
var outputPath = "/tmp/ffmpeg/out/avi.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingFLV(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/flv"
|
||||
var outputPath = "/tmp/ffmpeg/out/flv.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingMKV(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/mkv"
|
||||
var outputPath = "/tmp/ffmpeg/out/mkv.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingMOV(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/mov"
|
||||
var outputPath = "/tmp/ffmpeg/out/mov.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingMPEG(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/mpeg"
|
||||
var outputPath = "/tmp/ffmpeg/out/mpeg.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingOGG(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/ogg"
|
||||
var outputPath = "/tmp/ffmpeg/out/ogg.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingWAV(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/wav"
|
||||
var outputPath = "/tmp/ffmpeg/out/wav.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingWEBM(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/webm"
|
||||
var outputPath = "/tmp/ffmpeg/out/webm.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingWMV(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/wmv"
|
||||
var outputPath = "/tmp/ffmpeg/out/wmv.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodingProgress(t *testing.T) {
|
||||
|
||||
var inputPath = "/tmp/ffmpeg/avi"
|
||||
var outputPath = "/tmp/ffmpeg/out/avi.mp4"
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.Initialize(inputPath, outputPath)
|
||||
assert.Nil(t, err)
|
||||
|
||||
done := trans.Run(true)
|
||||
for val := range trans.Output() {
|
||||
if &val != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTranscodePipes(t *testing.T) {
|
||||
c1 := exec.Command("cat", "/tmp/ffmpeg/mkv")
|
||||
|
||||
trans := new(transcoder.Transcoder)
|
||||
|
||||
err := trans.InitializeEmptyTranscoder()
|
||||
assert.Nil(t, err)
|
||||
|
||||
w, err := trans.CreateInputPipe()
|
||||
assert.Nil(t, err)
|
||||
c1.Stdout = w
|
||||
|
||||
r, err := trans.CreateOutputPipe("mp4")
|
||||
assert.Nil(t, err)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
_, err := ioutil.ReadAll(r)
|
||||
assert.Nil(t, err)
|
||||
|
||||
r.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
err := c1.Run()
|
||||
assert.Nil(t, err)
|
||||
w.Close()
|
||||
}()
|
||||
done := trans.Run(false)
|
||||
err = <-done
|
||||
assert.Nil(t, err)
|
||||
|
||||
wg.Wait()
|
||||
}
|
9
transcoder/progress.go
Normal file
9
transcoder/progress.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package transcoder
|
||||
|
||||
type Progress struct {
|
||||
FramesProcessed string
|
||||
CurrentTime string
|
||||
CurrentBitrate string
|
||||
Progress float64
|
||||
Speed string
|
||||
}
|
@@ -3,6 +3,7 @@ package transcoder
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -12,18 +13,18 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/xfrr/goffmpeg/ffmpeg"
|
||||
"github.com/xfrr/goffmpeg/models"
|
||||
"github.com/xfrr/goffmpeg/utils"
|
||||
"github.com/xfrr/goffmpeg"
|
||||
"github.com/xfrr/goffmpeg/media"
|
||||
"github.com/xfrr/goffmpeg/pkg/duration"
|
||||
)
|
||||
|
||||
// Transcoder Main struct
|
||||
type Transcoder struct {
|
||||
stdErrPipe io.ReadCloser
|
||||
stdStdinPipe io.WriteCloser
|
||||
process *exec.Cmd
|
||||
mediafile *models.Mediafile
|
||||
configuration ffmpeg.Configuration
|
||||
stdErrPipe io.ReadCloser
|
||||
stdStdinPipe io.WriteCloser
|
||||
process *exec.Cmd
|
||||
mediafile *media.File
|
||||
configuration goffmpeg.Configuration
|
||||
whiteListProtocols []string
|
||||
}
|
||||
|
||||
@@ -43,12 +44,12 @@ func (t *Transcoder) SetProcess(cmd *exec.Cmd) {
|
||||
}
|
||||
|
||||
// SetMediaFile Set the media file
|
||||
func (t *Transcoder) SetMediaFile(v *models.Mediafile) {
|
||||
func (t *Transcoder) SetMediaFile(v *media.File) {
|
||||
t.mediafile = v
|
||||
}
|
||||
|
||||
// SetConfiguration Set the transcoding configuration
|
||||
func (t *Transcoder) SetConfiguration(v ffmpeg.Configuration) {
|
||||
func (t *Transcoder) SetConfiguration(v goffmpeg.Configuration) {
|
||||
t.configuration = v
|
||||
}
|
||||
|
||||
@@ -62,18 +63,18 @@ func (t Transcoder) Process() *exec.Cmd {
|
||||
}
|
||||
|
||||
// MediaFile Get the ttranscoding media file.
|
||||
func (t Transcoder) MediaFile() *models.Mediafile {
|
||||
func (t Transcoder) MediaFile() *media.File {
|
||||
return t.mediafile
|
||||
}
|
||||
|
||||
// FFmpegExec Get FFmpeg Bin path
|
||||
func (t Transcoder) FFmpegExec() string {
|
||||
return t.configuration.FfmpegBin
|
||||
return t.configuration.FFmpegBinPath()
|
||||
}
|
||||
|
||||
// FFprobeExec Get FFprobe Bin path
|
||||
func (t Transcoder) FFprobeExec() string {
|
||||
return t.configuration.FfprobeBin
|
||||
return t.configuration.FFprobeBinPath()
|
||||
}
|
||||
|
||||
// GetCommand Build and get command
|
||||
@@ -90,18 +91,18 @@ func (t Transcoder) GetCommand() []string {
|
||||
|
||||
// InitializeEmptyTranscoder initializes the fields necessary for a blank transcoder
|
||||
func (t *Transcoder) InitializeEmptyTranscoder() error {
|
||||
var Metadata models.Metadata
|
||||
var Metadata media.Metadata
|
||||
|
||||
var err error
|
||||
cfg := t.configuration
|
||||
if len(cfg.FfmpegBin) == 0 || len(cfg.FfprobeBin) == 0 {
|
||||
cfg, err = ffmpeg.Configure()
|
||||
if len(cfg.FFmpegBinPath()) == 0 || len(cfg.FFprobeBinPath()) == 0 {
|
||||
cfg, err = goffmpeg.Configure(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Set new Mediafile
|
||||
MediaFile := new(models.Mediafile)
|
||||
// Set new File
|
||||
MediaFile := new(media.File)
|
||||
MediaFile.SetMetadata(Metadata)
|
||||
|
||||
// Set transcoder configuration
|
||||
@@ -159,12 +160,12 @@ func (t *Transcoder) CreateOutputPipe(containerFormat string) (*io.PipeReader, e
|
||||
func (t *Transcoder) Initialize(inputPath string, outputPath string) error {
|
||||
var err error
|
||||
var outb, errb bytes.Buffer
|
||||
var Metadata models.Metadata
|
||||
var Metadata media.Metadata
|
||||
|
||||
cfg := t.configuration
|
||||
|
||||
if len(cfg.FfmpegBin) == 0 || len(cfg.FfprobeBin) == 0 {
|
||||
cfg, err = ffmpeg.Configure()
|
||||
if len(cfg.FFmpegBinPath()) == 0 || len(cfg.FFprobeBinPath()) == 0 {
|
||||
cfg, err = goffmpeg.Configure(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -180,7 +181,7 @@ func (t *Transcoder) Initialize(inputPath string, outputPath string) error {
|
||||
command = append([]string{"-protocol_whitelist", strings.Join(t.whiteListProtocols, ",")}, command...)
|
||||
}
|
||||
|
||||
cmd := exec.Command(cfg.FfprobeBin, command...)
|
||||
cmd := exec.Command(cfg.FFprobeBinPath(), command...)
|
||||
cmd.Stdout = &outb
|
||||
cmd.Stderr = &errb
|
||||
|
||||
@@ -189,12 +190,12 @@ func (t *Transcoder) Initialize(inputPath string, outputPath string) error {
|
||||
return fmt.Errorf("error executing (%s) | error: %s | message: %s %s", command, err, outb.String(), errb.String())
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(outb.String()), &Metadata); err != nil {
|
||||
if err = json.Unmarshal(outb.Bytes(), &Metadata); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set new Mediafile
|
||||
MediaFile := new(models.Mediafile)
|
||||
// Set new File
|
||||
MediaFile := new(media.File)
|
||||
MediaFile.SetMetadata(Metadata)
|
||||
MediaFile.SetInputPath(inputPath)
|
||||
MediaFile.SetOutputPath(outputPath)
|
||||
@@ -216,7 +217,7 @@ func (t *Transcoder) Run(progress bool) <-chan error {
|
||||
command = append([]string{"-nostats", "-loglevel", "0"}, command...)
|
||||
}
|
||||
|
||||
proc := exec.Command(t.configuration.FfmpegBin, command...)
|
||||
proc := exec.Command(t.configuration.FFmpegBinPath(), command...)
|
||||
if progress {
|
||||
errStream, err := proc.StderrPipe()
|
||||
if err != nil {
|
||||
@@ -256,7 +257,7 @@ func (t *Transcoder) Run(progress bool) <-chan error {
|
||||
|
||||
go func(err error) {
|
||||
if err != nil {
|
||||
done <- fmt.Errorf("Failed Start FFMPEG (%s) with %s, message %s %s", command, err, outb.String(), errb.String())
|
||||
done <- fmt.Errorf("failed start ffmpeg (%s) with %s, message %s %s", command, err, outb.String(), errb.String())
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
@@ -266,7 +267,7 @@ func (t *Transcoder) Run(progress bool) <-chan error {
|
||||
go t.closePipes()
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed Finish FFMPEG (%s) with %s message %s %s", command, err, outb.String(), errb.String())
|
||||
err = fmt.Errorf("failed finish ffmpeg (%s) with %s message %s %s", command, err, outb.String(), errb.String())
|
||||
}
|
||||
done <- err
|
||||
close(done)
|
||||
@@ -288,13 +289,13 @@ func (t *Transcoder) Stop() error {
|
||||
}
|
||||
|
||||
// Output Returns the transcoding progress channel
|
||||
func (t Transcoder) Output() <-chan models.Progress {
|
||||
out := make(chan models.Progress)
|
||||
func (t Transcoder) Output() <-chan Progress {
|
||||
out := make(chan Progress)
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
if t.stdErrPipe == nil {
|
||||
out <- models.Progress{}
|
||||
out <- Progress{}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -328,7 +329,7 @@ func (t Transcoder) Output() <-chan models.Progress {
|
||||
scanner.Buffer(buf, bufio.MaxScanTokenSize)
|
||||
|
||||
for scanner.Scan() {
|
||||
Progress := new(models.Progress)
|
||||
Progress := new(Progress)
|
||||
line := scanner.Text()
|
||||
if strings.Contains(line, "frame=") && strings.Contains(line, "time=") && strings.Contains(line, "bitrate=") {
|
||||
var re = regexp.MustCompile(`=\s+`)
|
||||
@@ -365,7 +366,7 @@ func (t Transcoder) Output() <-chan models.Progress {
|
||||
}
|
||||
}
|
||||
|
||||
timesec := utils.DurToSec(currentTime)
|
||||
timesec := duration.DurToSec(currentTime)
|
||||
dursec, _ := strconv.ParseFloat(t.MediaFile().Metadata().Format.Duration, 64)
|
||||
//live stream check
|
||||
if dursec != 0 {
|
||||
|
@@ -1,9 +1,10 @@
|
||||
package transcoder
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xfrr/goffmpeg/models"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xfrr/goffmpeg/media"
|
||||
)
|
||||
|
||||
func TestTranscoder(t *testing.T) {
|
||||
@@ -11,7 +12,7 @@ func TestTranscoder(t *testing.T) {
|
||||
t.Run("Should not set -protocol_whitelist option if it isn't present", func(t *testing.T) {
|
||||
ts := Transcoder{}
|
||||
|
||||
ts.SetMediaFile(&models.Mediafile{})
|
||||
ts.SetMediaFile(&media.File{})
|
||||
require.NotEqual(t, ts.GetCommand()[0:2], []string{"-protocol_whitelist", "file,http,https,tcp,tls"})
|
||||
require.NotContains(t, ts.GetCommand(), "protocol_whitelist")
|
||||
})
|
||||
@@ -19,8 +20,8 @@ func TestTranscoder(t *testing.T) {
|
||||
t.Run("Should set -protocol_whitelist option if it's present", func(t *testing.T) {
|
||||
ts := Transcoder{}
|
||||
|
||||
ts.SetMediaFile(&models.Mediafile{})
|
||||
ts.SetWhiteListProtocols([]string{"file","http","https","tcp","tls"})
|
||||
ts.SetMediaFile(&media.File{})
|
||||
ts.SetWhiteListProtocols([]string{"file", "http", "https", "tcp", "tls"})
|
||||
|
||||
require.Equal(t, ts.GetCommand()[0:2], []string{"-protocol_whitelist", "file,http,https,tcp,tls"})
|
||||
})
|
||||
|
@@ -1,94 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/xfrr/goffmpeg/models"
|
||||
)
|
||||
|
||||
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)
|
||||
min, _ := strconv.ParseFloat(durAry[1], 64)
|
||||
secs += min * (60)
|
||||
second, _ := strconv.ParseFloat(durAry[2], 64)
|
||||
secs += second
|
||||
return secs
|
||||
}
|
||||
|
||||
func GetFFmpegExec() []string {
|
||||
var platform = runtime.GOOS
|
||||
var command = []string{"", "ffmpeg"}
|
||||
|
||||
switch platform {
|
||||
case "windows":
|
||||
command[0] = "where"
|
||||
break
|
||||
default:
|
||||
command[0] = "which"
|
||||
break
|
||||
}
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func GetFFprobeExec() []string {
|
||||
var platform = runtime.GOOS
|
||||
var command = []string{"", "ffprobe"}
|
||||
|
||||
switch platform {
|
||||
case "windows":
|
||||
command[0] = "where"
|
||||
break
|
||||
default:
|
||||
command[0] = "which"
|
||||
break
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
func CheckFileType(streams []models.Streams) string {
|
||||
for i := 0; i < len(streams); i++ {
|
||||
st := streams[i]
|
||||
if st.CodecType == "video" {
|
||||
return "video"
|
||||
}
|
||||
}
|
||||
|
||||
return "audio"
|
||||
}
|
||||
|
||||
func LineSeparator() string {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return "\r\n"
|
||||
default:
|
||||
return "\n"
|
||||
}
|
||||
}
|
||||
|
||||
// TestCmd ...
|
||||
func TestCmd(command string, args string) (bytes.Buffer, error) {
|
||||
var out bytes.Buffer
|
||||
|
||||
cmd := exec.Command(command, args)
|
||||
|
||||
cmd.Stdout = &out
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("%s: %s", cmd.String(), err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
Reference in New Issue
Block a user