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 }