diff --git a/README.md b/README.md index c05615c..1f882b3 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ SetVideoCodec SetVframes SetFrameRate SetAudioRate +SetSkipAudio +SetSkipVideo SetMaxKeyFrame SetMinKeyFrame SetKeyframeInterval diff --git a/ffmpeg/ffmpeg.go b/ffmpeg/ffmpeg.go index ebba99c..677dee1 100644 --- a/ffmpeg/ffmpeg.go +++ b/ffmpeg/ffmpeg.go @@ -1,42 +1,42 @@ package ffmpeg import ( - "github.com/xfrr/goffmpeg/utils" - "os/exec" - "bytes" - "strings" + "bytes" + "github.com/xfrr/goffmpeg/utils" + "os/exec" + "strings" ) type Configuration struct { - FfmpegBin string - FfprobeBin string + FfmpegBin string + FfprobeBin string } func Configure() (Configuration, error) { - var outFFmpeg bytes.Buffer - var outProbe bytes.Buffer - execFFmpegCommand := utils.GetFFmpegExec() - execFFprobeCommand := utils.GetFFprobeExec() + var outFFmpeg bytes.Buffer + var outProbe bytes.Buffer + execFFmpegCommand := utils.GetFFmpegExec() + execFFprobeCommand := utils.GetFFprobeExec() - cmdFFmpeg := exec.Command(execFFmpegCommand[0], execFFmpegCommand[1]) - cmdProbe := exec.Command(execFFprobeCommand[0], execFFprobeCommand[1]) + cmdFFmpeg := exec.Command(execFFmpegCommand[0], execFFmpegCommand[1]) + cmdProbe := exec.Command(execFFprobeCommand[0], execFFprobeCommand[1]) - cmdFFmpeg.Stdout = &outFFmpeg - cmdProbe.Stdout = &outProbe + cmdFFmpeg.Stdout = &outFFmpeg + cmdProbe.Stdout = &outProbe - err := cmdFFmpeg.Run() - if err != nil { - return Configuration{}, err - } + err := cmdFFmpeg.Run() + if err != nil { + return Configuration{}, err + } - err = cmdProbe.Run() - if err != nil { - return Configuration{}, err - } + err = cmdProbe.Run() + if err != nil { + return Configuration{}, err + } - ffmpeg := strings.Replace(outFFmpeg.String(), utils.LineSeparator(), "", -1) - fprobe := strings.Replace(outProbe.String(), utils.LineSeparator(), "", -1) + ffmpeg := strings.Replace(outFFmpeg.String(), utils.LineSeparator(), "", -1) + fprobe := strings.Replace(outProbe.String(), utils.LineSeparator(), "", -1) - cnf := Configuration{ffmpeg, fprobe} - return cnf, nil + cnf := Configuration{ffmpeg, fprobe} + return cnf, nil } diff --git a/models/media.go b/models/media.go index 760b925..43ae3e0 100644 --- a/models/media.go +++ b/models/media.go @@ -1,173 +1,175 @@ package models import ( - "strings" - "strconv" - "fmt" - "reflect" + "fmt" + "reflect" + "strconv" + "strings" ) type Mediafile struct { - aspect string - resolution string - videoBitRate int - videoBitRateTolerance int - videoMaxBitRate int - videoMinBitrate int - videoCodec string - vframes int - frameRate int - audioRate int - maxKeyframe int - minKeyframe int - keyframeInterval int - audioCodec string - audioBitrate int - audioChannels int - bufferSize int - threads int - preset string - tune string - audioProfile string - videoProfile string - target string - duration string - durationInput string - seekTime string - quality int - strict int - muxDelay string - seekUsingTsInput bool - seekTimeInput string - inputPath string - hideBanner bool - outputPath string - outputFormat string - copyTs bool - nativeFramerateInput bool - inputInitialOffset string - rtmpLive string - hlsPlaylistType string - hlsListSize int - hlsSegmentDuration int - httpMethod string - httpKeepAlive bool - streamIds map[int]string - metadata Metadata - filter string + aspect string + resolution string + videoBitRate int + videoBitRateTolerance int + videoMaxBitRate int + videoMinBitrate int + videoCodec string + vframes int + frameRate int + audioRate int + maxKeyframe int + minKeyframe int + keyframeInterval int + audioCodec string + audioBitrate int + audioChannels int + bufferSize int + threads int + preset string + tune string + audioProfile string + videoProfile string + target string + duration string + durationInput string + seekTime string + quality int + strict int + muxDelay string + seekUsingTsInput bool + seekTimeInput string + inputPath string + hideBanner bool + outputPath string + outputFormat string + copyTs bool + nativeFramerateInput bool + inputInitialOffset string + rtmpLive string + hlsPlaylistType string + hlsListSize int + hlsSegmentDuration int + httpMethod string + httpKeepAlive bool + streamIds map[int]string + metadata Metadata + filter string + skipVideo bool + skipAudio bool } /*** SETTERS ***/ -func (m *Mediafile) SetFilter(v string){ - m.filter = v +func (m *Mediafile) SetFilter(v string) { + m.filter = v } func (m *Mediafile) SetAspect(v string) { - m.aspect = v + m.aspect = v } func (m *Mediafile) SetResolution(v string) { - m.resolution = v + m.resolution = v } func (m *Mediafile) SetVideoBitRate(v int) { - m.videoBitRate = v + m.videoBitRate = v } func (m *Mediafile) SetVideoBitRateTolerance(v int) { - m.videoBitRateTolerance = v + m.videoBitRateTolerance = v } func (m *Mediafile) SetVideoMaxBitrate(v int) { - m.videoMaxBitRate = v + m.videoMaxBitRate = v } func (m *Mediafile) SetVideoMinBitRate(v int) { - m.videoMinBitrate = v + m.videoMinBitrate = v } func (m *Mediafile) SetVideoCodec(v string) { - m.videoCodec = v + m.videoCodec = v } func (m *Mediafile) SetVframes(v int) { - m.vframes = v + m.vframes = v } func (m *Mediafile) SetFrameRate(v int) { - m.frameRate = v + m.frameRate = v } func (m *Mediafile) SetAudioRate(v int) { - m.audioRate = v + m.audioRate = v } func (m *Mediafile) SetMaxKeyFrame(v int) { - m.maxKeyframe = v + m.maxKeyframe = v } func (m *Mediafile) SetMinKeyFrame(v int) { - m.minKeyframe = v + m.minKeyframe = v } func (m *Mediafile) SetKeyframeInterval(v int) { - m.keyframeInterval = v + m.keyframeInterval = v } func (m *Mediafile) SetAudioCodec(v string) { - m.audioCodec = v + m.audioCodec = v } func (m *Mediafile) SetAudioBitRate(v int) { - m.audioBitrate = v + m.audioBitrate = v } func (m *Mediafile) SetAudioChannels(v int) { - m.audioChannels = v + m.audioChannels = v } func (m *Mediafile) SetBufferSize(v int) { - m.bufferSize = v + m.bufferSize = v } func (m *Mediafile) SetThreads(v int) { - m.threads = v + m.threads = v } func (m *Mediafile) SetPreset(v string) { - m.preset = v + m.preset = v } func (m *Mediafile) SetTune(v string) { - m.tune = v + m.tune = v } func (m *Mediafile) SetAudioProfile(v string) { - m.audioProfile = v + m.audioProfile = v } func (m *Mediafile) SetVideoProfile(v string) { - m.videoProfile = v + m.videoProfile = v } func (m *Mediafile) SetDuration(v string) { - m.duration = v + m.duration = v } func (m *Mediafile) SetDurationInput(v string) { - m.durationInput = v + m.durationInput = v } func (m *Mediafile) SetSeekTime(v string) { - m.seekTime = v + m.seekTime = v } func (m *Mediafile) SetSeekTimeInput(v string) { - m.seekTimeInput = v + m.seekTimeInput = v } func (m *Mediafile) SetQuality(v int) { - m.quality = v + m.quality = v } func (m *Mediafile) SetStrict(v int) { @@ -175,643 +177,677 @@ func (m *Mediafile) SetStrict(v int) { } func (m *Mediafile) SetSeekUsingTsInput(val bool) { - m.seekUsingTsInput = val + m.seekUsingTsInput = val } func (m *Mediafile) SetCopyTs(val bool) { - m.copyTs = val + m.copyTs = val } func (m *Mediafile) SetInputPath(val string) { - m.inputPath = val + m.inputPath = val } func (m *Mediafile) SetHideBanner(val bool) { - m.hideBanner = val + m.hideBanner = val } func (m *Mediafile) SetMuxDelay(val string) { - m.muxDelay = val + m.muxDelay = val } func (m *Mediafile) SetOutputPath(val string) { - m.outputPath = val + m.outputPath = val } func (m *Mediafile) SetOutputFormat(val string) { - m.outputFormat = val + m.outputFormat = val } func (m *Mediafile) SetNativeFramerateInput(val bool) { - m.nativeFramerateInput = val + m.nativeFramerateInput = val } func (m *Mediafile) SetRtmpLive(val string) { - m.rtmpLive = val + m.rtmpLive = val } func (m *Mediafile) SetHlsListSize(val int) { - m.hlsListSize = val + m.hlsListSize = val } func (m *Mediafile) SetHlsSegmentDuration(val int) { - m.hlsSegmentDuration = val + m.hlsSegmentDuration = val } func (m *Mediafile) SetHlsPlaylistType(val string) { - m.hlsPlaylistType = val + m.hlsPlaylistType = val } func (m *Mediafile) SetHttpMethod(val string) { - m.httpMethod = val + m.httpMethod = val } func (m *Mediafile) SetHttpKeepAlive(val bool) { - m.httpKeepAlive = val + m.httpKeepAlive = val } func (m *Mediafile) SetInputInitialOffset(val string) { - m.inputInitialOffset = val + m.inputInitialOffset = val } func (m *Mediafile) SetStreamIds(val map[int]string) { - m.streamIds = val + m.streamIds = val +} + +func (m *Mediafile) SetSkipVideo(val bool) { + m.skipVideo = val +} + +func (m *Mediafile) SetSkipAudio(val bool) { + m.skipAudio = val } func (m *Mediafile) SetMetadata(v Metadata) { - m.metadata = v + m.metadata = v } /*** GETTERS ***/ func (m *Mediafile) Filter() string { - return m.filter + return m.filter } func (m *Mediafile) Aspect() string { - return m.aspect + return m.aspect } func (m *Mediafile) Resolution() string { - return m.resolution + return m.resolution } func (m *Mediafile) VideoBitrate() int { - return m.videoBitRate + return m.videoBitRate } func (m *Mediafile) VideoBitRateTolerance() int { - return m.videoBitRateTolerance + return m.videoBitRateTolerance } func (m *Mediafile) VideoMaxBitRate() int { - return m.videoMaxBitRate + return m.videoMaxBitRate } func (m *Mediafile) VideoMinBitRate() int { - return m.videoMinBitrate + return m.videoMinBitrate } func (m *Mediafile) VideoCodec() string { - return m.videoCodec + return m.videoCodec } func (m *Mediafile) Vframes() int { - return m.vframes + return m.vframes } func (m *Mediafile) FrameRate() int { - return m.frameRate + return m.frameRate } func (m *Mediafile) AudioRate() int { - return m.audioRate + return m.audioRate } func (m *Mediafile) MaxKeyFrame() int { - return m.maxKeyframe + return m.maxKeyframe } func (m *Mediafile) MinKeyFrame() int { - return m.minKeyframe + return m.minKeyframe } func (m *Mediafile) KeyFrameInterval() int { - return m.keyframeInterval + return m.keyframeInterval } func (m *Mediafile) AudioCodec() string { - return m.audioCodec + return m.audioCodec } func (m *Mediafile) AudioBitrate() int { - return m.audioBitrate + return m.audioBitrate } func (m *Mediafile) AudioChannels() int { - return m.audioChannels + return m.audioChannels } func (m *Mediafile) BufferSize() int { - return m.bufferSize + return m.bufferSize } func (m *Mediafile) Threads() int { - return m.threads + return m.threads } func (m *Mediafile) Target() string { - return m.target + return m.target } func (m *Mediafile) Duration() string { - return m.duration + return m.duration } func (m *Mediafile) DurationInput() string { - return m.durationInput + return m.durationInput } func (m *Mediafile) SeekTime() string { - return m.seekTime + return m.seekTime } func (m *Mediafile) Preset() string { - return m.preset + return m.preset } func (m *Mediafile) AudioProfile() string { - return m.audioProfile + return m.audioProfile } func (m *Mediafile) VideoProfile() string { - return m.videoProfile + return m.videoProfile } func (m *Mediafile) Tune() string { - return m.tune + return m.tune } func (m *Mediafile) SeekTimeInput() string { - return m.seekTimeInput + return m.seekTimeInput } func (m *Mediafile) Quality() int { - return m.quality + return m.quality } func (m *Mediafile) Strict() int { - return m.strict + return m.strict } func (m *Mediafile) MuxDelay() string { - return m.muxDelay + return m.muxDelay } func (m *Mediafile) SeekUsingTsInput() bool { - return m.seekUsingTsInput + return m.seekUsingTsInput } func (m *Mediafile) CopyTs() bool { - return m.copyTs + return m.copyTs } func (m *Mediafile) InputPath() string { - return m.inputPath + return m.inputPath } func (m *Mediafile) HideBanner() bool { - return m.hideBanner + return m.hideBanner } func (m *Mediafile) OutputPath() string { - return m.outputPath + return m.outputPath } func (m *Mediafile) OutputFormat() string { - return m.outputFormat + return m.outputFormat } func (m *Mediafile) NativeFramerateInput() bool { - return m.nativeFramerateInput + return m.nativeFramerateInput } func (m *Mediafile) RtmpLive() string { - return m.rtmpLive + return m.rtmpLive } func (m *Mediafile) HlsListSize() int { - return m.hlsListSize + return m.hlsListSize } func (m *Mediafile) HlsSegmentDuration() int { - return m.hlsSegmentDuration + return m.hlsSegmentDuration } func (m *Mediafile) HlsPlaylistType() string { - return m.hlsPlaylistType + return m.hlsPlaylistType } func (m *Mediafile) InputInitialOffset() string { - return m.inputInitialOffset + return m.inputInitialOffset } func (m *Mediafile) HttpMethod() string { - return m.httpMethod + return m.httpMethod } func (m *Mediafile) HttpKeepAlive() bool { - return m.httpKeepAlive + return m.httpKeepAlive } func (m *Mediafile) StreamIds() map[int]string { - return m.streamIds + return m.streamIds +} + +func (m *Mediafile) SkipVideo() bool { + return m.skipVideo +} + +func (m *Mediafile) SkipAudio() bool { + return m.skipAudio } func (m *Mediafile) Metadata() Metadata { - return m.metadata + return m.metadata } /** OPTS **/ func (m *Mediafile) ToStrCommand() []string { - var strCommand []string + var strCommand []string - opts := []string { - "SeekTimeInput", - "SeekUsingTsInput", - "NativeFramerateInput", - "DurationInput", - "RtmpLive", - "InputInitialOffset", - "InputPath", - "HideBanner", + opts := []string{ + "SeekTimeInput", + "SeekUsingTsInput", + "NativeFramerateInput", + "DurationInput", + "RtmpLive", + "InputInitialOffset", + "InputPath", + "HideBanner", - "Aspect", - "Resolution", - "FrameRate", - "AudioRate", - "VideoCodec", - "Vframes", - "VideoBitRate", - "VideoBitRateTolerance", - "VideoMaxBitRate", - "VideoMinBitRate", - "VideoProfile", - "AudioCodec", - "AudioBitRate", - "AudioChannels", - "AudioProfile", - "Quality", - "Strict", - "BufferSize", - "MuxDelay", - "Threads", - "KeyframeInterval", - "Preset", - "Tune", - "Target", - "SeekTime", - "Duration", - "CopyTs", - "StreamIds", - "OutputFormat", - "HlsListSize", - "HlsSegmentDuration", - "HlsPlaylistType", - "Filter", - "HttpMethod", - "HttpKeepAlive", - "OutputPath", - } - for _, name := range opts { - opt := reflect.ValueOf(m).MethodByName(fmt.Sprintf("Obtain%s", name)) - if (opt != reflect.Value{}) { - result := opt.Call([]reflect.Value{}) + "Aspect", + "Resolution", + "FrameRate", + "AudioRate", + "VideoCodec", + "Vframes", + "VideoBitRate", + "VideoBitRateTolerance", + "VideoMaxBitRate", + "VideoMinBitRate", + "VideoProfile", + "SkipVideo", + "AudioCodec", + "AudioBitRate", + "AudioChannels", + "AudioProfile", + "SkipAudio", + "Quality", + "Strict", + "BufferSize", + "MuxDelay", + "Threads", + "KeyframeInterval", + "Preset", + "Tune", + "Target", + "SeekTime", + "Duration", + "CopyTs", + "StreamIds", + "OutputFormat", + "HlsListSize", + "HlsSegmentDuration", + "HlsPlaylistType", + "Filter", + "HttpMethod", + "HttpKeepAlive", + "OutputPath", + } + for _, name := range opts { + opt := reflect.ValueOf(m).MethodByName(fmt.Sprintf("Obtain%s", name)) + if (opt != reflect.Value{}) { + result := opt.Call([]reflect.Value{}) - if val, ok := result[0].Interface().([]string); ok { - strCommand = append(strCommand, val...) - } - } - } + if val, ok := result[0].Interface().([]string); ok { + strCommand = append(strCommand, val...) + } + } + } - return strCommand + return strCommand } -func (m *Mediafile) ObtainFilter() []string{ - if m.filter != "" { - return []string{"-vf", m.filter} - } - return nil +func (m *Mediafile) ObtainFilter() []string { + if m.filter != "" { + return []string{"-vf", m.filter} + } + return nil } func (m *Mediafile) ObtainAspect() []string { - // Set aspect - if m.resolution != "" { - resolution := strings.Split(m.resolution, "x") - if len(resolution) != 0 { - width, _ := strconv.ParseFloat(resolution[0], 64) - height, _ := strconv.ParseFloat(resolution[1], 64) - return []string{"-aspect", fmt.Sprintf("%f", width/height)} - } - } + // Set aspect + if m.resolution != "" { + resolution := strings.Split(m.resolution, "x") + if len(resolution) != 0 { + width, _ := strconv.ParseFloat(resolution[0], 64) + height, _ := strconv.ParseFloat(resolution[1], 64) + return []string{"-aspect", fmt.Sprintf("%f", width/height)} + } + } - if m.aspect != "" { - return []string{"-aspect", m.aspect} - } + if m.aspect != "" { + return []string{"-aspect", m.aspect} + } return nil } func (m *Mediafile) ObtainInputPath() []string { - return []string{"-i", m.inputPath} + return []string{"-i", m.inputPath} } func (m *Mediafile) ObtainHideBanner() []string { - if m.hideBanner { - return []string{"-hide_banner"} - } - return nil + if m.hideBanner { + return []string{"-hide_banner"} + } + return nil } func (m *Mediafile) ObtainNativeFramerateInput() []string { - if m.nativeFramerateInput { - return []string{"-re"} - } - return nil + if m.nativeFramerateInput { + return []string{"-re"} + } + return nil } func (m *Mediafile) ObtainOutputPath() []string { - return []string{m.outputPath} + return []string{m.outputPath} } func (m *Mediafile) ObtainVideoCodec() []string { - if m.videoCodec != "" { - return []string{"-c:v", m.videoCodec} - } - return nil + if m.videoCodec != "" { + return []string{"-c:v", m.videoCodec} + } + return nil } func (m *Mediafile) ObtainVframes() []string { - if m.vframes != 0 { - return []string{"-vframes",fmt.Sprintf("%d", m.vframes)} - } - return nil + if m.vframes != 0 { + return []string{"-vframes", fmt.Sprintf("%d", m.vframes)} + } + return nil } func (m *Mediafile) ObtainFrameRate() []string { - if m.frameRate != 0 { - return []string{"-r",fmt.Sprintf("%d", m.frameRate)} - } - return nil + if m.frameRate != 0 { + return []string{"-r", fmt.Sprintf("%d", m.frameRate)} + } + return nil } func (m *Mediafile) ObtainAudioRate() []string { - if m.audioRate != 0 { - return []string{"-ar",fmt.Sprintf("%d", m.audioRate)} - } - return nil + if m.audioRate != 0 { + return []string{"-ar", fmt.Sprintf("%d", m.audioRate)} + } + return nil } func (m *Mediafile) ObtainResolution() []string { - if m.resolution != "" { - return []string{"-s", m.resolution} - } - return nil + if m.resolution != "" { + return []string{"-s", m.resolution} + } + return nil } func (m *Mediafile) ObtainVideoBitRate() []string { - if m.videoBitRate != 0 { - return []string{"-b:v", fmt.Sprintf("%d", m.videoBitRate)} - } - return nil + if m.videoBitRate != 0 { + return []string{"-b:v", fmt.Sprintf("%d", m.videoBitRate)} + } + return nil } func (m *Mediafile) ObtainAudioCodec() []string { - if m.audioCodec != "" { - return []string{"-c:a", m.audioCodec} - } - return nil + if m.audioCodec != "" { + return []string{"-c:a", m.audioCodec} + } + return nil } func (m *Mediafile) ObtainAudioBitRate() []string { - if m.audioBitrate != 0 { - return []string{"-b:a", fmt.Sprintf("%d", m.audioBitrate)} - } - return nil + if m.audioBitrate != 0 { + return []string{"-b:a", fmt.Sprintf("%d", m.audioBitrate)} + } + return nil } func (m *Mediafile) ObtainAudioChannels() []string { - if m.audioChannels != 0 { - return []string{"-ac",fmt.Sprintf("%d", m.audioChannels)} - } - return nil + if m.audioChannels != 0 { + return []string{"-ac", fmt.Sprintf("%d", m.audioChannels)} + } + return nil } func (m *Mediafile) ObtainVideoMaxBitRate() []string { - if m.videoMaxBitRate != 0 { - return []string{"-maxrate",fmt.Sprintf("%dk", m.videoMaxBitRate)} - } - return nil + if m.videoMaxBitRate != 0 { + return []string{"-maxrate", fmt.Sprintf("%dk", m.videoMaxBitRate)} + } + return nil } func (m *Mediafile) ObtainVideoMinBitRate() []string { - if m.videoMinBitrate != 0 { - return []string{"-minrate",fmt.Sprintf("%dk", m.videoMinBitrate)} - } - return nil + if m.videoMinBitrate != 0 { + return []string{"-minrate", fmt.Sprintf("%dk", m.videoMinBitrate)} + } + return nil } func (m *Mediafile) ObtainBufferSize() []string { - if m.bufferSize != 0 { - return []string{"-bufsize",fmt.Sprintf("%dk", m.bufferSize)} - } - return nil + if m.bufferSize != 0 { + return []string{"-bufsize", fmt.Sprintf("%dk", m.bufferSize)} + } + return nil } func (m *Mediafile) ObtainVideoBitRateTolerance() []string { - if m.videoBitRateTolerance != 0 { - return []string{"-bt",fmt.Sprintf("%dk", m.videoBitRateTolerance)} - } - return nil + if m.videoBitRateTolerance != 0 { + return []string{"-bt", fmt.Sprintf("%dk", m.videoBitRateTolerance)} + } + return nil } func (m *Mediafile) ObtainThreads() []string { - if m.threads != 0 { - return []string{"-threads",fmt.Sprintf("%d", m.threads)} - } - return nil + if m.threads != 0 { + return []string{"-threads", fmt.Sprintf("%d", m.threads)} + } + return nil } func (m *Mediafile) ObtainTarget() []string { - if m.target != "" { - return []string{"-target",m.target} - } - return nil + if m.target != "" { + return []string{"-target", m.target} + } + return nil } func (m *Mediafile) ObtainDuration() []string { - if m.duration != "" { - return []string{"-t",m.duration} - } - return nil + if m.duration != "" { + return []string{"-t", m.duration} + } + return nil } func (m *Mediafile) ObtainDurationInput() []string { - if m.durationInput != "" { - return []string{"-t",m.durationInput} - } - return nil + if m.durationInput != "" { + return []string{"-t", m.durationInput} + } + return nil } func (m *Mediafile) ObtainKeyframeInterval() []string { - if m.keyframeInterval != 0 { - return []string{"-g",fmt.Sprintf("%d", m.keyframeInterval)} - } - return nil + if m.keyframeInterval != 0 { + return []string{"-g", fmt.Sprintf("%d", m.keyframeInterval)} + } + return nil } func (m *Mediafile) ObtainSeekTime() []string { - if m.seekTime != "" { - return []string{"-ss",m.seekTime} - } - return nil + if m.seekTime != "" { + return []string{"-ss", m.seekTime} + } + return nil } func (m *Mediafile) ObtainSeekTimeInput() []string { - if m.seekTimeInput != "" { - return []string{"-ss",m.seekTimeInput} - } - return nil + if m.seekTimeInput != "" { + return []string{"-ss", m.seekTimeInput} + } + return nil } func (m *Mediafile) ObtainPreset() []string { - if m.preset != "" { - return []string{"-preset",m.preset} - } - return nil + if m.preset != "" { + return []string{"-preset", m.preset} + } + return nil } func (m *Mediafile) ObtainTune() []string { - if m.tune != "" { - return []string{"-tune",m.tune} - } - return nil + if m.tune != "" { + return []string{"-tune", m.tune} + } + return nil } func (m *Mediafile) ObtainQuality() []string { - if m.quality != 0 { - return []string{"-crf",fmt.Sprintf("%d", m.quality)} - } - return nil + if m.quality != 0 { + return []string{"-crf", fmt.Sprintf("%d", m.quality)} + } + return nil } func (m *Mediafile) ObtainStrict() []string { - if m.strict != 0 { - return []string{"-strict",fmt.Sprintf("%d", m.strict)} - } - return nil + if m.strict != 0 { + return []string{"-strict", fmt.Sprintf("%d", m.strict)} + } + return nil } func (m *Mediafile) ObtainVideoProfile() []string { - if m.videoProfile != "" { - return []string{"-profile:v",m.videoProfile} - } - return nil + if m.videoProfile != "" { + return []string{"-profile:v", m.videoProfile} + } + return nil } func (m *Mediafile) ObtainAudioProfile() []string { - if m.audioProfile != "" { - return []string{"-profile:a", m.audioProfile} - } - return nil + if m.audioProfile != "" { + return []string{"-profile:a", m.audioProfile} + } + return nil } func (m *Mediafile) ObtainCopyTs() []string { - if m.copyTs { - return []string{"-copyts"} - } - return nil + if m.copyTs { + return []string{"-copyts"} + } + return nil } func (m *Mediafile) ObtainOutputFormat() []string { - if m.outputFormat != "" { - return []string{"-f",m.outputFormat} - } - return nil + if m.outputFormat != "" { + return []string{"-f", m.outputFormat} + } + return nil } func (m *Mediafile) ObtainMuxDelay() []string { - if m.muxDelay != "" { - return []string{"-muxdelay",m.muxDelay} - } - return nil + if m.muxDelay != "" { + return []string{"-muxdelay", m.muxDelay} + } + return nil } func (m *Mediafile) ObtainSeekUsingTsInput() []string { - if m.seekUsingTsInput { - return []string{"-seek_timestamp", "1"} - } + if m.seekUsingTsInput { + return []string{"-seek_timestamp", "1"} + } return nil } func (m *Mediafile) ObtainRtmpLive() []string { - if m.rtmpLive != "" { - return []string{"-rtmp_live",m.rtmpLive} - } else { - return nil - } + if m.rtmpLive != "" { + return []string{"-rtmp_live", m.rtmpLive} + } else { + return nil + } } func (m *Mediafile) ObtainHlsPlaylistType() []string { - if m.hlsPlaylistType != "" { - return []string{"-hls_playlist_type",m.hlsPlaylistType} - } else { - return nil - } + if m.hlsPlaylistType != "" { + return []string{"-hls_playlist_type", m.hlsPlaylistType} + } else { + return nil + } } func (m *Mediafile) ObtainInputInitialOffset() []string { - if m.inputInitialOffset != "" { - return []string{"-itsoffset",m.inputInitialOffset} - } else { - return nil - } + if m.inputInitialOffset != "" { + return []string{"-itsoffset", m.inputInitialOffset} + } else { + return nil + } } func (m *Mediafile) ObtainHlsListSize() []string { - return []string{"-hls_list_size",fmt.Sprintf("%d", m.hlsListSize)} + return []string{"-hls_list_size", fmt.Sprintf("%d", m.hlsListSize)} } func (m *Mediafile) ObtainHlsSegmentDuration() []string { - if m.hlsSegmentDuration != 0 { - return []string{"-hls_time",fmt.Sprintf("%d", m.hlsSegmentDuration)} - } else { - return nil - } + if m.hlsSegmentDuration != 0 { + return []string{"-hls_time", fmt.Sprintf("%d", m.hlsSegmentDuration)} + } else { + return nil + } } func (m *Mediafile) ObtainHttpMethod() []string { - if m.httpMethod != "" { - return []string{"-method",m.httpMethod} - } else { - return nil - } + if m.httpMethod != "" { + return []string{"-method", m.httpMethod} + } else { + return nil + } } func (m *Mediafile) ObtainHttpKeepAlive() []string { - if m.httpKeepAlive { - return []string{"-multiple_requests","1"} - } else { - return nil - } + if m.httpKeepAlive { + return []string{"-multiple_requests", "1"} + } else { + return nil + } +} + +func (m *Mediafile) ObtainSkipVideo() []string { + if m.skipVideo { + return []string{"-vn"} + } else { + return nil + } +} + +func (m *Mediafile) ObtainSkipAudio() []string { + if m.skipAudio { + return []string{"-an"} + } else { + return nil + } } func (m *Mediafile) ObtainStreamIds() []string { - if m.streamIds != nil && len(m.streamIds) != 0 { - result := []string{} - for i, val := range m.streamIds { - result = append(result, []string{"-streamid", fmt.Sprintf("%d:%s", i, val)}...) - } - return result - } - return nil + if m.streamIds != nil && len(m.streamIds) != 0 { + result := []string{} + for i, val := range m.streamIds { + result = append(result, []string{"-streamid", fmt.Sprintf("%d:%s", i, val)}...) + } + return result + } + return nil } diff --git a/models/models.go b/models/models.go index 080b0de..0e64c3f 100644 --- a/models/models.go +++ b/models/models.go @@ -1,81 +1,81 @@ package models type Ffmpeg struct { - FfmpegBinPath string - FfprobeBinPath string + FfmpegBinPath string + FfprobeBinPath string } type Metadata struct { - Streams []Streams `json:"streams"` - Format Format `json:"format"` + Streams []Streams `json:"streams"` + Format Format `json:"format"` } type Streams struct { - Index int - ID string `json:"id"` - CodecName string `json:"codec_name"` - CodecLongName string `json:"codec_long_name"` - Profile string `json:"profile"` - CodecType string `json:"codec_type"` - CodecTimeBase string `json:"codec_time_base"` - CodecTagString string `json:"codec_tag_string"` - CodecTag string `json:"codec_tag"` - Width int `json:"width"` - Height int `json:"height"` - CodedWidth int `json:"coded_width"` - CodedHeight int `json:"coded_height"` - HasBFrames int `json:"has_b_frames"` - SampleAspectRatio string `json:"sample_aspect_ratio"` - DisplayAspectRatio string `json:"display_aspect_ratio"` - PixFmt string `json:"pix_fmt"` - Level int `json:"level"` - ChromaLocation string `json:"chroma_location"` - Refs int `json:"refs"` - QuarterSample string `json:"quarter_sample"` - DivxPacked string `json:"divx_packed"` - RFrameRrate string `json:"r_frame_rate"` - AvgFrameRate string `json:"avg_frame_rate"` - TimeBase string `json:"time_base"` - DurationTs int `json:"duration_ts"` - Duration string `json:"duration"` - Disposition Disposition `json:"disposition"` - BitRate string `json:"bit_rate"` + Index int + ID string `json:"id"` + CodecName string `json:"codec_name"` + CodecLongName string `json:"codec_long_name"` + Profile string `json:"profile"` + CodecType string `json:"codec_type"` + CodecTimeBase string `json:"codec_time_base"` + CodecTagString string `json:"codec_tag_string"` + CodecTag string `json:"codec_tag"` + Width int `json:"width"` + Height int `json:"height"` + CodedWidth int `json:"coded_width"` + CodedHeight int `json:"coded_height"` + HasBFrames int `json:"has_b_frames"` + SampleAspectRatio string `json:"sample_aspect_ratio"` + DisplayAspectRatio string `json:"display_aspect_ratio"` + PixFmt string `json:"pix_fmt"` + Level int `json:"level"` + ChromaLocation string `json:"chroma_location"` + Refs int `json:"refs"` + QuarterSample string `json:"quarter_sample"` + DivxPacked string `json:"divx_packed"` + RFrameRrate string `json:"r_frame_rate"` + AvgFrameRate string `json:"avg_frame_rate"` + TimeBase string `json:"time_base"` + DurationTs int `json:"duration_ts"` + Duration string `json:"duration"` + Disposition Disposition `json:"disposition"` + BitRate string `json:"bit_rate"` } type 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"` + 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"` } 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"` + 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 + FramesProcessed string + CurrentTime string + CurrentBitrate string + Progress float64 + Speed string } type Tags struct { - Encoder string `json:"ENCODER"` + Encoder string `json:"ENCODER"` } diff --git a/tests/transcoding_test.go b/tests/transcoding_test.go index ffa292a..2062335 100755 --- a/tests/transcoding_test.go +++ b/tests/transcoding_test.go @@ -1,9 +1,9 @@ package test import ( - "testing" - "github.com/xfrr/goffmpeg/transcoder" "fmt" + "github.com/xfrr/goffmpeg/transcoder" + "testing" ) func TestInputNotFound(t *testing.T) { @@ -33,7 +33,7 @@ func TestTranscoding3GP(t *testing.T) { } done := trans.Run(false) - err = <- done + err = <-done if err != nil { t.Error(err) return @@ -54,7 +54,7 @@ func TestTranscodingAVI(t *testing.T) { } done := trans.Run(false) - err = <- done + err = <-done if err != nil { t.Error(err) return @@ -75,7 +75,7 @@ func TestTranscodingFLV(t *testing.T) { } done := trans.Run(false) - err = <- done + err = <-done if err != nil { t.Error(err) return @@ -159,7 +159,7 @@ func TestTranscodingOGG(t *testing.T) { } done := trans.Run(false) - err = <- done + err = <-done if err != nil { t.Error(err) return @@ -180,7 +180,7 @@ func TestTranscodingWAV(t *testing.T) { } done := trans.Run(false) - err = <- done + err = <-done if err != nil { t.Error(err) return @@ -201,7 +201,7 @@ func TestTranscodingWEBM(t *testing.T) { } done := trans.Run(false) - err = <- done + err = <-done if err != nil { t.Error(err) return @@ -222,7 +222,7 @@ func TestTranscodingWMV(t *testing.T) { } done := trans.Run(false) - err = <- done + err = <-done if err != nil { t.Error(err) return @@ -246,7 +246,7 @@ func TestTranscodingProgress(t *testing.T) { for val := range trans.Output() { fmt.Printf("%+v\n", val) } - err = <- done + err = <-done if err != nil { t.Error(err) return diff --git a/transcoder/transcoder.go b/transcoder/transcoder.go index 146e435..fe5ded5 100644 --- a/transcoder/transcoder.go +++ b/transcoder/transcoder.go @@ -1,247 +1,247 @@ package transcoder import ( - "github.com/xfrr/goffmpeg/models" - "github.com/xfrr/goffmpeg/ffmpeg" - "github.com/xfrr/goffmpeg/utils" - "errors" - "os" - "os/exec" - "fmt" - "bytes" - "encoding/json" - "bufio" - "strings" - "regexp" - "strconv" - "io" + "bufio" + "bytes" + "encoding/json" + "errors" + "fmt" + "github.com/xfrr/goffmpeg/ffmpeg" + "github.com/xfrr/goffmpeg/models" + "github.com/xfrr/goffmpeg/utils" + "io" + "os" + "os/exec" + "regexp" + "strconv" + "strings" ) type Transcoder struct { - stdErrPipe io.ReadCloser - process *exec.Cmd - mediafile *models.Mediafile - configuration ffmpeg.Configuration + stdErrPipe io.ReadCloser + process *exec.Cmd + mediafile *models.Mediafile + configuration ffmpeg.Configuration } func (t *Transcoder) SetProcessStderrPipe(v io.ReadCloser) { - t.stdErrPipe = v + t.stdErrPipe = v } func (t *Transcoder) SetProcess(cmd *exec.Cmd) { - t.process = cmd + t.process = cmd } func (t *Transcoder) SetMediaFile(v *models.Mediafile) { - t.mediafile = v + t.mediafile = v } func (t *Transcoder) SetConfiguration(v ffmpeg.Configuration) { - t.configuration = v + t.configuration = v } /*** GETTERS ***/ -func (t Transcoder) Process() *exec.Cmd{ - return t.process +func (t Transcoder) Process() *exec.Cmd { + return t.process } func (t Transcoder) MediaFile() *models.Mediafile { - return t.mediafile + return t.mediafile } func (t Transcoder) FFmpegExec() string { - return t.configuration.FfmpegBin + return t.configuration.FfmpegBin } func (t Transcoder) FFprobeExec() string { - return t.configuration.FfprobeBin + return t.configuration.FfprobeBin } func (t Transcoder) GetCommand() []string { - media := t.mediafile - rcommand := append([]string{"-y"}, media.ToStrCommand()...) - return rcommand + media := t.mediafile + rcommand := append([]string{"-y"}, media.ToStrCommand()...) + return rcommand } /*** FUNCTIONS ***/ -func (t *Transcoder) Initialize(inputPath string, outputPath string) (error) { +func (t *Transcoder) Initialize(inputPath string, outputPath string) error { - configuration, err := ffmpeg.Configure() - if err != nil { - fmt.Println(err) - return err - } + configuration, err := ffmpeg.Configure() + if err != nil { + fmt.Println(err) + return err + } - if inputPath == "" { - return errors.New("error: transcoder.Initialize -> inputPath missing") - } + if inputPath == "" { + return errors.New("error: transcoder.Initialize -> inputPath missing") + } - _, err = os.Stat(inputPath) - if os.IsNotExist(err) { - return errors.New("error: transcoder.Initialize -> input file not found") - } + _, err = os.Stat(inputPath) + if os.IsNotExist(err) { + return errors.New("error: 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(configuration.FfprobeBin, command...) - var out bytes.Buffer - cmd.Stdout = &out + var out bytes.Buffer + cmd.Stdout = &out - err = cmd.Run() - if err != nil { - return fmt.Errorf("Failed FFPROBE (%s) with %s, message %s", command, err, out.String()) - } + err = cmd.Run() + if err != nil { + return fmt.Errorf("Failed FFPROBE (%s) with %s, message %s", command, err, out.String()) + } - var Metadata models.Metadata + var Metadata models.Metadata - if err = json.Unmarshal([]byte(out.String()), &Metadata); err != nil { - return err - } + if err = json.Unmarshal([]byte(out.String()), &Metadata); err != nil { + return err + } - // Set new Mediafile - MediaFile := new(models.Mediafile) - MediaFile.SetMetadata(Metadata) - MediaFile.SetInputPath(inputPath) - MediaFile.SetOutputPath(outputPath) - // Set transcoder configuration + // Set new Mediafile + MediaFile := new(models.Mediafile) + MediaFile.SetMetadata(Metadata) + MediaFile.SetInputPath(inputPath) + MediaFile.SetOutputPath(outputPath) + // Set transcoder configuration - t.SetMediaFile(MediaFile) - t.SetConfiguration(configuration) + t.SetMediaFile(MediaFile) + t.SetConfiguration(configuration) - return nil + return nil } func (t *Transcoder) Run(progress bool) <-chan error { - done := make(chan error) - command := t.GetCommand() - if !progress { - command = append([]string{"-nostats", "-loglevel", "0"}, command...) - } - proc := exec.Command(t.configuration.FfmpegBin, command...) - if progress { - errStream, err := proc.StderrPipe() - if err != nil { - fmt.Println("Progress not available: "+ err.Error()) - } else { - t.SetProcessStderrPipe(errStream) - } - } + done := make(chan error) + command := t.GetCommand() + if !progress { + command = append([]string{"-nostats", "-loglevel", "0"}, command...) + } + proc := exec.Command(t.configuration.FfmpegBin, command...) + if progress { + errStream, err := proc.StderrPipe() + if err != nil { + fmt.Println("Progress not available: " + err.Error()) + } else { + t.SetProcessStderrPipe(errStream) + } + } - out := &bytes.Buffer{} - proc.Stdout = out + out := &bytes.Buffer{} + proc.Stdout = out - err := proc.Start() - t.SetProcess(proc) - go func(err error, out *bytes.Buffer) { - if err != nil { - done <- fmt.Errorf("Failed Start FFMPEG (%s) with %s, message %s", command, err, out.String()) - close(done) - return - } - err = proc.Wait() - if err != nil { - err = fmt.Errorf("Failed Finish FFMPEG (%s) with %s message %s", command, err, out.String()) - } - done <- err - close(done) - }(err, out) + err := proc.Start() + t.SetProcess(proc) + go func(err error, out *bytes.Buffer) { + if err != nil { + done <- fmt.Errorf("Failed Start FFMPEG (%s) with %s, message %s", command, err, out.String()) + close(done) + return + } + err = proc.Wait() + if err != nil { + err = fmt.Errorf("Failed Finish FFMPEG (%s) with %s message %s", command, err, out.String()) + } + done <- err + close(done) + }(err, out) - return done + return done } func (t Transcoder) Output() <-chan models.Progress { - out := make(chan models.Progress) + out := make(chan models.Progress) - go func() { - defer close(out) - if t.stdErrPipe == nil { - out <- models.Progress{} - return - } else { - defer t.stdErrPipe.Close() - } - scanner := bufio.NewScanner(t.stdErrPipe) + go func() { + defer close(out) + if t.stdErrPipe == nil { + out <- models.Progress{} + return + } else { + defer t.stdErrPipe.Close() + } + scanner := bufio.NewScanner(t.stdErrPipe) - split := func(data []byte, atEOF bool) (advance int, token []byte, spliterror error) { - if atEOF && len(data) == 0 { - return 0, nil, nil - } - if i := bytes.IndexByte(data, '\n'); i >= 0 { - // We have a full newline-terminated line. - return i + 1, data[0:i], nil - } - if i := bytes.IndexByte(data, '\r'); i >= 0 { - // We have a cr terminated line - return i + 1, data[0:i], nil - } - if atEOF { - return len(data), data, nil - } + split := func(data []byte, atEOF bool) (advance int, token []byte, spliterror error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if i := bytes.IndexByte(data, '\n'); i >= 0 { + // We have a full newline-terminated line. + return i + 1, data[0:i], nil + } + if i := bytes.IndexByte(data, '\r'); i >= 0 { + // We have a cr terminated line + return i + 1, data[0:i], nil + } + if atEOF { + return len(data), data, nil + } - return 0, nil, nil - } + return 0, nil, nil + } - scanner.Split(split) - buf := make([]byte, 2) - scanner.Buffer(buf, bufio.MaxScanTokenSize) + scanner.Split(split) + buf := make([]byte, 2) + scanner.Buffer(buf, bufio.MaxScanTokenSize) - for scanner.Scan() { - Progress := new(models.Progress) - line := scanner.Text() - if strings.Contains(line, "frame=") && strings.Contains(line, "time=") && strings.Contains(line, "bitrate=") { - var re= regexp.MustCompile(`=\s+`) - st := re.ReplaceAllString(line, `=`) + for scanner.Scan() { + Progress := new(models.Progress) + line := scanner.Text() + if strings.Contains(line, "frame=") && strings.Contains(line, "time=") && strings.Contains(line, "bitrate=") { + var re = regexp.MustCompile(`=\s+`) + st := re.ReplaceAllString(line, `=`) - f := strings.Fields(st) - var framesProcessed string - var currentTime string - var currentBitrate string - var currentSpeed string + f := strings.Fields(st) + var framesProcessed string + var currentTime string + var currentBitrate string + var currentSpeed string - for j := 0; j < len(f); j++ { - field := f[j] - fieldSplit := strings.Split(field, "=") + for j := 0; j < len(f); j++ { + field := f[j] + fieldSplit := strings.Split(field, "=") - if len(fieldSplit) > 1 { - fieldname := strings.Split(field, "=")[0] - fieldvalue := strings.Split(field, "=")[1] + if len(fieldSplit) > 1 { + fieldname := strings.Split(field, "=")[0] + fieldvalue := strings.Split(field, "=")[1] - if fieldname == "frame" { - framesProcessed = fieldvalue - } + if fieldname == "frame" { + framesProcessed = fieldvalue + } - if fieldname == "time" { - currentTime = fieldvalue - } + if fieldname == "time" { + currentTime = fieldvalue + } - if fieldname == "bitrate" { - currentBitrate = fieldvalue - } - if fieldname == "speed" { - currentSpeed = fieldvalue - } - } - } + if fieldname == "bitrate" { + currentBitrate = fieldvalue + } + if fieldname == "speed" { + currentSpeed = fieldvalue + } + } + } - timesec := utils.DurToSec(currentTime) - dursec, _ := strconv.ParseFloat(t.MediaFile().Metadata().Format.Duration, 64) - //live stream check - if dursec != 0 { - // Progress calculation - progress := (timesec * 100) / dursec - Progress.Progress = progress - } - Progress.CurrentBitrate = currentBitrate - Progress.FramesProcessed = framesProcessed - Progress.CurrentTime = currentTime - Progress.Speed = currentSpeed - out <- *Progress - } - } - }() + timesec := utils.DurToSec(currentTime) + dursec, _ := strconv.ParseFloat(t.MediaFile().Metadata().Format.Duration, 64) + //live stream check + if dursec != 0 { + // Progress calculation + progress := (timesec * 100) / dursec + Progress.Progress = progress + } + Progress.CurrentBitrate = currentBitrate + Progress.FramesProcessed = framesProcessed + Progress.CurrentTime = currentTime + Progress.Speed = currentSpeed + out <- *Progress + } + } + }() - return out + return out } diff --git a/utils/utils.go b/utils/utils.go index 716b13a..d034c23 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,10 +1,10 @@ package utils import ( - "strings" - "strconv" "github.com/xfrr/goffmpeg/models" "runtime" + "strconv" + "strings" ) func DurToSec(dur string) (sec float64) { @@ -58,7 +58,6 @@ func CheckFileType(streams []models.Streams) string { st := streams[i] if st.CodecType == "video" { return "video" - break } }