livestream/server/play.go

154 lines
3.1 KiB
Go
Raw Permalink Normal View History

2023-06-13 00:07:14 +03:00
package main
import (
"fmt"
2023-06-13 00:07:14 +03:00
"log"
"os/exec"
"path"
"strings"
ffmpeg "github.com/u2takey/ffmpeg-go"
2023-06-13 00:07:14 +03:00
)
const OUTFILE = "stream.m3u8"
2023-06-13 00:07:14 +03:00
type ffmpegProcess struct {
cancel func() error
2023-06-13 00:07:14 +03:00
cmd *exec.Cmd
}
func (f *ffmpegProcess) Stop() {
c := f.cancel
if c != nil {
c()
}
}
func playffmpeg(loop bool, input string, output string, WhenVideoEnds func(forcequit bool)) (f ffmpegProcess) {
subtitle_stream_index, err := SelectPreferredSubtitle(input)
if err != nil {
log.Println(err)
}
inputArgs := ffmpeg.KwArgs{
"re": "",
}
outputArgs := ffmpeg.KwArgs{
"c:v": "libx264",
"c:a": "aac",
"b:a": "128k",
"ac": "2",
2024-02-25 02:54:23 +02:00
"pix_fmt": "yuv420p",
"preset": "ultrafast",
"hls_flags": "delete_segments+append_list+omit_endlist",
"hls_time": "1",
"f": "hls",
}
if subtitle_stream_index != nil {
// Makes file with subtitles not spit them into .vtt file
inputArgs["sn"] = ""
}
2023-06-13 00:07:14 +03:00
if loop {
inputArgs["stream_loop"] = "-1"
}
if subtitle_stream_index != nil {
// Burns subtitles into video
outputArgs["vf"] = fmt.Sprintf("subtitles=%s:si=%d", input, *subtitle_stream_index)
2023-06-13 00:07:14 +03:00
}
ffmpegCmd := ffmpeg.Input(input, inputArgs).Output(output, outputArgs).Compile()
f.cancel = ffmpegCmd.Cancel
2023-06-13 00:07:14 +03:00
go func() {
out, err := ffmpegCmd.CombinedOutput()
2023-06-13 00:07:14 +03:00
var was_force_quit bool
2023-06-13 00:07:14 +03:00
if err != nil && strings.Contains(err.Error(), "signal: killed") {
was_force_quit = true
} else if err != nil {
log.Println(err.Error()+":", string(out))
2023-06-13 00:07:14 +03:00
}
log.Println("Playback ended")
2023-06-13 00:07:14 +03:00
if WhenVideoEnds != nil {
WhenVideoEnds(was_force_quit)
2023-06-13 00:07:14 +03:00
}
}()
return
}
func (s *State) playFile(fileName string) (err error) {
s.stopPlaying()
log.Println("Playing", fileName)
s.VideoProcess = playffmpeg(false, fileName, path.Join(s.StreamCacheDir, OUTFILE), func(forcequit bool) {
2023-06-13 00:07:14 +03:00
if err := s.playFallback(); err != nil {
log.Fatal(err)
}
})
s.PlayState = PlayingVideo
s.SetNowPlaying(path.Base(fileName))
return
}
func (s *State) playFallback() (err error) {
s.stopPlaying()
log.Println("Playing fallback")
s.VideoProcess = playffmpeg(true, s.FallbackFile, path.Join(s.StreamCacheDir, OUTFILE), nil)
2023-06-13 00:07:14 +03:00
s.PlayState = PlayingFallback
s.SetNowPlaying("")
return
}
func (s *State) PlayRandom(autoplay bool) {
s.stopPlaying()
fileName, err := s.getRandomMediaFile()
if err != nil {
log.Println(err)
2023-06-13 00:07:14 +03:00
return
}
log.Println("Playing random file", fileName)
s.VideoProcess = playffmpeg(false, fileName, path.Join(s.StreamCacheDir, OUTFILE), func(forcequit bool) {
2023-06-13 00:07:14 +03:00
if forcequit {
return
}
if autoplay {
s.PlayRandom(autoplay)
} else {
2024-02-24 16:54:27 +02:00
s.playFallback()
2023-06-13 00:07:14 +03:00
}
})
s.PlayState = PlayingVideo
s.SetNowPlaying(path.Base(fileName))
return
}
func (s *State) stopPlaying() {
s.VideoProcess.Stop()
s.PlayState = NotPlaying
switch s.PlayState {
case PlayingVideo:
log.Println("Stopping currently playing video")
case PlayingFallback:
log.Println("Stopping currently playing fallback video")
}
}
func (s *State) SetNowPlaying(p string) {
s.CurrentFile = p
s.Websocket.Broadcast(WebsocketMessage{
Type: WebsocketMessageTypeCurrentlyPlaying,
Message: s.NowPlaying(),
}.Encode())
}