From ca3027d8b90cc31a7be60a90db0226912ce95d0d Mon Sep 17 00:00:00 2001 From: Rohit Garg Date: Mon, 21 May 2018 10:44:12 +0530 Subject: [PATCH 1/3] Added input path to mediafile and seek using TS option for input --- models/media.go | 325 ++++++++++++++++++--------------- models/models.go | 114 ++++++------ transcoder/transcoder.go | 381 +++++++++++++++++++-------------------- 3 files changed, 425 insertions(+), 395 deletions(-) diff --git a/models/media.go b/models/media.go index 5a7cdc1..a265a7f 100644 --- a/models/media.go +++ b/models/media.go @@ -1,10 +1,10 @@ package models import ( - "strings" - "strconv" - "fmt" - "reflect" + "strings" + "strconv" + "fmt" + "reflect" ) type Mediafile struct { @@ -24,341 +24,380 @@ type Mediafile struct { audioChannels int bufferSize int threads int - preset string + preset string target string duration string seekTime string quality int metadata Metadata + muxDelay int + seekUsingTsInput bool + inputPath string } /*** SETTERS ***/ 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) SetFrameRate(v int) { - m.frameRate = v + m.frameRate = 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) SetDuration(v string) { - m.duration = v + m.duration = v } func (m *Mediafile) SetSeekTime(v string) { - m.seekTime = v + m.seekTime = v } func (m *Mediafile) SetQuality(v int) { - m.quality = v + m.quality = v +} + +func (m *Mediafile) SetSeekUsingTsInput(val bool) { + m.seekUsingTsInput = val +} + +func (m *Mediafile) SetInputPath(val string) { + m.inputPath = val } func (m *Mediafile) SetMetadata(v Metadata) { - m.metadata = v + m.metadata = v } /*** GETTERS ***/ 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) FrameRate() int { - return m.frameRate + return m.frameRate } 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) SeekTime() string { - return m.seekTime + return m.seekTime } func (m Mediafile) Quality() int { - return m.quality + return m.quality +} + +func (m Mediafile) MuxDelay() int { + return m.muxDelay +} + +func (m Mediafile) SeekUsingTsInput() bool { + return m.seekUsingTsInput +} + +func (m Mediafile) InputPath() string { + return m.inputPath } 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{"Aspect", "VideoCodec", "FrameRate", "Resolution", "VideoBitRate", "VideoBitRateTolerance", "AudioCodec", "AudioBitRate", "AudioChannels", "VideoMaxBitRate", "VideoMinBitRate", "BufferSize", "Threads", "Preset", "Target", "Duration", "KeyframeInterval", "SeekTime", "Quality"} - for _, name := range opts { - opt := reflect.ValueOf(&m).MethodByName(fmt.Sprintf("Obtain%s", name)) - if (opt != reflect.Value{}) { - result := opt.Call([]reflect.Value{}) + opts := []string{"SeekUsingTsInput", "InputPath", "Aspect", "VideoCodec", "FrameRate", "Resolution", "VideoBitRate", "VideoBitRateTolerance", "AudioCodec", "AudioBitRate", "AudioChannels", "VideoMaxBitRate", "VideoMinBitRate", "BufferSize", "Threads", "Preset", "Target", "Duration", "KeyframeInterval", "SeekTime", "Quality", "ObtainMuxDelay"} + for _, name := range opts { + opt := reflect.ValueOf(&m).MethodByName(fmt.Sprintf("Obtain%s", name)) + if (opt != reflect.Value{}) { + result := opt.Call([]reflect.Value{}) - if result[0].String() != "" { - strCommand += " " - strCommand += result[0].String() - } - } - } + if result[0].String() != "" { + strCommand += " " + strCommand += result[0].String() + } + } + } - return strCommand + return strCommand } 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 fmt.Sprintf("-aspect %f", width/height) - } - } - - if m.aspect != "" { - return fmt.Sprintf("-aspect %s", m.aspect) - } else { - return "" - } + // 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 fmt.Sprintf("-aspect %f", width/height) + } + } + + if m.aspect != "" { + return fmt.Sprintf("-aspect %s", m.aspect) + } else { + return "" + } +} + +func (m *Mediafile) ObtainInputPath() string { + return fmt.Sprintf("-i \"%s\"", m.inputPath) } func (m *Mediafile) ObtainVideoCodec() string { - if m.videoCodec != "" { - return fmt.Sprintf("-vcodec %s", m.videoCodec) - } - return "" + if m.videoCodec != "" { + return fmt.Sprintf("-vcodec %s", m.videoCodec) + } + return "" } func (m *Mediafile) ObtainFrameRate() string { - if m.frameRate != 0 { - return fmt.Sprintf("-r %d", m.frameRate) - } - return "" + if m.frameRate != 0 { + return fmt.Sprintf("-r %d", m.frameRate) + } + return "" } func (m *Mediafile) ObtainResolution() string { - if m.resolution != "" { - return fmt.Sprintf("-s %s", m.resolution) - } - return "" + if m.resolution != "" { + return fmt.Sprintf("-s %s", m.resolution) + } + return "" } func (m *Mediafile) ObtainVideoBitRate() string { - if m.videoBitRate != 0 { - return fmt.Sprintf("-b:v %d", m.videoBitRate) - } - return "" + if m.videoBitRate != 0 { + return fmt.Sprintf("-b:v %d", m.videoBitRate) + } + return "" } func (m *Mediafile) ObtainAudioCodec() string { - if m.audioCodec != "" { - return fmt.Sprintf("-acodec %s", m.audioCodec) - } - return "" + if m.audioCodec != "" { + return fmt.Sprintf("-acodec %s", m.audioCodec) + } + return "" } func (m *Mediafile) ObtainAudioBitRate() string { - if m.audioBitrate != 0 { - return fmt.Sprintf("-b:a %d", m.audioBitrate) - } - return "" + if m.audioBitrate != 0 { + return fmt.Sprintf("-b:a %d", m.audioBitrate) + } + return "" } func (m *Mediafile) ObtainAudioChannels() string { - if m.audioChannels != 0 { - return fmt.Sprintf("-ac %d", m.audioChannels) - } - return "" + if m.audioChannels != 0 { + return fmt.Sprintf("-ac %d", m.audioChannels) + } + return "" } func (m *Mediafile) ObtainVideoMaxBitRate() string { - if m.videoMaxBitRate != 0 { - return fmt.Sprintf("-maxrate %dk", m.videoMaxBitRate) - } - return "" + if m.videoMaxBitRate != 0 { + return fmt.Sprintf("-maxrate %dk", m.videoMaxBitRate) + } + return "" } func (m *Mediafile) ObtainVideoMinBitRate() string { - if m.videoMinBitrate != 0 { - return fmt.Sprintf("-minrate %dk", m.videoMinBitrate) - } - return "" + if m.videoMinBitrate != 0 { + return fmt.Sprintf("-minrate %dk", m.videoMinBitrate) + } + return "" } func (m *Mediafile) ObtainBufferSize() string { - if m.bufferSize != 0 { - return fmt.Sprintf("-bufsize %dk", m.bufferSize) - } - return "" + if m.bufferSize != 0 { + return fmt.Sprintf("-bufsize %dk", m.bufferSize) + } + return "" } func (m *Mediafile) ObtainVideoBitRateTolerance() string { - if m.videoBitRateTolerance != 0 { - return fmt.Sprintf("-bt %dk", m.videoBitRateTolerance) - } - return "" + if m.videoBitRateTolerance != 0 { + return fmt.Sprintf("-bt %dk", m.videoBitRateTolerance) + } + return "" } func (m *Mediafile) ObtainThreads() string { - if m.threads != 0 { - return fmt.Sprintf("-threads %d", m.threads) - } - return "" + if m.threads != 0 { + return fmt.Sprintf("-threads %d", m.threads) + } + return "" } func (m *Mediafile) ObtainTarget() string { - if m.target != "" { - return fmt.Sprintf("-target %s", m.target) - } - return "" + if m.target != "" { + return fmt.Sprintf("-target %s", m.target) + } + return "" } func (m *Mediafile) ObtainDuration() string { - if m.duration != "" { - return fmt.Sprintf("-t %s", m.duration) - } - return "" + if m.duration != "" { + return fmt.Sprintf("-t %s", m.duration) + } + return "" } func (m *Mediafile) ObtainKeyframeInterval() string { - if m.keyframeInterval != 0 { - return fmt.Sprintf("-g %d", m.keyframeInterval) - } - return "" + if m.keyframeInterval != 0 { + return fmt.Sprintf("-g %d", m.keyframeInterval) + } + return "" } func (m *Mediafile) ObtainSeekTime() string { - if m.seekTime != "" { - return fmt.Sprintf("-ss %s", m.seekTime) - } - return "" + if m.seekTime != "" { + return fmt.Sprintf("-ss %s", m.seekTime) + } + return "" } func (m *Mediafile) ObtainPreset() string { - if m.preset != "" { - return fmt.Sprintf("-preset %s", m.preset) - } - return "" + if m.preset != "" { + return fmt.Sprintf("-preset %s", m.preset) + } + return "" +} + +func (m *Mediafile) ObtainMuxDelay() string { + return fmt.Sprintf("-muxdelay %d", m.muxDelay) +} + +func (m *Mediafile) ObtainSeekUsingTsInput() string { + if m.seekUsingTsInput { + return "-seek_timestamp 1" + } else { + return "" + } } diff --git a/models/models.go b/models/models.go index bdd7198..9c72682 100644 --- a/models/models.go +++ b/models/models.go @@ -1,79 +1,79 @@ 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 - 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 + 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 + FramesProcessed string + CurrentTime string + CurrentBitrate string + Progress float64 } type Tags struct { - Encoder string `json:"ENCODER"` + Encoder string `json:"ENCODER"` } diff --git a/transcoder/transcoder.go b/transcoder/transcoder.go index ebdac56..0bb9360 100644 --- a/transcoder/transcoder.go +++ b/transcoder/transcoder.go @@ -1,87 +1,78 @@ package transcoder import ( - "errors" - "os" - "goffmpeg/models" - "os/exec" - "fmt" - "goffmpeg/ffmpeg" - "goffmpeg/utils" - "bytes" - "encoding/json" - "bufio" - "strings" - "regexp" - "strconv" + "errors" + "os" + "goffmpeg/models" + "os/exec" + "fmt" + "goffmpeg/ffmpeg" + "goffmpeg/utils" + "bytes" + "encoding/json" + "bufio" + "strings" + "regexp" + "strconv" ) type Transcoder struct { - process *exec.Cmd - inputPath string - outputPath string - mediafile *models.Mediafile - configuration ffmpeg.Configuration + process *exec.Cmd + outputPath string + mediafile *models.Mediafile + configuration ffmpeg.Configuration } func (t *Transcoder) SetProccess(v *exec.Cmd) { - t.process = v -} - -func (t *Transcoder) SetInputPath(v string) { - t.inputPath = v + t.process = v } func (t *Transcoder) SetOutputPath(v string) { - t.outputPath = v + t.outputPath = v } 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) InputPath() string { - return t.inputPath + return t.process } func (t Transcoder) OutputPath() string { - return t.outputPath + return t.outputPath } 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 { - var rcommand string + var rcommand string - rcommand = fmt.Sprintf("%s -y -i \"%s\" ", t.configuration.FfmpegBin, t.inputPath) + rcommand = fmt.Sprintf("%s -y ", t.configuration.FfmpegBin) - media := t.mediafile + media := t.mediafile - rcommand += media.ToStrCommand() + rcommand += media.ToStrCommand() - rcommand += " \"" + t.outputPath + "\"" + rcommand += " \"" + t.outputPath + "\"" - return rcommand + return rcommand } @@ -89,194 +80,194 @@ func (t Transcoder) GetCommand() string { 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 := fmt.Sprintf("%s -i \"%s\" -print_format json -show_format -show_streams -show_error", configuration.FfprobeBin, inputPath) + command := fmt.Sprintf("%s -i \"%s\" -print_format json -show_format -show_streams -show_error", configuration.FfprobeBin, inputPath) - cmd := exec.Command(configuration.ExecCmd, configuration.ExecArgs, command) + cmd := exec.Command(configuration.ExecCmd, configuration.ExecArgs, command) - fmt.Println("FFprobe command: " + command) + fmt.Println("FFprobe command: " + command) - var out bytes.Buffer + var out bytes.Buffer - cmd.Stdout = &out + cmd.Stdout = &out - err = cmd.Start() + err = cmd.Start() - if err != nil { - return err - } + if err != nil { + return err + } - _, err = cmd.Process.Wait() - if err != nil { - return err - } + _, err = cmd.Process.Wait() + if err != nil { + return err + } - 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 + // Set new Mediafile MediaFile := new(models.Mediafile) MediaFile.SetMetadata(Metadata) - + MediaFile.SetInputPath(inputPath) // Set transcoder configuration - t.SetInputPath(inputPath) + t.SetOutputPath(outputPath) t.SetMediaFile(MediaFile) - t.SetConfiguration(configuration) + t.SetConfiguration(configuration) return nil } func (t *Transcoder) Run() (<-chan bool, error) { - done := make(chan bool) - var err error + done := make(chan bool) + var err error - command := t.GetCommand() + command := t.GetCommand() - fmt.Println("FFmpeg command: " + command) + fmt.Println("FFmpeg command: " + command) - proc := exec.Command(t.configuration.ExecCmd, t.configuration.ExecArgs, command) + proc := exec.Command(t.configuration.ExecCmd, t.configuration.ExecArgs, command) - t.SetProccess(proc) + t.SetProccess(proc) - go func() { - err := proc.Start() - if err != nil { - return - } + go func() { + err := proc.Start() + if err != nil { + return + } - proc.Wait() + proc.Wait() - done <- true - close(done) - }() + done <- true + close(done) + }() - return done, err + return done, err } func (t Transcoder) Output() (<-chan models.Progress, error) { - out := make(chan models.Progress) - var err error - - go func() { - defer close(out) - - stderr, stderror := t.Process().StderrPipe() - if err != nil { - err = stderror - return - } - - scanner := bufio.NewScanner(stderr) - filetype := utils.CheckFileType(t.MediaFile().Metadata().Streams) - - split := func(data []byte, atEOF bool) (advance int, token []byte, spliterror error) { - - if atEOF && len(data) == 0 { - return 0, nil, nil - } - - Iframe := strings.Index(string(data), "frame=") - - if filetype == "video" { - if Iframe > 0 { - return Iframe + 1, data[Iframe:], nil - } - } else { - if i := bytes.IndexByte(data, '\n'); i >= 0 { - // We have a full newline-terminated line. - return i + 1, data[0:i], nil - } - } - - if atEOF { - return len(data), data, nil - } - - return 0, nil, nil - } - - scanner.Split(split) - buf := make([]byte, 2) - scanner.Buffer(buf, bufio.MaxScanTokenSize) - - var lastProgress float64 - for scanner.Scan() { - Progress := new(models.Progress) - line := scanner.Text() - - if 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 - - 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 fieldname == "frame" { - framesProcessed = fieldvalue - } - - if fieldname == "time" { - currentTime = fieldvalue - } - - if fieldname == "bitrate" { - currentBitrate = fieldvalue - } - } - } - - timesec := utils.DurToSec(currentTime) - dursec, _ := strconv.ParseFloat(t.MediaFile().Metadata().Format.Duration, 64) - // Progress calculation - progress := (timesec * 100) / dursec - - Progress.Progress = progress - Progress.CurrentBitrate = currentBitrate - Progress.FramesProcessed = framesProcessed - Progress.CurrentTime = currentTime - - if progress != lastProgress { - lastProgress = progress - out <- *Progress - } - } - } - - if scerror := scanner.Err(); err != nil { - err = scerror - } - }() - - return out, err + out := make(chan models.Progress) + var err error + + go func() { + defer close(out) + + stderr, stderror := t.Process().StderrPipe() + if err != nil { + err = stderror + return + } + + scanner := bufio.NewScanner(stderr) + filetype := utils.CheckFileType(t.MediaFile().Metadata().Streams) + + split := func(data []byte, atEOF bool) (advance int, token []byte, spliterror error) { + + if atEOF && len(data) == 0 { + return 0, nil, nil + } + + Iframe := strings.Index(string(data), "frame=") + + if filetype == "video" { + if Iframe > 0 { + return Iframe + 1, data[Iframe:], nil + } + } else { + if i := bytes.IndexByte(data, '\n'); i >= 0 { + // We have a full newline-terminated line. + return i + 1, data[0:i], nil + } + } + + if atEOF { + return len(data), data, nil + } + + return 0, nil, nil + } + + scanner.Split(split) + buf := make([]byte, 2) + scanner.Buffer(buf, bufio.MaxScanTokenSize) + + var lastProgress float64 + for scanner.Scan() { + Progress := new(models.Progress) + line := scanner.Text() + + if 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 + + 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 fieldname == "frame" { + framesProcessed = fieldvalue + } + + if fieldname == "time" { + currentTime = fieldvalue + } + + if fieldname == "bitrate" { + currentBitrate = fieldvalue + } + } + } + + timesec := utils.DurToSec(currentTime) + dursec, _ := strconv.ParseFloat(t.MediaFile().Metadata().Format.Duration, 64) + // Progress calculation + progress := (timesec * 100) / dursec + + Progress.Progress = progress + Progress.CurrentBitrate = currentBitrate + Progress.FramesProcessed = framesProcessed + Progress.CurrentTime = currentTime + + if progress != lastProgress { + lastProgress = progress + out <- *Progress + } + } + } + + if scerror := scanner.Err(); err != nil { + err = scerror + } + }() + + return out, err } From 943b31515bb0574f531c0ad0a7e7b3f91be2970e Mon Sep 17 00:00:00 2001 From: Rohit Garg Date: Mon, 21 May 2018 10:59:49 +0530 Subject: [PATCH 2/3] Added input seektime and duration --- models/media.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/models/media.go b/models/media.go index a265a7f..7b217db 100644 --- a/models/media.go +++ b/models/media.go @@ -27,11 +27,13 @@ type Mediafile struct { preset string target string duration string + durationInput string seekTime string quality int metadata Metadata muxDelay int seekUsingTsInput bool + seekTimeInput string inputPath string } @@ -109,10 +111,18 @@ func (m *Mediafile) SetDuration(v string) { m.duration = v } +func (m *Mediafile) SetDurationInput(v string) { + m.durationInput = v +} + func (m *Mediafile) SetSeekTime(v string) { m.seekTime = v } +func (m *Mediafile) SetSeekTimeInput(v string) { + m.seekTimeInput = v +} + func (m *Mediafile) SetQuality(v int) { m.quality = v } @@ -203,10 +213,18 @@ func (m Mediafile) Duration() string { return m.duration } +func (m Mediafile) DurationInput() string { + return m.durationInput +} + func (m Mediafile) SeekTime() string { return m.seekTime } +func (m Mediafile) SeekTimeInput() string { + return m.seekTimeInput +} + func (m Mediafile) Quality() int { return m.quality } @@ -231,7 +249,7 @@ func (m Mediafile) Metadata() Metadata { func (m Mediafile) ToStrCommand() string { var strCommand string - opts := []string{"SeekUsingTsInput", "InputPath", "Aspect", "VideoCodec", "FrameRate", "Resolution", "VideoBitRate", "VideoBitRateTolerance", "AudioCodec", "AudioBitRate", "AudioChannels", "VideoMaxBitRate", "VideoMinBitRate", "BufferSize", "Threads", "Preset", "Target", "Duration", "KeyframeInterval", "SeekTime", "Quality", "ObtainMuxDelay"} + opts := []string{"SeekTimeInput", "DurationInput", "SeekUsingTsInput", "InputPath", "Aspect", "VideoCodec", "FrameRate", "Resolution", "VideoBitRate", "VideoBitRateTolerance", "AudioCodec", "AudioBitRate", "AudioChannels", "VideoMaxBitRate", "VideoMinBitRate", "BufferSize", "Threads", "Preset", "Target", "Duration", "KeyframeInterval", "SeekTime", "Quality", "ObtainMuxDelay"} for _, name := range opts { opt := reflect.ValueOf(&m).MethodByName(fmt.Sprintf("Obtain%s", name)) if (opt != reflect.Value{}) { @@ -368,6 +386,13 @@ func (m *Mediafile) ObtainDuration() string { return "" } +func (m *Mediafile) ObtainDurationInput() string { + if m.durationInput != "" { + return fmt.Sprintf("-t %s", m.durationInput) + } + return "" +} + func (m *Mediafile) ObtainKeyframeInterval() string { if m.keyframeInterval != 0 { return fmt.Sprintf("-g %d", m.keyframeInterval) @@ -382,6 +407,13 @@ func (m *Mediafile) ObtainSeekTime() string { return "" } +func (m *Mediafile) ObtainSeekTimeInput() string { + if m.seekTimeInput != "" { + return fmt.Sprintf("-ss %s", m.seekTimeInput) + } + return "" +} + func (m *Mediafile) ObtainPreset() string { if m.preset != "" { return fmt.Sprintf("-preset %s", m.preset) From d3ea8869c2d771fc2aaae4b58b3a4e0aae7da535 Mon Sep 17 00:00:00 2001 From: Rohit Garg Date: Mon, 21 May 2018 11:14:58 +0530 Subject: [PATCH 3/3] fixed one bug and added copyts --- ffmpeg/ffmpeg.go | 76 ++++++++++++++++++++++++------------------------ models/media.go | 18 +++++++++++- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/ffmpeg/ffmpeg.go b/ffmpeg/ffmpeg.go index 5670051..a625e2f 100644 --- a/ffmpeg/ffmpeg.go +++ b/ffmpeg/ffmpeg.go @@ -1,58 +1,58 @@ package ffmpeg import ( - "os/exec" - "bytes" - "strings" - "goffmpeg/utils" + "os/exec" + "bytes" + "strings" + "goffmpeg/utils" ) type Configuration struct { - FfmpegBin string - FfprobeBin string - ExecCmd string - ExecArgs string + FfmpegBin string + FfprobeBin string + ExecCmd string + ExecArgs string } func Configure() (Configuration, error) { - var outFFmpeg bytes.Buffer - var outProbe bytes.Buffer + var outFFmpeg bytes.Buffer + var outProbe bytes.Buffer - execCmd := utils.GetExec() - execFFmpegCommand := utils.GetFFmpegExec() - execFFprobeCommand := utils.GetFFprobeExec() - execArgs := utils.GetExecArgs() + execCmd := utils.GetExec() + execFFmpegCommand := utils.GetFFmpegExec() + execFFprobeCommand := utils.GetFFprobeExec() + execArgs := utils.GetExecArgs() - cmdFFmpeg := exec.Command(execCmd, execArgs, execFFmpegCommand) - cmdProbe := exec.Command(execCmd, execArgs, execFFprobeCommand) + cmdFFmpeg := exec.Command(execCmd, execArgs, execFFmpegCommand) + cmdProbe := exec.Command(execCmd, execArgs, execFFprobeCommand) - cmdFFmpeg.Stdout = &outFFmpeg - cmdProbe.Stdout = &outProbe + cmdFFmpeg.Stdout = &outFFmpeg + cmdProbe.Stdout = &outProbe - err := cmdFFmpeg.Start() - if err != nil { - return Configuration{}, err - } + err := cmdFFmpeg.Start() + if err != nil { + return Configuration{}, err + } - _, err = cmdFFmpeg.Process.Wait() - if err != nil { - return Configuration{}, err - } + _, err = cmdFFmpeg.Process.Wait() + if err != nil { + return Configuration{}, err + } - err = cmdProbe.Start() - if err != nil { - return Configuration{}, err - } + err = cmdProbe.Start() + if err != nil { + return Configuration{}, err + } - _, err = cmdProbe.Process.Wait() - if err != nil { - return Configuration{}, err - } + _, err = cmdProbe.Process.Wait() + if err != nil { + return Configuration{}, err + } - ffmpeg := strings.Replace(outFFmpeg.String(), "\n", "", -1) - fprobe := strings.Replace(outProbe.String(), "\n", "", -1) + ffmpeg := strings.Replace(outFFmpeg.String(), "\n", "", -1) + fprobe := strings.Replace(outProbe.String(), "\n", "", -1) - cnf := Configuration{ffmpeg, fprobe, execCmd, execArgs} + cnf := Configuration{ffmpeg, fprobe, execCmd, execArgs} - return cnf, nil + return cnf, nil } diff --git a/models/media.go b/models/media.go index 7b217db..452623c 100644 --- a/models/media.go +++ b/models/media.go @@ -35,6 +35,7 @@ type Mediafile struct { seekUsingTsInput bool seekTimeInput string inputPath string + copyTs bool } /*** SETTERS ***/ @@ -131,6 +132,10 @@ func (m *Mediafile) SetSeekUsingTsInput(val bool) { m.seekUsingTsInput = val } +func (m *Mediafile) SetCopyTs(val bool) { + m.copyTs = val +} + func (m *Mediafile) SetInputPath(val string) { m.inputPath = val } @@ -237,6 +242,10 @@ func (m Mediafile) SeekUsingTsInput() bool { return m.seekUsingTsInput } +func (m Mediafile) CopyTs() bool { + return m.copyTs +} + func (m Mediafile) InputPath() string { return m.inputPath } @@ -249,7 +258,7 @@ func (m Mediafile) Metadata() Metadata { func (m Mediafile) ToStrCommand() string { var strCommand string - opts := []string{"SeekTimeInput", "DurationInput", "SeekUsingTsInput", "InputPath", "Aspect", "VideoCodec", "FrameRate", "Resolution", "VideoBitRate", "VideoBitRateTolerance", "AudioCodec", "AudioBitRate", "AudioChannels", "VideoMaxBitRate", "VideoMinBitRate", "BufferSize", "Threads", "Preset", "Target", "Duration", "KeyframeInterval", "SeekTime", "Quality", "ObtainMuxDelay"} + opts := []string{"SeekTimeInput", "DurationInput", "SeekUsingTsInput", "InputPath", "Aspect", "VideoCodec", "FrameRate", "Resolution", "VideoBitRate", "VideoBitRateTolerance", "AudioCodec", "AudioBitRate", "AudioChannels", "VideoMaxBitRate", "VideoMinBitRate", "BufferSize", "Threads", "Preset", "Target", "Duration", "KeyframeInterval", "SeekTime", "Quality", "MuxDelay", "CopyTs"} for _, name := range opts { opt := reflect.ValueOf(&m).MethodByName(fmt.Sprintf("Obtain%s", name)) if (opt != reflect.Value{}) { @@ -421,6 +430,13 @@ func (m *Mediafile) ObtainPreset() string { return "" } +func (m *Mediafile) ObtainCopyTs() string { + if m.copyTs { + return "-copyts" + } + return "" +} + func (m *Mediafile) ObtainMuxDelay() string { return fmt.Sprintf("-muxdelay %d", m.muxDelay) }