2024-02-17 19:08:58 +06:00
|
|
|
package kernel
|
2024-01-14 16:31:07 +06:00
|
|
|
|
|
|
|
import (
|
2024-03-07 22:18:35 +05:00
|
|
|
"bufio"
|
2024-01-15 20:28:02 +06:00
|
|
|
"errors"
|
2024-03-07 22:18:35 +05:00
|
|
|
encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
|
2024-02-12 22:21:47 +06:00
|
|
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper"
|
2024-03-07 22:18:35 +05:00
|
|
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel/encoder"
|
2024-01-20 01:53:25 +06:00
|
|
|
"io"
|
2024-01-14 16:31:07 +06:00
|
|
|
"os/exec"
|
2024-01-16 00:04:50 +06:00
|
|
|
"regexp"
|
2024-01-14 16:31:07 +06:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2024-02-25 23:38:16 +06:00
|
|
|
type File struct {
|
|
|
|
Path string
|
|
|
|
Name string
|
|
|
|
Ext string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ConvertSetting struct {
|
|
|
|
VideoFileInput File
|
|
|
|
VideoFileOut File
|
|
|
|
OverwriteOutputFiles bool
|
2024-03-07 22:18:35 +05:00
|
|
|
Encoder encoder2.EncoderContract
|
2024-02-25 23:38:16 +06:00
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
type ConvertorContract interface {
|
2024-01-20 01:53:25 +06:00
|
|
|
RunConvert(setting ConvertSetting, progress ProgressContract) error
|
2024-01-15 20:28:02 +06:00
|
|
|
GetTotalDuration(file *File) (float64, error)
|
2024-01-18 01:39:20 +06:00
|
|
|
GetFFmpegVesrion() (string, error)
|
|
|
|
GetFFprobeVersion() (string, error)
|
|
|
|
ChangeFFmpegPath(path string) (bool, error)
|
|
|
|
ChangeFFprobePath(path string) (bool, error)
|
2024-01-20 14:58:16 +06:00
|
|
|
GetRunningProcesses() map[int]*exec.Cmd
|
2024-03-07 22:18:35 +05:00
|
|
|
GetSupportFormats() (encoder.ConvertorFormatsContract, error)
|
2024-01-18 01:39:20 +06:00
|
|
|
}
|
|
|
|
|
2024-01-20 01:53:25 +06:00
|
|
|
type ProgressContract interface {
|
|
|
|
GetProtocole() string
|
|
|
|
Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error
|
|
|
|
}
|
|
|
|
|
2024-01-18 01:39:20 +06:00
|
|
|
type FFPathUtilities struct {
|
|
|
|
FFmpeg string
|
|
|
|
FFprobe string
|
2024-01-14 16:31:07 +06:00
|
|
|
}
|
|
|
|
|
2024-01-20 14:58:16 +06:00
|
|
|
type runningProcesses struct {
|
|
|
|
items map[int]*exec.Cmd
|
|
|
|
numberOfStarts int
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
type Convertor struct {
|
2024-01-20 14:58:16 +06:00
|
|
|
ffPathUtilities *FFPathUtilities
|
|
|
|
runningProcesses runningProcesses
|
2024-01-14 16:31:07 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
type ConvertData struct {
|
|
|
|
totalDuration float64
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func NewService(ffPathUtilities *FFPathUtilities) *Convertor {
|
|
|
|
return &Convertor{
|
|
|
|
ffPathUtilities: ffPathUtilities,
|
2024-01-20 14:58:16 +06:00
|
|
|
runningProcesses: runningProcesses{items: map[int]*exec.Cmd{}, numberOfStarts: 0},
|
2024-01-14 16:31:07 +06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func (s Convertor) RunConvert(setting ConvertSetting, progress ProgressContract) error {
|
2024-01-18 20:23:23 +06:00
|
|
|
overwriteOutputFiles := "-n"
|
|
|
|
if setting.OverwriteOutputFiles == true {
|
|
|
|
overwriteOutputFiles = "-y"
|
|
|
|
}
|
2024-03-07 22:18:35 +05:00
|
|
|
args := []string{overwriteOutputFiles, "-i", setting.VideoFileInput.Path}
|
|
|
|
args = append(args, setting.Encoder.GetParams()...)
|
|
|
|
args = append(args, "-progress", progress.GetProtocole(), setting.VideoFileOut.Path)
|
2024-01-18 01:39:20 +06:00
|
|
|
cmd := exec.Command(s.ffPathUtilities.FFmpeg, args...)
|
2024-01-20 01:53:25 +06:00
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
2024-01-14 16:31:07 +06:00
|
|
|
|
2024-01-20 01:53:25 +06:00
|
|
|
stdOut, err := cmd.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
stdErr, err := cmd.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-20 14:58:16 +06:00
|
|
|
index := s.runningProcesses.numberOfStarts
|
|
|
|
s.runningProcesses.numberOfStarts++
|
|
|
|
s.runningProcesses.items[index] = cmd
|
2024-01-20 01:53:25 +06:00
|
|
|
|
2024-01-20 02:24:14 +06:00
|
|
|
errProgress := progress.Run(stdOut, stdErr)
|
2024-01-20 01:53:25 +06:00
|
|
|
|
|
|
|
err = cmd.Wait()
|
2024-01-20 14:58:16 +06:00
|
|
|
delete(s.runningProcesses.items, index)
|
2024-01-20 02:24:14 +06:00
|
|
|
if errProgress != nil {
|
|
|
|
return errProgress
|
|
|
|
}
|
2024-01-14 16:31:07 +06:00
|
|
|
if err != nil {
|
2024-01-16 18:08:50 +06:00
|
|
|
return err
|
2024-01-14 16:31:07 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func (s Convertor) GetTotalDuration(file *File) (duration float64, err error) {
|
2024-01-15 20:28:02 +06:00
|
|
|
args := []string{"-v", "error", "-select_streams", "v:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path}
|
2024-01-18 01:39:20 +06:00
|
|
|
cmd := exec.Command(s.ffPathUtilities.FFprobe, args...)
|
2024-01-20 01:53:25 +06:00
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
2024-01-14 16:31:07 +06:00
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
2024-01-16 18:08:50 +06:00
|
|
|
errString := strings.TrimSpace(string(out))
|
|
|
|
if len(errString) > 1 {
|
|
|
|
return 0, errors.New(errString)
|
|
|
|
}
|
|
|
|
return 0, err
|
2024-01-14 16:31:07 +06:00
|
|
|
}
|
2024-03-07 22:18:35 +05:00
|
|
|
frames := strings.TrimSpace(string(out))
|
|
|
|
if len(frames) == 0 {
|
|
|
|
return s.getAlternativeTotalDuration(file)
|
|
|
|
}
|
|
|
|
return strconv.ParseFloat(frames, 64)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s Convertor) getAlternativeTotalDuration(file *File) (duration float64, err error) {
|
|
|
|
args := []string{"-v", "error", "-select_streams", "a:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path}
|
|
|
|
cmd := exec.Command(s.ffPathUtilities.FFprobe, args...)
|
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
errString := strings.TrimSpace(string(out))
|
|
|
|
if len(errString) > 1 {
|
|
|
|
return 0, errors.New(errString)
|
|
|
|
}
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
frames := strings.TrimSpace(string(out))
|
|
|
|
if len(frames) == 0 {
|
|
|
|
return 0, errors.New("error getting number of frames")
|
|
|
|
}
|
|
|
|
return strconv.ParseFloat(frames, 64)
|
2024-01-14 16:31:07 +06:00
|
|
|
}
|
2024-01-18 01:39:20 +06:00
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func (s Convertor) GetFFmpegVesrion() (string, error) {
|
2024-01-18 01:39:20 +06:00
|
|
|
cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-version")
|
2024-01-20 01:53:25 +06:00
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
2024-01-18 01:39:20 +06:00
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1)
|
|
|
|
return text[0], nil
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func (s Convertor) GetFFprobeVersion() (string, error) {
|
2024-01-18 01:39:20 +06:00
|
|
|
cmd := exec.Command(s.ffPathUtilities.FFprobe, "-version")
|
2024-01-20 01:53:25 +06:00
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
2024-01-18 01:39:20 +06:00
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1)
|
|
|
|
return text[0], nil
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func (s Convertor) ChangeFFmpegPath(path string) (bool, error) {
|
2024-01-18 01:39:20 +06:00
|
|
|
cmd := exec.Command(path, "-version")
|
2024-01-20 01:53:25 +06:00
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
2024-01-18 01:39:20 +06:00
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if strings.Contains(strings.TrimSpace(string(out)), "ffmpeg") == false {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
s.ffPathUtilities.FFmpeg = path
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func (s Convertor) ChangeFFprobePath(path string) (bool, error) {
|
2024-01-18 01:39:20 +06:00
|
|
|
cmd := exec.Command(path, "-version")
|
2024-01-20 01:53:25 +06:00
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
2024-01-18 01:39:20 +06:00
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if strings.Contains(strings.TrimSpace(string(out)), "ffprobe") == false {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
s.ffPathUtilities.FFprobe = path
|
|
|
|
return true, nil
|
|
|
|
}
|
2024-01-20 14:58:16 +06:00
|
|
|
|
2024-03-07 22:18:35 +05:00
|
|
|
func (s Convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) {
|
|
|
|
formats := encoder.NewConvertorFormats()
|
|
|
|
cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-encoders")
|
2024-03-07 22:50:00 +05:00
|
|
|
helper.PrepareBackgroundCommand(cmd)
|
2024-03-07 22:18:35 +05:00
|
|
|
|
|
|
|
stdOut, err := cmd.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return formats, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return formats, err
|
|
|
|
}
|
|
|
|
|
|
|
|
scannerErr := bufio.NewReader(stdOut)
|
|
|
|
for {
|
|
|
|
line, _, err := scannerErr.ReadLine()
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
text := strings.Split(strings.TrimSpace(string(line)), " ")
|
|
|
|
encoderType := string(text[0][0])
|
|
|
|
if len(text) < 2 || (encoderType != "V" && encoderType != "A") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
formats.NewEncoder(text[1])
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cmd.Wait()
|
|
|
|
if err != nil {
|
|
|
|
return formats, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return formats, nil
|
|
|
|
}
|
|
|
|
|
2024-02-17 19:08:58 +06:00
|
|
|
func (s Convertor) GetRunningProcesses() map[int]*exec.Cmd {
|
2024-01-20 14:58:16 +06:00
|
|
|
return s.runningProcesses.items
|
|
|
|
}
|