parent
b0447572f2
commit
2705ffcb1c
@ -0,0 +1,192 @@ |
|||||||
|
package liveurls |
||||||
|
|
||||||
|
import ( |
||||||
|
"log" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"path/filepath" |
||||||
|
"sync" |
||||||
|
"syscall" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/gin-gonic/gin" |
||||||
|
) |
||||||
|
|
||||||
|
type FFMpegTask struct { |
||||||
|
URL string |
||||||
|
OutputDir string |
||||||
|
M3U8File string |
||||||
|
Cmd *exec.Cmd |
||||||
|
Mutex sync.Mutex |
||||||
|
LastCheck time.Time |
||||||
|
Active bool |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
tasks = make(map[string]*FFMpegTask) |
||||||
|
tasksMutex sync.Mutex |
||||||
|
checkPeriod = 10 * time.Second |
||||||
|
timeout = 1 * time.Minute |
||||||
|
) |
||||||
|
|
||||||
|
func clearOutputDir(dir string) error { |
||||||
|
files, err := os.ReadDir(dir) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
for _, file := range files { |
||||||
|
filePath := filepath.Join(dir, file.Name()) |
||||||
|
if err := os.RemoveAll(filePath); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func StartFFMpeg(task *FFMpegTask) { |
||||||
|
task.Mutex.Lock() |
||||||
|
defer task.Mutex.Unlock() |
||||||
|
|
||||||
|
if task.Active { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if err := clearOutputDir(task.OutputDir); err != nil { |
||||||
|
log.Printf("Failed to clear output directory for %s: %v\n", task.URL, err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
cmd := exec.Command("ffmpeg", "-fflags", "+genpts", "-analyzeduration", "1000000", |
||||||
|
"-i", task.URL, |
||||||
|
"-map", "0:v", "-map", "0:a", "-map", "0:a", |
||||||
|
"-c:v", "copy", |
||||||
|
"-c:a:0", "eac3", "-filter:a:0", "channelmap=0|1|2|3|4|5:FL+FR+FC+LFE+SL+SR", "-b:a:0", "384k", |
||||||
|
"-c:a:1", "copy", |
||||||
|
"-f", "hls", "-hls_time", "12", "-hls_list_size", "3", |
||||||
|
"-hls_flags", "delete_segments+append_list", |
||||||
|
"-hls_segment_filename", filepath.Join(task.OutputDir, "segment_%013d.ts"), |
||||||
|
"-rtbufsize", "100M", "-max_delay", "1000000", |
||||||
|
task.M3U8File, |
||||||
|
) |
||||||
|
err := cmd.Start() |
||||||
|
if err != nil { |
||||||
|
log.Printf("Failed to start ffmpeg for %s: %v\n", task.URL, err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
task.Cmd = cmd |
||||||
|
task.LastCheck = time.Now() |
||||||
|
task.Active = true |
||||||
|
|
||||||
|
log.Printf("Started ffmpeg for %s\n", task.URL) |
||||||
|
} |
||||||
|
|
||||||
|
func CheckFFMpeg(task *FFMpegTask) { |
||||||
|
task.Mutex.Lock() |
||||||
|
defer task.Mutex.Unlock() |
||||||
|
|
||||||
|
if task.Cmd == nil || task.Cmd.Process == nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Use syscall.Signal(0) to check if the process is still running
|
||||||
|
err := task.Cmd.Process.Signal(syscall.Signal(0)) |
||||||
|
if err != nil { |
||||||
|
log.Printf("FFmpeg process for %s stopped. Restarting...\n", task.URL) |
||||||
|
StartFFMpeg(task) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
currentTime := time.Now() |
||||||
|
if currentTime.Sub(task.LastCheck) > timeout { |
||||||
|
log.Printf("FFmpeg process for %s is stuck. Restarting...\n", task.URL) |
||||||
|
StartFFMpeg(task) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func HandleAV3ARequest(c *gin.Context) { |
||||||
|
tasksMutex.Lock() |
||||||
|
streamName := c.Param("rid") |
||||||
|
task, exists := tasks[streamName] |
||||||
|
if !exists { |
||||||
|
tasksMutex.Unlock() |
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "Stream not found"}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if !task.Active { |
||||||
|
StartFFMpeg(task) |
||||||
|
} |
||||||
|
tasksMutex.Unlock() |
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Stream is being prepared, please retry shortly"}) |
||||||
|
|
||||||
|
ticker := time.NewTicker(3 * time.Second) |
||||||
|
defer ticker.Stop() |
||||||
|
|
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-ticker.C: |
||||||
|
if _, err := os.Stat(task.M3U8File); err == nil { |
||||||
|
data, err := os.ReadFile(task.M3U8File) |
||||||
|
if err != nil { |
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read m3u8 file"}) |
||||||
|
return |
||||||
|
} |
||||||
|
c.Header("Content-Type", "application/vnd.apple.mpegurl") |
||||||
|
c.String(http.StatusOK, string(data)) |
||||||
|
return |
||||||
|
} |
||||||
|
default: |
||||||
|
time.Sleep(1 * time.Second) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func ConfigureTasks() { |
||||||
|
tasks = map[string]*FFMpegTask{ |
||||||
|
"cctv4k16_10m.m3u8": { |
||||||
|
URL: "http://192.168.10.1:35455/ysptp/cctv4k16_10m.m3u8", |
||||||
|
OutputDir: "/home/cctv4k1610m", |
||||||
|
M3U8File: "/home/cctv4k1610m/cctv4k16_10m.m3u8", |
||||||
|
}, |
||||||
|
"cctv4k16.m3u8": { |
||||||
|
URL: "http://192.168.10.1:35455/ysptp/cctv4k16.m3u8", |
||||||
|
OutputDir: "/home/cctv4k16", |
||||||
|
M3U8File: "/home/cctv4k16/cctv4k16.m3u8", |
||||||
|
}, |
||||||
|
"cctv4k_10m.m3u8": { |
||||||
|
URL: "http://192.168.10.1:35455/ysptp/cctv4k_10m.m3u8", |
||||||
|
OutputDir: "/home/cctv4k10m", |
||||||
|
M3U8File: "/home/cctv4k10m/cctv4k_10m.m3u8", |
||||||
|
}, |
||||||
|
"cctv4k.m3u8": { |
||||||
|
URL: "http://192.168.10.1:35455/ysptp/cctv4k.m3u8", |
||||||
|
OutputDir: "/home/cctv4k", |
||||||
|
M3U8File: "/home/cctv4k/cctv4k.m3u8", |
||||||
|
}, |
||||||
|
"cctv8k_36m.m3u8": { |
||||||
|
URL: "http://192.168.10.1:35455/ysptp/cctv8k_36m.m3u8", |
||||||
|
OutputDir: "/home/cctv8k36m", |
||||||
|
M3U8File: "/home/cctv8k36m/cctv8k_36m.m3u8", |
||||||
|
}, |
||||||
|
"cctv8k_120m.m3u8": { |
||||||
|
URL: "http://192.168.10.1:35455/ysptp/cctv8k_120m.m3u8", |
||||||
|
OutputDir: "/home/cctv8k120m", |
||||||
|
M3U8File: "/home/cctv8k120m/cctv8k_120m.m3u8", |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func MonitorTasks() { |
||||||
|
for { |
||||||
|
time.Sleep(checkPeriod) |
||||||
|
tasksMutex.Lock() |
||||||
|
for _, task := range tasks { |
||||||
|
CheckFFMpeg(task) |
||||||
|
} |
||||||
|
tasksMutex.Unlock() |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue