log improv
parent
1141c358b8
commit
a094c37fb5
|
@ -10,6 +10,8 @@ type Config struct {
|
||||||
chunkSize int
|
chunkSize int
|
||||||
// How many *chunks* to look behind before restarting transcoding
|
// How many *chunks* to look behind before restarting transcoding
|
||||||
lookBehind int
|
lookBehind int
|
||||||
// How many chunks to buffer ahead of player position
|
// Number of chunks in goal to restart encoding
|
||||||
goalBuffer int
|
goalBufferMin int
|
||||||
|
// Number of chunks in goal to stop encoding
|
||||||
|
goalBufferMax int
|
||||||
}
|
}
|
||||||
|
|
13
main.go
13
main.go
|
@ -44,7 +44,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
path := "/" + strings.Join(parts[1:len(parts)-1], "/")
|
path := "/" + strings.Join(parts[1:len(parts)-1], "/")
|
||||||
chunk := parts[len(parts)-1]
|
chunk := parts[len(parts)-1]
|
||||||
|
|
||||||
log.Println("Serving", path, streamid, chunk)
|
// log.Println("Serving", path, streamid, chunk)
|
||||||
|
|
||||||
if streamid == "" || chunk == "" || path == "" {
|
if streamid == "" || chunk == "" || path == "" {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
@ -109,11 +109,12 @@ func main() {
|
||||||
log.Println("Starting VOD server")
|
log.Println("Starting VOD server")
|
||||||
|
|
||||||
h := NewHandler(&Config{
|
h := NewHandler(&Config{
|
||||||
ffmpeg: "ffmpeg",
|
ffmpeg: "ffmpeg",
|
||||||
ffprobe: "ffprobe",
|
ffprobe: "ffprobe",
|
||||||
chunkSize: 3,
|
chunkSize: 3,
|
||||||
lookBehind: 5,
|
lookBehind: 5,
|
||||||
goalBuffer: 5,
|
goalBufferMin: 3,
|
||||||
|
goalBufferMax: 8,
|
||||||
})
|
})
|
||||||
|
|
||||||
http.Handle("/", h)
|
http.Handle("/", h)
|
||||||
|
|
|
@ -68,11 +68,7 @@ func NewManager(c *Config, path string, id string, close chan string) (*Manager,
|
||||||
bitrate: int(float64(highest) * 1.5),
|
bitrate: int(float64(highest) * 1.5),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("New manager", m.id,
|
log.Printf("%s: new manager for %s", m.id, m.path)
|
||||||
"with streams:", len(m.streams),
|
|
||||||
"duration:", m.probe.Duration,
|
|
||||||
"resolution:", m.probe.Width, "x", m.probe.Height,
|
|
||||||
)
|
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
24
stream.go
24
stream.go
|
@ -123,6 +123,10 @@ func (s *Stream) createChunk(id int) *Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) returnChunk(w http.ResponseWriter, chunk *Chunk) {
|
func (s *Stream) returnChunk(w http.ResponseWriter, chunk *Chunk) {
|
||||||
|
// This function is called with lock, but we don't need it
|
||||||
|
s.mutex.Unlock()
|
||||||
|
defer s.mutex.Lock()
|
||||||
|
|
||||||
// Read file and write to response
|
// Read file and write to response
|
||||||
filename := s.getTsPath(chunk.id)
|
filename := s.getTsPath(chunk.id)
|
||||||
f, err := os.Open(filename)
|
f, err := os.Open(filename)
|
||||||
|
@ -132,7 +136,6 @@ func (s *Stream) returnChunk(w http.ResponseWriter, chunk *Chunk) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
log.Printf("Served chunk %d", chunk.id)
|
|
||||||
w.Header().Set("Content-Type", "video/MP2T")
|
w.Header().Set("Content-Type", "video/MP2T")
|
||||||
io.Copy(w, f)
|
io.Copy(w, f)
|
||||||
}
|
}
|
||||||
|
@ -185,7 +188,7 @@ func (s *Stream) restartAtChunk(w http.ResponseWriter, id int) {
|
||||||
chunk := s.createChunk(id) // create first chunk
|
chunk := s.createChunk(id) // create first chunk
|
||||||
|
|
||||||
// Start the transcoder
|
// Start the transcoder
|
||||||
s.goal = id + 5
|
s.goal = id + s.c.goalBufferMax
|
||||||
s.transcode(id)
|
s.transcode(id)
|
||||||
|
|
||||||
s.waitForChunk(w, chunk) // this is also a request
|
s.waitForChunk(w, chunk) // this is also a request
|
||||||
|
@ -275,7 +278,7 @@ func (s *Stream) transcode(startId int) {
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
s.coder = exec.Command(s.c.ffmpeg, args...)
|
s.coder = exec.Command(s.c.ffmpeg, args...)
|
||||||
log.Println("Starting FFmpeg process with args", strings.Join(s.coder.Args[:], " "))
|
log.Printf("%s-%s: %s", strings.Join(s.coder.Args[:], " "))
|
||||||
|
|
||||||
cmdStdOut, err := s.coder.StdoutPipe()
|
cmdStdOut, err := s.coder.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -297,13 +300,13 @@ func (s *Stream) transcode(startId int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) checkGoal(id int) {
|
func (s *Stream) checkGoal(id int) {
|
||||||
goal := id + s.c.goalBuffer
|
goal := id + s.c.goalBufferMin
|
||||||
if goal > s.goal {
|
if goal > s.goal {
|
||||||
s.goal = goal
|
s.goal = id + s.c.goalBufferMax
|
||||||
|
|
||||||
// resume encoding
|
// resume encoding
|
||||||
if s.coder != nil {
|
if s.coder != nil {
|
||||||
log.Println("Resuming encoding")
|
log.Printf("%s-%s: resuming transcoding", s.m.id, s.quality)
|
||||||
s.coder.Process.Signal(syscall.SIGCONT)
|
s.coder.Process.Signal(syscall.SIGCONT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,6 +348,9 @@ func (s *Stream) monitorTranscodeOutput(cmdStdOut io.ReadCloser, startAt float64
|
||||||
l := string(line)
|
l := string(line)
|
||||||
|
|
||||||
if strings.Contains(l, ".ts") {
|
if strings.Contains(l, ".ts") {
|
||||||
|
// Debug
|
||||||
|
log.Printf("%s-%s: recv %s", s.m.id, s.quality, l)
|
||||||
|
|
||||||
// 1080p-000003.ts
|
// 1080p-000003.ts
|
||||||
idx := strings.Split(strings.Split(l, "-")[1], ".")[0]
|
idx := strings.Split(strings.Split(l, "-")[1], ".")[0]
|
||||||
id, err := strconv.Atoi(idx)
|
id, err := strconv.Atoi(idx)
|
||||||
|
@ -352,7 +358,7 @@ func (s *Stream) monitorTranscodeOutput(cmdStdOut io.ReadCloser, startAt float64
|
||||||
log.Println("Error parsing chunk id")
|
log.Println("Error parsing chunk id")
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
func() {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
@ -373,13 +379,11 @@ func (s *Stream) monitorTranscodeOutput(cmdStdOut io.ReadCloser, startAt float64
|
||||||
|
|
||||||
// Check goal satisfied
|
// Check goal satisfied
|
||||||
if id >= s.goal {
|
if id >= s.goal {
|
||||||
log.Println("Goal satisfied, pausing encoding")
|
log.Printf("%s-%s: goal satisfied: %d", s.m.id, s.quality, s.goal)
|
||||||
s.coder.Process.Signal(syscall.SIGSTOP)
|
s.coder.Process.Signal(syscall.SIGSTOP)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("ffmpeg:", l)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join the process
|
// Join the process
|
||||||
|
|
Loading…
Reference in New Issue