mirror of
https://github.com/fxkt-tech/liv
synced 2025-09-26 20:11:20 +08:00
simple version
This commit is contained in:
77
ffmpeg.go
77
ffmpeg.go
@@ -2,72 +2,63 @@ package ffmpeg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"log"
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
|
"fxkt.tech/ffmpeg/filter"
|
||||||
|
"fxkt.tech/ffmpeg/input"
|
||||||
|
"fxkt.tech/ffmpeg/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FFmpeg struct {
|
type FFmpeg struct {
|
||||||
cmd string
|
cmd string
|
||||||
infiles []string
|
y bool // y is yes for cover output file.
|
||||||
filters []*Filter
|
inputs input.Inputs
|
||||||
outputs []*Output
|
filters filter.Filters
|
||||||
|
outputs output.Outputs
|
||||||
Sentence string
|
Sentence string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFFmpeg() *FFmpeg {
|
func Default() *FFmpeg {
|
||||||
return &FFmpeg{
|
return &FFmpeg{
|
||||||
cmd: "ffmpeg",
|
cmd: "ffmpeg",
|
||||||
|
y: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ff *FFmpeg) ChangeCmd(cmd string) {
|
func (ff *FFmpeg) Yes(y bool) {
|
||||||
ff.cmd = cmd
|
ff.y = y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ff *FFmpeg) AddInputs(inputs ...string) {
|
func (ff *FFmpeg) AddInput(inputs ...*input.Input) {
|
||||||
ff.infiles = append(ff.infiles, inputs...)
|
ff.inputs = append(ff.inputs, inputs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ff *FFmpeg) AddFilter(filters ...*Filter) {
|
func (ff *FFmpeg) AddFilter(filters ...*filter.Filter) {
|
||||||
ff.filters = append(ff.filters, filters...)
|
ff.filters = append(ff.filters, filters...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ff *FFmpeg) OutputGraph(output ...*Output) {
|
func (ff *FFmpeg) AddOutput(outputs ...*output.Output) {
|
||||||
ff.outputs = output
|
ff.outputs = append(ff.outputs, outputs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ff *FFmpeg) combination() []string {
|
func (ff *FFmpeg) Params() (params []string) {
|
||||||
var params []string
|
if ff.y {
|
||||||
params = append(params, "-y")
|
params = append(params, "-y")
|
||||||
for _, infile := range ff.infiles {
|
|
||||||
params = append(params, "-i", infile)
|
|
||||||
}
|
}
|
||||||
|
params = append(params, ff.inputs.Params()...)
|
||||||
var filtersStr []string
|
params = append(params, ff.filters.String())
|
||||||
for _, filter := range ff.filters {
|
params = append(params, ff.outputs.Params()...)
|
||||||
filtersStr = append(filtersStr, filter.String())
|
return
|
||||||
}
|
|
||||||
params = append(params, "-filter_complex", strings.Join(filtersStr, ";"))
|
|
||||||
for _, op := range ff.outputs {
|
|
||||||
params = append(params, op.params...)
|
|
||||||
}
|
|
||||||
return params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ff *FFmpeg) Run() error {
|
func (ff *FFmpeg) Run(ctx context.Context) (err error) {
|
||||||
params := ff.combination()
|
cc := exec.CommandContext(ctx, ff.cmd, ff.Params()...)
|
||||||
cmd := exec.CommandContext(context.Background(), ff.cmd, params...)
|
ff.Sentence = cc.String()
|
||||||
ff.Sentence = cmd.String()
|
retbytes, err := cc.CombinedOutput()
|
||||||
fmt.Println(ff.Sentence)
|
if err != nil {
|
||||||
outBytes, err := cmd.CombinedOutput()
|
return
|
||||||
fmt.Println(string(outBytes))
|
|
||||||
|
|
||||||
if err != nil || cmd.ProcessState.ExitCode() != 0 {
|
|
||||||
sls := strings.Split(string(outBytes), "\n")
|
|
||||||
err = errors.New(strings.ReplaceAll(strings.Join(sls[len(sls)-4:len(sls)-1], "|"), "\n", "|"))
|
|
||||||
}
|
}
|
||||||
return err
|
log.Println(string(retbytes))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
41
ffmpeg_test.go
Normal file
41
ffmpeg_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package ffmpeg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"fxkt.tech/ffmpeg/filter"
|
||||||
|
"fxkt.tech/ffmpeg/input"
|
||||||
|
"fxkt.tech/ffmpeg/output"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFFmpeg(t *testing.T) {
|
||||||
|
ff := Default()
|
||||||
|
ff.AddInput(
|
||||||
|
input.New(
|
||||||
|
input.SetI("vieo.mp4"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ff.AddFilter(
|
||||||
|
filter.New(
|
||||||
|
filter.SetInStream("0"),
|
||||||
|
filter.SetContent("scale=trunc(oh*a/2)*2:720"),
|
||||||
|
filter.SetOutStream("x720"),
|
||||||
|
),
|
||||||
|
filter.New(
|
||||||
|
filter.SetInStream("x720"),
|
||||||
|
filter.SetContent("delogo=0:0:100:100"),
|
||||||
|
filter.SetOutStream("xx720"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ff.AddOutput(
|
||||||
|
output.New(
|
||||||
|
output.SetMap("xx720"),
|
||||||
|
output.SetMap("0:a"),
|
||||||
|
output.SetMetadata("comment=yilan888"),
|
||||||
|
output.SetFile("out720.mp4"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ff.Run(context.Background())
|
||||||
|
t.Log(ff.Sentence)
|
||||||
|
}
|
30
filter.go
30
filter.go
@@ -1,30 +0,0 @@
|
|||||||
package ffmpeg
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type Filter struct {
|
|
||||||
alias string
|
|
||||||
content string
|
|
||||||
others []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFilter(alias string, content string, others ...string) *Filter {
|
|
||||||
return &Filter{
|
|
||||||
alias: alias,
|
|
||||||
content: content,
|
|
||||||
others: others,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
func (f *Filter) String() string {
|
|
||||||
var steamsAlias string
|
|
||||||
for _, other := range f.others {
|
|
||||||
steamsAlias = fmt.Sprintf("%s[%s]", steamsAlias, other)
|
|
||||||
}
|
|
||||||
var alias string
|
|
||||||
if f.alias != "" {
|
|
||||||
alias = fmt.Sprintf("[%s]", f.alias)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s%s%s", steamsAlias, f.content, alias)
|
|
||||||
}
|
|
44
filter/filter.go
Normal file
44
filter/filter.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package filter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
// instreams []string
|
||||||
|
// content string
|
||||||
|
// outstreams []string
|
||||||
|
opt *option
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(opts ...OptionFunc) *Filter {
|
||||||
|
o := &option{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
return &Filter{
|
||||||
|
opt: o,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) Params() (params []string) {
|
||||||
|
if len(f.opt.instreams) != 0 {
|
||||||
|
for _, stream := range f.opt.instreams {
|
||||||
|
params = append(params, fmt.Sprintf("[%s]", stream))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f.opt.content != "" {
|
||||||
|
params = append(params, f.opt.content)
|
||||||
|
}
|
||||||
|
if len(f.opt.outstreams) != 0 {
|
||||||
|
for _, stream := range f.opt.outstreams {
|
||||||
|
params = append(params, fmt.Sprintf("[%s]", stream))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) String() string {
|
||||||
|
return strings.Join(f.Params(), "")
|
||||||
|
}
|
27
filter/filter_option.go
Normal file
27
filter/filter_option.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package filter
|
||||||
|
|
||||||
|
type OptionFunc func(*option)
|
||||||
|
|
||||||
|
type option struct {
|
||||||
|
instreams []string
|
||||||
|
content string
|
||||||
|
outstreams []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetInStream(s string) OptionFunc {
|
||||||
|
return func(o *option) {
|
||||||
|
o.instreams = append(o.instreams, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetContent(c string) OptionFunc {
|
||||||
|
return func(o *option) {
|
||||||
|
o.content = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetOutStream(s string) OptionFunc {
|
||||||
|
return func(o *option) {
|
||||||
|
o.outstreams = append(o.outstreams, s)
|
||||||
|
}
|
||||||
|
}
|
16
filter/filters.go
Normal file
16
filter/filters.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package filter
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type Filters []*Filter
|
||||||
|
|
||||||
|
func (filters Filters) Params() (params []string) {
|
||||||
|
for _, filter := range filters {
|
||||||
|
params = append(params, filter.String())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filters Filters) String() string {
|
||||||
|
return strings.Join(filters.Params(), ";")
|
||||||
|
}
|
@@ -5,22 +5,33 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Input is 输入.
|
// Input is common input info.
|
||||||
type Input struct {
|
type Input struct {
|
||||||
i string // 输入的媒体文件
|
// i string // i is input file.
|
||||||
ss int64 // 媒体文件选择的起始时间点
|
// ss int64 // ss is starttime.
|
||||||
t int64 //从起始时间开始的持续时间
|
// t int64 // t is duration.
|
||||||
ext []string // 额外字段
|
// ext []string // extra params.
|
||||||
|
opt *option
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(opts ...OptionFunc) *Input {
|
||||||
|
o := &option{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
return &Input{
|
||||||
|
opt: o,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) Params() (params []string) {
|
func (i *Input) Params() (params []string) {
|
||||||
if i.ss != 0 {
|
if i.opt.ss != 0 {
|
||||||
params = append(params, "-ss", strconv.FormatInt(i.ss, 10))
|
params = append(params, "-ss", strconv.FormatInt(i.opt.ss, 10))
|
||||||
}
|
}
|
||||||
if i.t != 0 {
|
if i.opt.t != 0 {
|
||||||
params = append(params, "-t", strconv.FormatInt(i.t, 10))
|
params = append(params, "-t", strconv.FormatInt(i.opt.t, 10))
|
||||||
}
|
}
|
||||||
params = append(params, "-i", i.i)
|
params = append(params, "-i", i.opt.i)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
input/input_option.go
Normal file
16
input/input_option.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
type OptionFunc func(*option)
|
||||||
|
|
||||||
|
type option struct {
|
||||||
|
i string // i is input file.
|
||||||
|
ss int64 // ss is starttime.
|
||||||
|
t int64 // t is duration.
|
||||||
|
ext []string // extra params.
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetI(i string) OptionFunc {
|
||||||
|
return func(o *option) {
|
||||||
|
o.i = i
|
||||||
|
}
|
||||||
|
}
|
10
input/inputs.go
Normal file
10
input/inputs.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
type Inputs []*Input
|
||||||
|
|
||||||
|
func (inputs Inputs) Params() (params []string) {
|
||||||
|
for _, input := range inputs {
|
||||||
|
params = append(params, input.Params()...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
31
internal/pkg/strconv/strconv.go
Normal file
31
internal/pkg/strconv/strconv.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package strconv
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func ToInt32(s string) (int32, error) {
|
||||||
|
i, err := strconv.ParseInt(s, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int32(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToInt64(s string) (int64, error) {
|
||||||
|
return strconv.ParseInt(s, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToInt32ByFloatString(s string) (int32, error) {
|
||||||
|
f, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int32(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToInt64ByFloatString(s string) (int64, error) {
|
||||||
|
f, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int64(f), nil
|
||||||
|
}
|
@@ -1,6 +1,70 @@
|
|||||||
package output
|
package output
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Output is common output info.
|
||||||
type Output struct {
|
type Output struct {
|
||||||
maps []string
|
// maps []string // mean is -map.
|
||||||
f string
|
// cv, ca string // cv is c:v, ca is c:a.
|
||||||
|
// metadatas []string // mean is -metadata.
|
||||||
|
// threads int32 // thread counts, default 4.
|
||||||
|
// max_muxing_queue_size int32 // queue size when muxing, default 4086.
|
||||||
|
// movflags string // location of mp4 moov.
|
||||||
|
// f string // f is -f format.
|
||||||
|
// file string
|
||||||
|
opt *option
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(opts ...OptionFunc) *Output {
|
||||||
|
o := &option{
|
||||||
|
threads: 4,
|
||||||
|
max_muxing_queue_size: 4086,
|
||||||
|
movflags: "faststart",
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
return &Output{
|
||||||
|
opt: o,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Output) Params() (params []string) {
|
||||||
|
if len(o.opt.maps) != 0 {
|
||||||
|
for _, m := range o.opt.maps {
|
||||||
|
params = append(params, "-map", fmt.Sprintf("[%s]", m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if o.opt.cv != "" {
|
||||||
|
params = append(params, "c:v", o.opt.cv)
|
||||||
|
}
|
||||||
|
if o.opt.ca != "" {
|
||||||
|
params = append(params, "c:a", o.opt.ca)
|
||||||
|
}
|
||||||
|
if len(o.opt.metadatas) != 0 {
|
||||||
|
for _, m := range o.opt.metadatas {
|
||||||
|
params = append(params, "-metadata", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if o.opt.max_muxing_queue_size != 0 {
|
||||||
|
params = append(params, "-max_muxing_queue_size", strconv.FormatInt(int64(o.opt.max_muxing_queue_size), 10))
|
||||||
|
}
|
||||||
|
if o.opt.movflags != "" {
|
||||||
|
params = append(params, "-movflags", o.opt.movflags)
|
||||||
|
}
|
||||||
|
if o.opt.f != "" {
|
||||||
|
params = append(params, "-f", o.opt.f)
|
||||||
|
}
|
||||||
|
if o.opt.file != "" {
|
||||||
|
params = append(params, o.opt.file)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Output) String() string {
|
||||||
|
return strings.Join(o.Params(), " ")
|
||||||
}
|
}
|
||||||
|
32
output/output_option.go
Normal file
32
output/output_option.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package output
|
||||||
|
|
||||||
|
type OptionFunc func(*option)
|
||||||
|
|
||||||
|
type option struct {
|
||||||
|
maps []string // mean is -map.
|
||||||
|
cv, ca string // cv is c:v, ca is c:a.
|
||||||
|
metadatas []string // mean is -metadata.
|
||||||
|
threads int32 // thread counts, default 4.
|
||||||
|
max_muxing_queue_size int32 // queue size when muxing, default 4086.
|
||||||
|
movflags string // location of mp4 moov.
|
||||||
|
f string // f is -f format.
|
||||||
|
file string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetMap(m string) OptionFunc {
|
||||||
|
return func(o *option) {
|
||||||
|
o.maps = append(o.maps, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetMetadata(md string) OptionFunc {
|
||||||
|
return func(o *option) {
|
||||||
|
o.metadatas = append(o.metadatas, md)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetFile(f string) OptionFunc {
|
||||||
|
return func(o *option) {
|
||||||
|
o.file = f
|
||||||
|
}
|
||||||
|
}
|
10
output/outputs.go
Normal file
10
output/outputs.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package output
|
||||||
|
|
||||||
|
type Outputs []*Output
|
||||||
|
|
||||||
|
func (Outputs Outputs) Params() (params []string) {
|
||||||
|
for _, output := range Outputs {
|
||||||
|
params = append(params, output.Params()...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@@ -27,7 +27,7 @@ ffmpeg
|
|||||||
-y
|
-y
|
||||||
-t 5
|
-t 5
|
||||||
-i in.mp4 -i logo.png
|
-i in.mp4 -i logo.png
|
||||||
-filter_complex '[0]delogo=1490:40:400:100[p1];[p1]split=2[p1_hd][p1_sd];[p1_hd]scale=-1:720[p2_hd];[p2_hd][1]overlay=W-w-10:10[p_hd];[p1_sd]scale=-1:540[p2_sd];[p2_sd][1]overlay=W-w-10:10[p_sd]'
|
-filter_complex '[0]delogo=1490:40:400:100[p1];[p1]split=2[p1_hd][p1_sd];[p1_hd]scale=trunc(oh*a/2)*2:720[p2_hd];[p2_hd][1]overlay=W-w-10:10[p_hd];[p1_sd]scale=-1:540[p2_sd];[p2_sd][1]overlay=W-w-10:10[p_sd]'
|
||||||
|
|
||||||
-map '[p_hd]'
|
-map '[p_hd]'
|
||||||
-map 0:a
|
-map 0:a
|
||||||
@@ -40,4 +40,8 @@ ffmpeg
|
|||||||
out_hd.mp4
|
out_hd.mp4
|
||||||
|
|
||||||
-map '[p_sd]' -map 0:a -metadata comment=fu789sg -c:v libwz264 -c:a copy -threads 4 -max_muxing_queue_size 4086 -movflags faststart out_sd.mp4
|
-map '[p_sd]' -map 0:a -metadata comment=fu789sg -c:v libwz264 -c:a copy -threads 4 -max_muxing_queue_size 4086 -movflags faststart out_sd.mp4
|
||||||
```
|
|
||||||
|
trunc(oh*a/2)*2:720
|
||||||
|
720:trunc(ow/a/2)*2
|
||||||
|
```
|
||||||
|
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"fxkt.tech/ffmpeg"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestExample(t *testing.T) {
|
|
||||||
ff := ffmpeg.NewFFmpeg()
|
|
||||||
ff.AddInputs(
|
|
||||||
"video.mp4",
|
|
||||||
"logo.png",
|
|
||||||
)
|
|
||||||
ff.AddFilter(
|
|
||||||
ffmpeg.NewFilter("dl", `delogo=0:0:400:200`, "0"),
|
|
||||||
ffmpeg.NewFilter("d_480][d_360", `split=2`, "dl"),
|
|
||||||
ffmpeg.NewFilter("rlt_480", `scale=trunc(oh*a/2)*2:480`, "d_480"),
|
|
||||||
ffmpeg.NewFilter("rlt_360", `scale=trunc(oh*a/2)*2:360`, "d_360"),
|
|
||||||
)
|
|
||||||
ff.OutputGraph(
|
|
||||||
ffmpeg.NewOutput(
|
|
||||||
"-map", "[rlt_480]",
|
|
||||||
"-map", "0:a",
|
|
||||||
"-metadata", "comment=fu789sg",
|
|
||||||
"-c:v", "libx264", "-c:a", "copy",
|
|
||||||
"-threads", "4",
|
|
||||||
"-max_muxing_queue_size", "4086",
|
|
||||||
"-movflags", "faststart",
|
|
||||||
"out_480.mp4",
|
|
||||||
),
|
|
||||||
ffmpeg.NewOutput(
|
|
||||||
"-map", "[rlt_360]",
|
|
||||||
"-map", "0:a",
|
|
||||||
"-metadata", "comment=fu789sg",
|
|
||||||
"-c:v", "libx264", "-c:a", "copy",
|
|
||||||
"-threads", "4",
|
|
||||||
"-max_muxing_queue_size", "4086",
|
|
||||||
"-movflags", "faststart",
|
|
||||||
"out_480.mp4",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
err := ff.Run()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user