You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
4.4 KiB
192 lines
4.4 KiB
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()
|
|
}
|
|
}
|
|
|