$c % 2 === 0)); $nodes[$node] = $cpus; } } ksort($nodes); return $nodes; } /* Allocate core: core-index first, node second */ function allocateCore(int $serviceId): array { global $coreFile; $map = json_decode(file_get_contents($coreFile), true) ?: []; $nodes = getNumaTopology(); $nodeIds = array_keys($nodes); $nodeCount = count($nodeIds); if ($nodeCount === 0) { return ["node" => 0, "cpu" => 0]; } // total services already allocated $index = count($map); /* * ROUND-ROBIN MATH (IMPORTANT) * * coreIndex = floor(index / nodeCount) * nodeIndex = index % nodeCount */ $coreIndex = intdiv($index, $nodeCount); $nodeIndex = $index % $nodeCount; $node = $nodeIds[$nodeIndex]; // wrap core index safely if cores exhausted $coreCount = count($nodes[$node]); if ($coreCount === 0) { return ["node" => $node, "cpu" => 0]; } $cpu = $nodes[$node][$coreIndex % $coreCount]; $map[$serviceId] = [ "node" => $node, "cpu" => $cpu ]; file_put_contents($coreFile, json_encode($map, JSON_PRETTY_PRINT)); return $map[$serviceId]; } /* Free core */ function freeCore(int $serviceId): void { global $coreFile; $map = json_decode(file_get_contents($coreFile), true) ?: []; if (isset($map[$serviceId])) { unset($map[$serviceId]); file_put_contents($coreFile, json_encode($map, JSON_PRETTY_PRINT)); } } /* Get assigned core */ function getServiceCore(int $serviceId): ?array { global $coreFile; $map = json_decode(file_get_contents($coreFile), true) ?: []; return $map[$serviceId] ?? null; } $core = (int)$alloc["cpu"]; $node = (int)$alloc["node"]; $ffmpeg = 'numactl --cpunodebind=' . $node . ' --membind=' . $node . ' taskset -c ' . $core . ' ffmpeg -hide_banner -loglevel info \ -thread_queue_size 65536 \ -fflags +genpts+discardcorrupt+nobuffer \ -readrate 1.0 \ -i "udp://@' . $new["input_udp"] . '?fifo_size=100000000&buffer_size=100000000&overrun_nonfatal=1" \ -vf "setpts=PTS-STARTPTS,yadif=mode=0:parity=0:deint=0,scale=' . $new["resolution"] . ',format=yuv420p" \ -c:v ' . $new["video_format"] . ' \ -threads 1 \ -r 25 \ -fps_mode cfr \ -g 12 \ -bf 0 \ -b:v ' . $new["video_bitrate"] . 'k \ -minrate ' . $new["video_bitrate"] . 'k \ -maxrate ' . $new["video_bitrate"] . 'k \ -bufsize ' . $new["video_bitrate"] . 'k \ -c:a ' . $new["audio_format"] . ' \ -b:a ' . $new["audio_bitrate"] . 'k \ -ar 48000 -ac 2 \ -af "volume=' . $new["volume"] . 'dB,aresample=async=1000" \ -metadata service_provider="ShreeBhattJI" '; if ($new["service_name"] !== "") { $ffmpeg .= '-metadata service_name="' . $new["service_name"] . '" '; } $ffmpeg .= '-pcr_period 20 \ -f mpegts "udp://' . $new["output_udp"] . '?pkt_size=1316&bitrate=4500000&flush_packets=1"'; if ($new["service_name"] !== "") $ffmpeg .= '-metadata service_name="' . $new["service_name"] . '"'; $ffmpeg .= ' -f mpegts "udp://@' . $new["output_udp"] . '?pkt_size=1316&bitrate=4500000"'; file_put_contents("/var/www/encoder/{$new["id"]}.sh", $ffmpeg); if ($new["service"] === "enable") { exec("sudo systemctl enable encoder@{$new["id"]}"); exec("sudo systemctl restart encoder@{$new["id"]}"); } echo "OK"; exit; } /* ---------------- DELETE ---------------- */ if ($_SERVER["REQUEST_METHOD"] === "POST" && $_POST["action"] === "delete") { $id = intval($_POST["id"]); $newData = []; foreach ($data as $row) { if ($row["id"] != $id) $newData[] = $row; } file_put_contents($jsonFile, json_encode($newData, JSON_PRETTY_PRINT)); exec("sudo systemctl stop encoder@$id"); exec("sudo systemctl disable encoder@$id"); freeCore($id); if (file_exists("/var/www/encoder/$id.sh")) unlink("/var/www/encoder/$id.sh"); echo "OK"; exit; } /* ---------------- EDIT ---------------- */ if ($_SERVER["REQUEST_METHOD"] === "POST" && $_POST["action"] === "edit") { $id = intval($_POST["id"]); $newData = []; foreach ($data as $row) { if ($row["id"] == $id) { $row = [ "id" => $id, "service_name" => $_POST["service_name"], "input_udp" => $_POST["input_udp"], "output_udp" => $_POST["output_udp"], "video_format" => $_POST["video_format"], "audio_format" => $_POST["audio_format"], "resolution" => $_POST["resolution"], "video_bitrate" => $_POST["video_bitrate"], "audio_bitrate" => $_POST["audio_bitrate"], "volume" => $_POST["volume"], "service" => $_POST["service"] ]; $new = $row; $alloc = getServiceCore($id); if ($alloc === null) { $alloc = allocateCore($id); } $core = (int)$alloc["cpu"]; $node = (int)$alloc["node"]; $ffmpeg = 'numactl --cpunodebind=' . $node . ' --membind=' . $node . ' taskset -c ' . $core . ' ffmpeg -hide_banner -loglevel info \ -thread_queue_size 65536 \ -fflags +genpts+discardcorrupt+nobuffer \ -readrate 1.0 \ -i "udp://@' . $new["input_udp"] . '?fifo_size=100000000&buffer_size=100000000&overrun_nonfatal=1" \ -vf "setpts=PTS-STARTPTS,yadif=mode=0:parity=0:deint=0,scale=' . $new["resolution"] . ',format=yuv420p" \ -c:v ' . $new["video_format"] . ' \ -threads 1 \ -r 25 \ -fps_mode cfr \ -g 12 \ -bf 0 \ -b:v ' . $new["video_bitrate"] . 'k \ -minrate ' . $new["video_bitrate"] . 'k \ -maxrate ' . $new["video_bitrate"] . 'k \ -bufsize ' . $new["video_bitrate"] . 'k \ -c:a ' . $new["audio_format"] . ' \ -b:a ' . $new["audio_bitrate"] . 'k \ -ar 48000 -ac 2 \ -af "volume=' . $new["volume"] . 'dB,aresample=async=1000" \ -metadata service_provider="ShreeBhattJI" '; if ($new["service_name"] !== "") { $ffmpeg .= '-metadata service_name="' . $new["service_name"] . '" '; } $ffmpeg .= '-pcr_period 20 \ -f mpegts "udp://' . $new["output_udp"] . '?pkt_size=1316&bitrate=4500000&flush_packets=1"'; file_put_contents("/var/www/encoder/$id.sh", $ffmpeg); if ($new["service"] === "enable") { exec("sudo systemctl enable encoder@$id"); exec("sudo systemctl restart encoder@$id"); } else { exec("sudo systemctl stop encoder@$id"); exec("sudo systemctl disable encoder@$id"); } } $newData[] = $row; } file_put_contents($jsonFile, json_encode($newData, JSON_PRETTY_PRINT)); echo "OK"; exit; } /* ---------------- RESTART ---------------- */ if ($_SERVER["REQUEST_METHOD"] === "POST" && $_POST["action"] === "restart") { $id = intval($_POST["id"]); exec("sudo systemctl restart encoder@$id"); echo "OK"; exit; } ?>
| ID | Service Name | Input | Output | Video | Audio | Resolution | V-Bitrate | A-Bitrate | Volume (dB) | Status | Actions |
|---|---|---|---|---|---|---|---|---|---|---|---|
| = $row["id"] ?> | = $row["service_name"] ?> | = $row["input_udp"] ?> | = $row["output_udp"] ?> | = $row["video_format"] ?> | = $row["audio_format"] ?> | = $row["resolution"] ?> | = $row["video_bitrate"] ?> | = $row["audio_bitrate"] ?> | = $row["volume"] ?> dB | = $row["service"] ?> |