feat: simple snapshot

This commit is contained in:
Justyer
2022-10-11 20:17:08 +08:00
parent 6699fa5304
commit e1d3444bfc
12 changed files with 222 additions and 31 deletions

4
.gitignore vendored
View File

@@ -15,4 +15,6 @@
# vendor/ # vendor/
*.mp4 *.mp4
*.png *.png
*.jpg
*.jpeg

View File

@@ -3,5 +3,6 @@ package liv
import "errors" import "errors"
var ( var (
ErrParamsInvalid = errors.New("params is invalid") ErrParamsInvalid = errors.New("params is invalid")
ErrParamsInvalid2 = errors.New("interval required when frame_type is 1(normal frame)")
) )

View File

@@ -0,0 +1,34 @@
package main
import (
"context"
"fmt"
"github.com/fxkt-tech/liv"
"github.com/fxkt-tech/liv/ffmpeg"
)
func main() {
var (
ctx = context.Background()
params = &liv.SnapshotParams{
Infile: "in.mp4",
Outfile: "ss/%05d.jpg",
StartTime: 3,
FrameType: 0,
Num: 1,
Interval: 1,
}
)
tc := liv.NewSnapshot(
liv.FFmpegOptions(
ffmpeg.Binary("ffmpeg"),
// ffmpeg.Dry(true),
),
)
err := tc.Simple(ctx, params)
if err != nil {
fmt.Println(err)
}
}

View File

@@ -24,6 +24,9 @@ type CommonFilter struct {
} }
func (cf *CommonFilter) Name(index int) string { func (cf *CommonFilter) Name(index int) string {
if cf.name == "" {
return ""
}
return fmt.Sprintf("[%s%d]", cf.name, index) return fmt.Sprintf("[%s%d]", cf.name, index)
} }
@@ -153,6 +156,22 @@ func Delogo(name string, x, y, w, h int64) Filter {
} }
} }
func Select(name, expr string) Filter {
return &CommonFilter{
name: name,
content: fmt.Sprintf("select=%s", expr),
counts: 1,
}
}
func FPS(name string, fps *math.Rational[int32]) Filter {
return &CommonFilter{
name: name,
content: fmt.Sprintf("fps=fps=%d/%d", fps.Num, fps.Den),
counts: 1,
}
}
// filter slice // filter slice
type Filters []Filter type Filters []Filter

View File

@@ -26,3 +26,7 @@ func (n *Naming) Gen() string {
func (n *Naming) Gen64() string { func (n *Naming) Gen64() string {
return fmt.Sprintf("%x", math.MaxInt64) return fmt.Sprintf("%x", math.MaxInt64)
} }
func (n *Naming) Empty() string {
return ""
}

View File

@@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/fxkt-tech/liv/ffmpeg/codec"
) )
type OutputOption func(*Output) type OutputOption func(*Output)
@@ -102,6 +100,12 @@ func VarStreamMap(s string) OutputOption {
} }
} }
func VSync(vsync string) OutputOption {
return func(o *Output) {
o.vsync = vsync
}
}
// hls // hls
func HLSSegmentType(value string) OutputOption { func HLSSegmentType(value string) OutputOption {
@@ -121,16 +125,19 @@ func HLSPlaylistType(value string) OutputOption {
o.hls_playlist_type = value o.hls_playlist_type = value
} }
} }
func HLSTime(value int32) OutputOption { func HLSTime(value int32) OutputOption {
return func(o *Output) { return func(o *Output) {
o.hls_time = value o.hls_time = value
} }
} }
func MasterPlName(value string) OutputOption { func MasterPlName(value string) OutputOption {
return func(o *Output) { return func(o *Output) {
o.master_pl_name = value o.master_pl_name = value
} }
} }
func HLSSegmentFilename(value string) OutputOption { func HLSSegmentFilename(value string) OutputOption {
return func(o *Output) { return func(o *Output) {
o.hls_segment_filename = value o.hls_segment_filename = value
@@ -166,6 +173,7 @@ type Output struct {
f string // f is -f format. f string // f is -f format.
file string file string
var_stream_map string var_stream_map string
vsync string
// hls configs // hls configs
hls_segment_type string hls_segment_type string
@@ -182,12 +190,12 @@ type Output struct {
func New(opts ...OutputOption) *Output { func New(opts ...OutputOption) *Output {
op := &Output{ op := &Output{
threads: 4, // threads: 4,
max_muxing_queue_size: 4086, // max_muxing_queue_size: 4086,
movflags: "faststart", // movflags: "faststart",
cv: codec.X264, // cv: codec.X264,
ca: codec.Copy, // ca: codec.Copy,
hls_time: 2, // hls_time: 2,
} }
for _, o := range opts { for _, o := range opts {
o(op) o(op)
@@ -227,6 +235,9 @@ func (o *Output) Params() (params []string) {
if o.var_stream_map != "" { if o.var_stream_map != "" {
params = append(params, "-var_stream_map", o.var_stream_map) params = append(params, "-var_stream_map", o.var_stream_map)
} }
if o.vsync != "" {
params = append(params, "-vsync", o.vsync)
}
if o.f == "hls" { if o.f == "hls" {
if o.hls_segment_type != "" { if o.hls_segment_type != "" {
params = append(params, "-hls_segment_type", o.hls_segment_type) params = append(params, "-hls_segment_type", o.hls_segment_type)

View File

@@ -17,3 +17,14 @@ func CeilOddInt32(n int32) int32 {
} }
return n - 1 return n - 1
} }
type Rational[T constraints.Integer] struct {
Num, Den T
}
func Fraction[T constraints.Integer](num, den T) *Rational[T] {
return &Rational[T]{
Num: num,
Den: den,
}
}

22
options.go Normal file
View File

@@ -0,0 +1,22 @@
package liv
import "github.com/fxkt-tech/liv/ffmpeg"
type Option func(*options)
type options struct {
ffmpegOpts []ffmpeg.FFmpegOption
ffprobeOpts []ffmpeg.FFprobeOption
}
func FFmpegOptions(ffmpegOpts ...ffmpeg.FFmpegOption) Option {
return func(o *options) {
o.ffmpegOpts = ffmpegOpts
}
}
func FFprobeOptions(ffprobeOpts ...ffmpeg.FFprobeOption) Option {
return func(o *options) {
o.ffprobeOpts = ffprobeOpts
}
}

68
snapshot.go Normal file
View File

@@ -0,0 +1,68 @@
package liv
import (
"context"
"github.com/fxkt-tech/liv/ffmpeg"
"github.com/fxkt-tech/liv/ffmpeg/filter"
"github.com/fxkt-tech/liv/ffmpeg/input"
"github.com/fxkt-tech/liv/ffmpeg/naming"
"github.com/fxkt-tech/liv/ffmpeg/output"
"github.com/fxkt-tech/liv/internal/math"
)
type Snapshot struct {
*options
spec *SnapshotSpec
}
func NewSnapshot(opts ...Option) *Snapshot {
o := &options{}
for _, opt := range opts {
opt(o)
}
ss := &Snapshot{
spec: NewSnapshotSpec(),
options: o,
}
return ss
}
func (ss *Snapshot) Simple(ctx context.Context, params *SnapshotParams) error {
err := ss.spec.CheckSatified(params)
if err != nil {
return err
}
var (
nm = naming.New()
inputs input.Inputs
filters filter.Filters
outputOptions []output.OutputOption
)
inputs = append(inputs, input.WithTime(params.StartTime, 0, params.Infile))
// 使用普通帧截图时,必须要传截图间隔,除非只截一张
switch params.FrameType {
case 0: // 关键帧
filters = append(filters, filter.Select(nm.Empty(), "'eq(pict_type,I)'"))
outputOptions = append(outputOptions, output.VSync("vfr"))
case 1:
if params.Num != 1 {
filters = append(filters, filter.FPS(nm.Empty(), math.Fraction(1, params.Interval)))
}
}
outputOptions = append(outputOptions,
output.Vframes(params.Num),
output.File(params.Outfile),
)
return ffmpeg.NewFFmpeg(ss.ffmpegOpts...).
AddInput(inputs...).
AddFilter(filters...).
AddOutput(output.New(outputOptions...)).
Run(ctx)
}

12
snapshot_params.go Normal file
View File

@@ -0,0 +1,12 @@
package liv
type SnapshotParams struct {
Infile string
Outfile string
StartTime float64
Interval int32
Num int32
FrameType int32
NotBlack bool
NotWhite bool
}

17
snapshot_spec.go Normal file
View File

@@ -0,0 +1,17 @@
package liv
type SnapshotSpec struct{}
func NewSnapshotSpec() *SnapshotSpec {
return &SnapshotSpec{}
}
func (*SnapshotSpec) CheckSatified(params *SnapshotParams) error {
if params == nil {
return ErrParamsInvalid
}
if params.FrameType == 1 && params.Num > 1 && params.Interval <= 0 {
return ErrParamsInvalid2
}
return nil
}

View File

@@ -11,33 +11,20 @@ import (
"github.com/fxkt-tech/liv/ffmpeg/output" "github.com/fxkt-tech/liv/ffmpeg/output"
) )
type TranscodeOption func(*Transcode)
func FFmpegOptions(ffmpegOpts ...ffmpeg.FFmpegOption) TranscodeOption {
return func(t *Transcode) {
t.ffmpegOpts = ffmpegOpts
}
}
func FFprobeOptions(ffprobeOpts ...ffmpeg.FFprobeOption) TranscodeOption {
return func(t *Transcode) {
t.ffprobeOpts = ffprobeOpts
}
}
type Transcode struct { type Transcode struct {
ffmpegOpts []ffmpeg.FFmpegOption *options
ffprobeOpts []ffmpeg.FFprobeOption
spec *TranscodeSpec spec *TranscodeSpec
} }
func NewTranscode(opts ...TranscodeOption) *Transcode { func NewTranscode(opts ...Option) *Transcode {
tc := &Transcode{ o := &options{}
spec: NewTranscodeSpec(),
}
for _, opt := range opts { for _, opt := range opts {
opt(tc) opt(o)
}
tc := &Transcode{
spec: NewTranscodeSpec(),
options: o,
} }
return tc return tc
} }
@@ -100,6 +87,9 @@ func (tc *Transcode) SimpleMP4(ctx context.Context, params *TranscodeParams) err
output.Map(filter.SelectStream(0, filter.StreamAudio, false)), output.Map(filter.SelectStream(0, filter.StreamAudio, false)),
output.VideoCodec(codec.X264), output.VideoCodec(codec.X264),
output.AudioCodec(codec.AAC), output.AudioCodec(codec.AAC),
output.MovFlags("faststart"),
output.Thread(4),
output.MaxMuxingQueueSize(4086),
output.File(sub.Outfile), output.File(sub.Outfile),
} }
// 处理在每一路输出流的裁剪 // 处理在每一路输出流的裁剪