120 lines
3.3 KiB
Go
120 lines
3.3 KiB
Go
package ffmpeg
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strconv"
|
|
"stream-poc2/model"
|
|
)
|
|
|
|
// EncodeChunk doesn't have the nicest function signature...
|
|
func EncodeChunk(profileSettings model.VideoProfileSettings, nativeHeight int, preset string, origSegmentPath string, writer io.Writer) error {
|
|
ffmpegArgs := []string{
|
|
"-nostdin", "-hide_banner", "-loglevel", "error",
|
|
}
|
|
|
|
if profileSettings.Resolution.Height != nativeHeight {
|
|
// apply resize filter
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-hwaccel", "cuda", "-hwaccel_output_format", "cuda",
|
|
"-i", origSegmentPath,
|
|
"-vf", fmt.Sprintf("scale_cuda=%d:%d", profileSettings.Resolution.Width, profileSettings.Resolution.Height),
|
|
)
|
|
} else {
|
|
// just load the segment
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-i", origSegmentPath,
|
|
)
|
|
}
|
|
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-copyts",
|
|
"-c:v", profileSettings.FFMpegCodec(),
|
|
)
|
|
if preset != "" {
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-preset", preset,
|
|
)
|
|
}
|
|
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-b:v", strconv.Itoa(profileSettings.AvgBitrate), "-maxrate", strconv.Itoa(profileSettings.MaxBitrate), "-bufsize", "1M",
|
|
"-an", // <- no audio
|
|
"-f", "mpegts", "-mpegts_copyts", "1",
|
|
"-",
|
|
)
|
|
|
|
log.Println("exec ffmpeg args: ", ffmpegArgs)
|
|
|
|
cmd := exec.Command("/usr/bin/ffmpeg", ffmpegArgs...)
|
|
cmd.Stdout = writer
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
log.Println("Failed to run:", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MakeSegments has the ugliest function signature no the planet
|
|
func MakeSegments(filepath, outdir string, audioStreams []model.AudioStream) (string, []string, error) {
|
|
segmentsVideoCSV := path.Join(outdir, "segments_video.csv")
|
|
segmentsAudioCSV := make([]string, len(audioStreams))
|
|
|
|
// do slicing
|
|
ffmpegArgs := []string{
|
|
"-nostdin", "-hide_banner", "-loglevel", "error",
|
|
"-i", filepath,
|
|
// video stream
|
|
"-c:v", "copy", "-an",
|
|
"-f", "ssegment", "-segment_format", "mpegts", "-segment_list", segmentsVideoCSV,
|
|
"-segment_list_type", "csv", "-segment_time", strconv.Itoa(SlicerTargetDurationSec), path.Join(outdir, "video", "s%d.ts"),
|
|
}
|
|
|
|
for i, audioStream := range audioStreams {
|
|
csvName := path.Join(outdir, fmt.Sprintf("segments_audio_%d.csv", audioStream.Index))
|
|
segmentsAudioCSV[i] = csvName
|
|
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
// audio streams (encode to mp3)
|
|
"-vn", "-map", fmt.Sprintf("0:a:%d", i),
|
|
)
|
|
|
|
if audioStream.Codec != "mp3" {
|
|
// mp3 always works
|
|
// TODO: This does not update the stream info in the metadata !!!
|
|
log.Println("Transcoding audio to mp3 for stream ", audioStream.Index)
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-c:a", "libmp3lame", "-q:a", "0",
|
|
)
|
|
} else {
|
|
log.Println("Not transcoding audio as it is already mp3 for stream ", audioStream.Index)
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-c:a", "copy",
|
|
)
|
|
}
|
|
|
|
ffmpegArgs = append(ffmpegArgs,
|
|
"-f", "ssegment", "-segment_format", "mpegts", "-segment_list", csvName,
|
|
"-segment_list_type", "csv", "-segment_time", strconv.Itoa(SlicerTargetDurationSec), path.Join(outdir, "audio", strconv.Itoa(audioStream.Index), "s%d.ts"),
|
|
)
|
|
}
|
|
|
|
cmd := exec.Command("/usr/bin/ffmpeg", ffmpegArgs...)
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
log.Println("failed to segment:", err)
|
|
return "", nil, err
|
|
}
|
|
|
|
return segmentsVideoCSV, segmentsAudioCSV, nil
|
|
}
|