mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-10 15:26:38 +08:00
split transcoder service helpers
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// WaitReady blocks (with a deadline) until the playlist file shows up on
|
||||
// disk. Returns true on success.
|
||||
func (t *TranscoderService) WaitReady(ctx context.Context, mediaID string, timeout time.Duration) bool {
|
||||
deadline := time.Now().Add(timeout)
|
||||
for {
|
||||
if _, err := os.Stat(t.PlaylistPath(mediaID)); err == nil {
|
||||
t.mu.Lock()
|
||||
if j, ok := t.jobs[mediaID]; ok {
|
||||
j.playlistOK = true
|
||||
}
|
||||
t.mu.Unlock()
|
||||
return true
|
||||
}
|
||||
if time.Now().After(deadline) || ctx.Err() != nil {
|
||||
return false
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case <-time.After(250 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopJob cancels a running ffmpeg process for mediaID, if any.
|
||||
func (t *TranscoderService) StopJob(mediaID string) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if j, ok := t.jobs[mediaID]; ok {
|
||||
j.cancel()
|
||||
delete(t.jobs, mediaID)
|
||||
}
|
||||
}
|
||||
|
||||
// TouchJob records client activity for the HLS playlist or segment. The idle
|
||||
// watchdog uses it to stop ffmpeg soon after the player is closed or switches
|
||||
// back to direct play.
|
||||
func (t *TranscoderService) TouchJob(mediaID string) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.touchJobLocked(mediaID)
|
||||
}
|
||||
|
||||
func (t *TranscoderService) touchJobLocked(mediaID string) {
|
||||
if j, ok := t.jobs[mediaID]; ok {
|
||||
j.lastAccess = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// StopAll terminates every running transcode (called on graceful shutdown).
|
||||
func (t *TranscoderService) StopAll() {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
for id, j := range t.jobs {
|
||||
j.cancel()
|
||||
delete(t.jobs, id)
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveJob is the JSON shape exposed to the React Tasks panel.
|
||||
type ActiveJob struct {
|
||||
MediaID string `json:"media_id"`
|
||||
Encoder string `json:"encoder"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
PlaylistOK bool `json:"playlist_ok"`
|
||||
}
|
||||
|
||||
// Active returns a snapshot of the currently running transcode jobs.
|
||||
func (t *TranscoderService) Active() []ActiveJob {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
out := make([]ActiveJob, 0, len(t.jobs))
|
||||
for _, j := range t.jobs {
|
||||
out = append(out, ActiveJob{
|
||||
MediaID: j.mediaID,
|
||||
Encoder: j.encoder,
|
||||
StartedAt: j.startedAt,
|
||||
PlaylistOK: j.playlistOK,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t *TranscoderService) maxConcurrent() int {
|
||||
if t.cfg.Transcoder.MaxConcurrent <= 0 {
|
||||
return 1
|
||||
}
|
||||
return t.cfg.Transcoder.MaxConcurrent
|
||||
}
|
||||
|
||||
func (t *TranscoderService) idleTimeout() time.Duration {
|
||||
if t.cfg.Transcoder.IdleTimeoutSeconds <= 0 {
|
||||
return 120 * time.Second
|
||||
}
|
||||
return time.Duration(t.cfg.Transcoder.IdleTimeoutSeconds) * time.Second
|
||||
}
|
||||
|
||||
func (t *TranscoderService) monitorIdle(ctx context.Context, job *hlsJob) {
|
||||
timeout := t.idleTimeout()
|
||||
ticker := time.NewTicker(15 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
t.mu.Lock()
|
||||
current, ok := t.jobs[job.mediaID]
|
||||
if !ok {
|
||||
t.mu.Unlock()
|
||||
return
|
||||
}
|
||||
idleFor := time.Since(current.lastAccess)
|
||||
t.mu.Unlock()
|
||||
if idleFor >= timeout {
|
||||
t.log.Info("transcode idle timeout",
|
||||
zap.String("media_id", job.mediaID),
|
||||
zap.Duration("idle_for", idleFor),
|
||||
zap.Duration("timeout", timeout),
|
||||
)
|
||||
t.StopJob(job.mediaID)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user