Browse Source

ffmpeg

master
RealXLFD 3 months ago
parent
commit
7baac09435
  1. 2
      cliapps/ffmpeg/ffmpeg_external.go
  2. 14
      cliapps/ffmpeg/init.go
  3. 123
      cliapps/ffmpeg/metadata.go
  4. 80
      cliapps/ffmpeg/options.go
  5. 14
      cliapps/vips/header.go
  6. 9
      cliapps/vips/init.go
  7. 4
      cliapps/vips/options.go
  8. 26
      cliapps/vips/vips_external.go
  9. 15
      cmdui/input.go

2
cliapps/ffmpeg/ffmpeg_external.go

@ -1,5 +1,5 @@
package ffmpeg
func Convert() {
func Convert(src, dst string) {
}

14
cliapps/ffmpeg/init.go

@ -22,7 +22,10 @@ func Init(path string) error {
if !stat.IsDir() {
return os.ErrNotExist
}
app := filepath.Join(path, `ffmpeg`)
app := filepath.Join(
path,
`ffmpeg`,
)
// var isFound bool
cmdFfmpegVersion := exec.Command(
app,
@ -41,3 +44,12 @@ func Init(path string) error {
}
return errors.New(`ffmpeg version not found`)
}
type ErrFFMPEGNotInitialized error
func checkFFMPEG() error {
if ffmpegPath == `` || ffmpegVersion == `` {
return ErrFFMPEGNotInitialized(errors.New(`ffmpeg not initialized`))
}
return nil
}

123
cliapps/ffmpeg/metadata.go

@ -1 +1,124 @@
package ffmpeg
import (
"encoding/json"
"os/exec"
"path/filepath"
)
type Metadata struct {
Streams []struct {
Index int `json:"index"`
CodecName string `json:"codec_name"`
CodecLongName string `json:"codec_long_name"`
Profile string `json:"profile"`
CodecType string `json:"codec_type"`
CodecTagString string `json:"codec_tag_string"`
CodecTag string `json:"codec_tag"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
CodedWidth int `json:"coded_width,omitempty"`
CodedHeight int `json:"coded_height,omitempty"`
ClosedCaptions int `json:"closed_captions,omitempty"`
FilmGrain int `json:"film_grain,omitempty"`
HasBFrames int `json:"has_b_frames,omitempty"`
SampleAspectRatio string `json:"sample_aspect_ratio,omitempty"`
DisplayAspectRatio string `json:"display_aspect_ratio,omitempty"`
PixFmt string `json:"pix_fmt,omitempty"`
Level int `json:"level,omitempty"`
ColorRange string `json:"color_range,omitempty"`
ColorSpace string `json:"color_space,omitempty"`
ColorTransfer string `json:"color_transfer,omitempty"`
ColorPrimaries string `json:"color_primaries,omitempty"`
ChromaLocation string `json:"chroma_location,omitempty"`
FieldOrder string `json:"field_order,omitempty"`
Refs int `json:"refs,omitempty"`
IsAvc string `json:"is_avc,omitempty"`
NalLengthSize string `json:"nal_length_size,omitempty"`
Id string `json:"id"`
RFrameRate string `json:"r_frame_rate"`
AvgFrameRate string `json:"avg_frame_rate"`
TimeBase string `json:"time_base"`
StartPts int `json:"start_pts"`
StartTime string `json:"start_time"`
DurationTs int `json:"duration_ts"`
Duration string `json:"duration"`
BitRate string `json:"bit_rate"`
BitsPerRawSample string `json:"bits_per_raw_sample,omitempty"`
ExtradataSize int `json:"extradata_size"`
Disposition struct {
Default int `json:"default"`
Dub int `json:"dub"`
Original int `json:"original"`
Comment int `json:"comment"`
Lyrics int `json:"lyrics"`
Karaoke int `json:"karaoke"`
Forced int `json:"forced"`
HearingImpaired int `json:"hearing_impaired"`
VisualImpaired int `json:"visual_impaired"`
CleanEffects int `json:"clean_effects"`
AttachedPic int `json:"attached_pic"`
TimedThumbnails int `json:"timed_thumbnails"`
NonDiegetic int `json:"non_diegetic"`
Captions int `json:"captions"`
Descriptions int `json:"descriptions"`
Metadata int `json:"metadata"`
Dependent int `json:"dependent"`
StillImage int `json:"still_image"`
} `json:"disposition"`
Tags map[string]string `json:"tags"`
SampleFmt string `json:"sample_fmt,omitempty"`
SampleRate string `json:"sample_rate,omitempty"`
Channels int `json:"channels,omitempty"`
ChannelLayout string `json:"channel_layout,omitempty"`
BitsPerSample int `json:"bits_per_sample,omitempty"`
InitialPadding int `json:"initial_padding,omitempty"`
} `json:"streams"`
Format struct {
Filename string `json:"filename"`
NbStreams int `json:"nb_streams"`
NbPrograms int `json:"nb_programs"`
NbStreamGroups int `json:"nb_stream_groups"`
FormatName string `json:"format_name"`
FormatLongName string `json:"format_long_name"`
StartTime string `json:"start_time"`
Duration string `json:"duration"`
Size string `json:"size"`
BitRate string `json:"bit_rate"`
ProbeScore int `json:"probe_score"`
Tags map[string]string `json:"tags"`
} `json:"format"`
}
func GetMetadata(src string) (*Metadata, error) {
err := checkFFMPEG()
if err != nil {
return nil, err
}
appPath := filepath.Join(
ffmpegPath,
`ffprobe`,
)
cmd := exec.Command(
appPath,
"-hide_banner",
"-show_format",
"-print_format",
"json",
"-show_streams",
src,
)
out, err := cmd.Output()
if err != nil {
return nil, err
}
var metadata Metadata
err = json.Unmarshal(
out,
&metadata,
)
if err != nil {
return nil, err
}
return &metadata, nil
}

80
cliapps/ffmpeg/options.go

@ -0,0 +1,80 @@
package ffmpeg
import "strconv"
type Option interface {
Option() string
}
type VideoEncoder string
func (v VideoEncoder) Option() string {
return string(v)
}
type H264CRF int
func (c H264CRF) Option() string {
intC := int(c)
if intC < 0 || intC > 51 {
panic("H264CRF must be between 0 and 51")
}
return strconv.Itoa(intC)
}
type H264Preset string
func (p H264Preset) Option() string {
return string(p)
}
const (
H264PresetUltrafast H264Preset = `ultrafast`
H264PresetSuperfast H264Preset = `superfast`
H264PresetVeryfast H264Preset = `veryfast`
H264PresetFaster H264Preset = `faster`
H264PresetFast H264Preset = `fast`
H264PresetMedium H264Preset = `medium`
H264PresetSlow H264Preset = `slow`
H264PresetSlower H264Preset = `slower`
H264PresetVeryslow H264Preset = `veryslow`
H264PresetPlacebo H264Preset = `placebo`
)
type H264Tune string
func (t H264Tune) Option() string {
return string(t)
}
const (
H264TuneFilm H264Tune = `film`
H264TuneAnimation H264Tune = `animation`
H264TuneGrain H264Tune = `grain`
H264TuneStillimage H264Tune = `stillimage`
H264TunePsnr H264Tune = `psnr`
H264TuneSsim H264Tune = `ssim`
H264TuneFastdecode H264Tune = `fastdecode`
H264TuneZerolatency H264Tune = `zerolatency`
)
type H264Profile string
func (p H264Profile) Option() string {
return string(p)
}
const (
H264ProfileBaseline H264Profile = `baseline`
H264ProfileMain H264Profile = `main`
H264ProfileHigh H264Profile = `high`
H264ProfileHigh10 H264Profile = `high10`
H264ProfileHigh422 H264Profile = `high422`
H264ProfileHigh444 H264Profile = `high444`
)
type CustomOption string
func (o CustomOption) Option() string {
return string(o)
}

14
cliapps/vips/header.go

@ -7,8 +7,8 @@ import (
"regexp"
)
// ImageInfo 是一个包含有关图像信息的结构体,并支持 JSON 编组
type ImageInfo struct {
// Header 是一个包含有关图像信息的结构体,并支持 JSON 编组
type Header struct {
Width int `json:"width"`
Height int `json:"height"`
Bands int `json:"bands"`
@ -24,7 +24,11 @@ type ImageInfo struct {
BitsPerSample int `json:"bits-per-sample"`
}
func GetInfo(file string) (*ImageInfo, error) {
func GetHeader(file string) (*Header, error) {
err := checkVips()
if err != nil {
return nil, err
}
details, err := exec.Command(
filepath.Join(
vipsPath,
@ -41,8 +45,8 @@ func GetInfo(file string) (*ImageInfo, error) {
string(details),
-1,
)
// 创建 ImageInfo 结构体并填充数据
var info ImageInfo
// 创建 Header 结构体并填充数据
var info Header
for _, match := range matches {
switch match[1] {
case "width":

9
cliapps/vips/init.go

@ -66,3 +66,12 @@ func Init(path string) error {
)
return nil
}
type ErrVipsNotInitialized error
func checkVips() error {
if vipsPath == `` || vipsVersion == `` {
return ErrVipsNotInitialized(errors.New(`VIPS not initialized`))
}
return nil
}

4
cliapps/vips/static.go → cliapps/vips/options.go

@ -1,6 +1,8 @@
package vips
import "strconv"
import (
"strconv"
)
const (
KernelNearest Kernel = `nearest`

26
cliapps/vips/vips_external.go

@ -7,26 +7,26 @@ import (
type Process struct {
FilePath string
OutputPath string
ImageInfo *ImageInfo
ImageInfo *Header
Finish bool
}
// New 创建一个新的VIPS处理流
func New(file string, out string) (*Process, error) {
stat, err := os.Stat(file)
func New(src, dst string) (*Process, error) {
stat, err := os.Stat(src)
if err != nil {
return nil, err
}
if stat.IsDir() {
return nil, os.ErrNotExist
}
info, err := GetInfo(file)
info, err := GetHeader(src)
if err != nil {
return nil, err
}
return &Process{
FilePath: file,
OutputPath: out,
FilePath: src,
OutputPath: dst,
ImageInfo: info,
Finish: false,
}, nil
@ -34,6 +34,10 @@ func New(file string, out string) (*Process, error) {
// Resize 调整图片大小
func (p *Process) Resize(width, height int, options ...Option) error {
err := checkVips()
if err != nil {
return err
}
var quality Quality
var kernel Kernel
var scale float64
@ -57,7 +61,7 @@ func (p *Process) Resize(width, height int, options ...Option) error {
scale = float64(width) / float64(p.ImageInfo.Width)
}
if !force && scale >= 1 {
err := vipsCopy(
err = vipsCopy(
p.FilePath,
p.OutputPath,
int(quality))
@ -65,7 +69,7 @@ func (p *Process) Resize(width, height int, options ...Option) error {
return err
}
} else {
err := vipsResize(
err = vipsResize(
p.FilePath,
p.OutputPath,
scale,
@ -82,7 +86,11 @@ func (p *Process) Resize(width, height int, options ...Option) error {
// Convert 转换图片格式
func (p *Process) Convert() error {
err := vipsCopy(
err := checkVips()
if err != nil {
return err
}
err = vipsCopy(
p.FilePath,
p.OutputPath,
0,

15
cmdui/input.go

@ -3,14 +3,16 @@ package cmdui
import (
"bufio"
"os"
"strings"
)
func GetPath() (input string) {
func GetPath() (path string) {
scanner := bufio.NewScanner(os.Stdin)
for {
if scanner.Scan() {
input = scanner.Text()
stat, err := os.Stat(input)
input := scanner.Text()
path = strings.Trim(input, `"`)
stat, err := os.Stat(path)
if err != nil {
printer("路径不存在!\n")
} else if stat.IsDir() {
@ -22,12 +24,13 @@ func GetPath() (input string) {
return
}
func GetFile() (input string) {
func GetFile() (path string) {
scanner := bufio.NewScanner(os.Stdin)
for {
if scanner.Scan() {
input = scanner.Text()
stat, err := os.Stat(input)
input := scanner.Text()
path = strings.Trim(input, `"`)
stat, err := os.Stat(path)
if err != nil {
printer("路径不存在!\n")
} else if !stat.IsDir() {

Loading…
Cancel
Save