From 7545f9991c84715eafda8a69497dedaf5663fe36 Mon Sep 17 00:00:00 2001 From: Divarion_D Date: Sun, 16 Aug 2026 21:17:01 +0300 Subject: [PATCH] refactor(streaming): remove dead code orphaned by the daemon cutover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audited the streaming subsystem for symbols with zero real callers (ruled out dynamic dispatch: command-name strings, routes, #[ListensTo], self::/$this-> internal calls, bare-name string dispatch). Removals, each grep-verified across all of src/: - Whole class TS (src/Streaming/TimeshiftClient.php) — superseded by the inline TS byte-parsing in LLOD/Loopback; only ref left was a stale comment. Drops its 2 require + 3 use lines too. - StreamUtils::getTSInfo — 0 callers. - SegmentReader::getLLODSegments — replaced by the LLOD-v3 daemon feed; 0 callers. - ProcessChecker::isPIDRunning + isPIDsRunning (dead pair) — 0 callers; drops the now-orphaned CurlClient import. - ProcessManager: checkPidFile + matchesCmdline (dead pair), killByPattern, countProcesses, currentPid, releaseCronLock — all 0 callers (acquireCronLock stays; locks self-release on exit). - AsyncFileOperations: checkFilesExists, awaitFileExistsAdaptive, awaitFileModified, getCacheStats, filterExistingFiles — never-wired public helpers, 0 refs. php -l clean on all touched files; make gates green; no phpstan-baseline entries reference the removed symbols. --- src/Cli/Commands/LlodCommand.php | 1 - src/Cli/Commands/LoopbackCommand.php | 8 +- src/Core/Process/ProcessManager.php | 92 -------------- src/Core/Util/StreamUtils.php | 10 -- src/Public/stream/live.php | 1 - src/Streaming/AsyncFileOperations.php | 99 ---------------- src/Streaming/Delivery/SegmentReader.php | 34 ------ src/Streaming/Health/ProcessChecker.php | 37 ------ src/Streaming/TimeshiftClient.php | 145 ----------------------- 9 files changed, 3 insertions(+), 424 deletions(-) delete mode 100644 src/Streaming/TimeshiftClient.php diff --git a/src/Cli/Commands/LlodCommand.php b/src/Cli/Commands/LlodCommand.php index 4191c3ec..388ab7f3 100644 --- a/src/Cli/Commands/LlodCommand.php +++ b/src/Cli/Commands/LlodCommand.php @@ -90,7 +90,6 @@ class LlodCommand implements CommandInterface { set_time_limit(0); error_reporting(E_WARNING | E_PARSE); cli_set_process_title('LLOD[' . $rStreamID . ']'); - require MAIN_HOME . 'Streaming/TimeshiftClient.php'; $rSettings = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'settings')); diff --git a/src/Cli/Commands/LoopbackCommand.php b/src/Cli/Commands/LoopbackCommand.php index 567ce00c..34996fd5 100644 --- a/src/Cli/Commands/LoopbackCommand.php +++ b/src/Cli/Commands/LoopbackCommand.php @@ -5,7 +5,6 @@ namespace XcVm\Cli\Commands; use XcVm\Cli\CommandInterface; use XcVm\Core\Config\ConfigReader; use XcVm\Domain\Stream\StreamProcess; -use XcVm\Streaming\TS; /** * LoopbackCommand — loopback command @@ -112,7 +111,6 @@ class LoopbackCommand implements CommandInterface { set_time_limit(0); cli_set_process_title('Loopback[' . $rStreamID . ']'); - require MAIN_HOME . 'Streaming/TimeshiftClient.php'; $rSettings = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'settings')); $rServers = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'servers')); @@ -200,9 +198,9 @@ class LoopbackCommand implements CommandInterface { $rAdaptationField = $rHeader >> 4 & 3; if (($rAdaptationField & 2) === 2) { if (0 < count($rPATHeaders) && unpack('C', $rPacket[4])[1] == 7 && substr($rPacket, 4, 2) == KEYFRAME_HEADER) { - // Extract PCR directly from the adaptation field. TS::parsePacket() is - // unsuitable here — its getBits() only advances on zero bits and returns - // garbage PTS, causing the gate to never fire (one giant segment). + // Extract PCR directly from the adaptation field (parsed inline — a + // generic bit-reader mis-advances on zero bits and returns garbage PTS, + // causing the gate to never fire → one giant segment). // Keyframe flags are 0x50, so PCR_flag (0x10) is set: PCR base in the // 5 bytes starting at offset 6. $rKfPTS = null; diff --git a/src/Core/Process/ProcessManager.php b/src/Core/Process/ProcessManager.php index 4d38dc31..24fec359 100644 --- a/src/Core/Process/ProcessManager.php +++ b/src/Core/Process/ProcessManager.php @@ -164,52 +164,6 @@ class ProcessManager { return false; } - /** - * Check if a process is alive using a PID file - * - * Reads PID from file, then checks /proc/PID/cmdline for expected string. - * - * @param string $pidFile Path to PID file - * @param string $searchString Expected string in cmdline - * @return bool - */ - public static function checkPidFile($pidFile, $searchString) { - if (!file_exists($pidFile)) { - return false; - } - - $pid = (int)trim(file_get_contents($pidFile)); - - if ($pid <= 0) { - return false; - } - - return self::matchesCmdline($pid, $searchString); - } - - /** - * Check if a process cmdline contains a search string - * - * @param int $pid Process ID - * @param string $search String to look for in cmdline - * @return bool - */ - public static function matchesCmdline($pid, $search) { - $pid = (int)$pid; - - if ($pid <= 0 || !self::procExists($pid)) { - return false; - } - - $cmdline = @file_get_contents('/proc/' . $pid . '/cmdline'); - - if ($cmdline === false) { - return false; - } - - return stripos($cmdline, $search) !== false; - } - // ─────────────────────────────────────────────────────────── // Process Control // ─────────────────────────────────────────────────────────── @@ -235,25 +189,6 @@ class ProcessManager { return posix_kill($pid, $signal); } - /** - * Kill all processes matching a pattern via cmdline - * - * @param string $pattern Pattern to match in ps output - */ - public static function killByPattern($pattern) { - $pattern = escapeshellarg($pattern); - shell_exec("kill -9 `ps -ef | grep {$pattern} | grep -v grep | awk '{print \$2}'`"); - } - - /** - * Get the current process PID - * - * @return int - */ - public static function currentPid() { - return getmypid(); - } - /** * Get the age of a process in seconds (how long it has been running). * @@ -360,22 +295,6 @@ class ProcessManager { return true; } - /** - * Release a cron lock - * - * @param string $lockFile Path to PID lock file - */ - public static function releaseCronLock($lockFile) { - if (file_exists($lockFile)) { - $pid = (int)trim(file_get_contents($lockFile)); - - // Only remove if it's our lock - if ($pid === getmypid()) { - unlink($lockFile); - } - } - } - // ─────────────────────────────────────────────────────────── // Internal Helpers // ─────────────────────────────────────────────────────────── @@ -512,17 +431,6 @@ class ProcessManager { // Utility // ─────────────────────────────────────────────────────────── - /** - * Count running processes matching a pattern - * - * @param string $pattern grep pattern - * @return int - */ - public static function countProcesses($pattern) { - $pattern = escapeshellarg($pattern); - return (int)trim(shell_exec("ps ax | grep -v grep | grep -c {$pattern}")); - } - /** * Check if an nginx master process is running. * diff --git a/src/Core/Util/StreamUtils.php b/src/Core/Util/StreamUtils.php index b4bf1af3..e0302d52 100644 --- a/src/Core/Util/StreamUtils.php +++ b/src/Core/Util/StreamUtils.php @@ -3,7 +3,6 @@ namespace XcVm\Core\Util; use XcVm\Core\Process\ProcessManager; -use XcVm\Streaming\TS; /** * StreamUtils — stream utils @@ -362,13 +361,4 @@ class StreamUtils { return false; } - /** - * Probe an MPEG-TS file via the bundled `tsinfo` binary. - * - * @param string $rFilename Path to the .ts file. - * @return array|null Decoded probe data, or null on failure. - */ - public static function getTSInfo($rFilename) { - return json_decode(shell_exec(BIN_PATH . 'tsinfo ' . escapeshellarg($rFilename)), true); - } } diff --git a/src/Public/stream/live.php b/src/Public/stream/live.php index abde0e2f..8aa6198b 100644 --- a/src/Public/stream/live.php +++ b/src/Public/stream/live.php @@ -16,7 +16,6 @@ use XcVm\Streaming\Delivery\SegmentReader; use XcVm\Streaming\Delivery\SignalSender; use XcVm\Streaming\Fanout\FanoutClient; use XcVm\Streaming\Lifecycle\ShutdownHandler; -use XcVm\Streaming\TS; /** * Live stream delivery endpoint diff --git a/src/Streaming/AsyncFileOperations.php b/src/Streaming/AsyncFileOperations.php index 0c9705cc..e904c7fb 100644 --- a/src/Streaming/AsyncFileOperations.php +++ b/src/Streaming/AsyncFileOperations.php @@ -177,21 +177,6 @@ class AsyncFileOperations { return $content; } - /** - * Batch check multiple files existence - * More efficient than individual checks - * - * @param array $files Array of file paths - * @return array Array with file => bool mapping - */ - public static function checkFilesExists(array $files) { - $results = []; - foreach ($files as $file) { - $results[$file] = @stat($file) !== false; - } - return $results; - } - /** * Wait for ANY file from list to exist * Returns on first match or after timeout @@ -216,67 +201,6 @@ class AsyncFileOperations { return false; } - /** - * Adaptive wait with exponential backoff - * Starts with short waits, then increases delay - * - * @param string $file File path to monitor - * @param int $maxRetries Maximum retry attempts - * @param int $initialDelayMs Initial delay in milliseconds - * @return bool - */ - public static function awaitFileExistsAdaptive($file, $maxRetries = 300, $initialDelayMs = 10) { - $delay = max(1000, $initialDelayMs * 1000); // Start in microseconds - $maxDelay = 500000; // Cap at 500ms - - for ($i = 0; $i < $maxRetries; $i++) { - if (@stat($file) !== false) { - return true; - } - - usleep($delay); - - // Increase delay exponentially but cap it - $delay = min($maxDelay, intval($delay * 1.2)); - } - - return false; - } - - /** - * Monitor file for changes (mtime) - * More efficient than polling - * - * @param string $file File path - * @param int $timeoutSeconds Maximum wait time - * @return bool True if file was modified - */ - public static function awaitFileModified($file, $timeoutSeconds = 30) { - $stat = @stat($file); - if ($stat === false) { - return false; - } - - $originalMtime = $stat['mtime']; - $endTime = time() + $timeoutSeconds; - - while (time() < $endTime) { - $currentStat = @stat($file); - - if ($currentStat === false) { - return false; - } - - if ($currentStat['mtime'] > $originalMtime) { - return true; - } - - usleep(50000); // 50ms check interval - } - - return false; - } - /** * Clear cache for specific file * @param string $file File path @@ -289,17 +213,6 @@ class AsyncFileOperations { } } - /** - * Get cache statistics for debugging - * @return array - */ - public static function getCacheStats() { - return [ - 'cached_files' => count(self::$fileCache), - 'cache_memory' => memory_get_usage(true), - ]; - } - /** * Replacement for usleep with select() * More CPU-efficient than usleep in loops @@ -335,16 +248,4 @@ class AsyncFileOperations { return $stat !== false ? $stat['size'] : false; } - /** - * Check if multiple files exist in parallel fashion - * Better than sequential checks - * - * @param array $files File paths to check - * @return array Files that exist - */ - public static function filterExistingFiles(array $files) { - return array_filter($files, function ($file) { - return @stat($file) !== false; - }); - } } diff --git a/src/Streaming/Delivery/SegmentReader.php b/src/Streaming/Delivery/SegmentReader.php index a99c23e1..d3922941 100644 --- a/src/Streaming/Delivery/SegmentReader.php +++ b/src/Streaming/Delivery/SegmentReader.php @@ -13,40 +13,6 @@ namespace XcVm\Streaming\Delivery; */ class SegmentReader { - public static function getLLODSegments($rStreamID, $rPlaylist, $rPrebuffer = 1) { - $rPrebuffer++; - $rSegments = $rKeySegments = array(); - if (!file_exists($rPlaylist)) { - return null; - } - $rSource = file_get_contents($rPlaylist); - if (!preg_match_all('/(.*?).ts((#\\w+)+|#?)/', $rSource, $rMatches)) { - return null; - } - if (0 >= count($rMatches[1])) { - return null; - } - $rLastKey = null; - for ($i = 0; $i < count($rMatches[1]); $i++) { - $rFilename = $rMatches[1][$i]; - list($rSID, $rSegmentID) = explode('_', $rFilename); - if (!empty($rMatches[2][$i])) { - $rKeySegments[$rSegmentID] = array(); - $rLastKey = $rSegmentID; - } - if ($rLastKey) { - $rKeySegments[$rLastKey][] = $rSegmentID; - } - } - $rKeySegments = array_slice($rKeySegments, count($rKeySegments) - $rPrebuffer, $rPrebuffer, true); - foreach ($rKeySegments as $rKeySegment => $rSubSegments) { - foreach ($rSubSegments as $rSegmentID) { - $rSegments[] = $rStreamID . '_' . $rSegmentID . '.ts'; - } - } - return (!empty($rSegments) ? $rSegments : null); - } - public static function getPlaylistSegments($rPlaylist, $rPrebuffer = 0, $rSegmentDuration = 10) { if (!file_exists($rPlaylist)) { return null; diff --git a/src/Streaming/Health/ProcessChecker.php b/src/Streaming/Health/ProcessChecker.php index 6d5cb475..e00afbe6 100644 --- a/src/Streaming/Health/ProcessChecker.php +++ b/src/Streaming/Health/ProcessChecker.php @@ -2,7 +2,6 @@ namespace XcVm\Streaming\Health; -use XcVm\Core\Http\CurlClient; /** * ProcessChecker — process checker @@ -15,42 +14,6 @@ use XcVm\Core\Http\CurlClient; */ class ProcessChecker { - public static function isPIDsRunning($rServerIDS, $rPIDs, $rEXE) { - global $rServers; - if (!is_array($rServerIDS)) { - $rServerIDS = array(intval($rServerIDS)); - } - $rPIDs = array_map('intval', $rPIDs); - $rOutput = array(); - foreach ($rServerIDS as $rServerID) { - if (is_array($rServers) && array_key_exists($rServerID, $rServers)) { - $rResponse = CurlClient::serverRequest($rServerID, $rServers[$rServerID]['api_url_ip'] . '&action=pidsAreRunning', array('program' => $rEXE, 'pids' => $rPIDs)); - if ($rResponse) { - $rDecoded = json_decode($rResponse, true); - if (is_array($rDecoded)) { - $rOutput[$rServerID] = array_map('trim', $rDecoded); - } else { - $rOutput[$rServerID] = false; - } - } else { - $rOutput[$rServerID] = false; - } - } - } - return $rOutput; - } - - public static function isPIDRunning($rServerID, $rPID, $rEXE) { - global $rServers; - if (!is_null($rPID) && is_numeric($rPID) && is_array($rServers) && array_key_exists($rServerID, $rServers)) { - if (!($rOutput = self::isPIDsRunning($rServerID, array($rPID), $rEXE))) { - return false; - } - return $rOutput[$rServerID][$rPID] ?? false; - } - return false; - } - public static function checkPID($rPID, $rSearch) { if (!is_array($rSearch)) { $rSearch = array($rSearch); diff --git a/src/Streaming/TimeshiftClient.php b/src/Streaming/TimeshiftClient.php deleted file mode 100644 index f77baab1..00000000 --- a/src/Streaming/TimeshiftClient.php +++ /dev/null @@ -1,145 +0,0 @@ - - * @copyright 2025-2026 Vateron Media - * @link https://github.com/Vateron-Media/XC_VM - * @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html - */ - -class TS { - public static $rBuffer = null; - public static $rPosition = null; - public static $rByte = null; - public static $rIndex = null; - - public function setPacket($rBuffer) { - self::$rBuffer = $rBuffer; - self::$rPosition = 7; - list(self::$rByte) = array_values(unpack('C', self::$rBuffer[0])); - self::$rIndex = 1; - } - - public function getBits($rNumBits) { - $rNum = 0; - $rMask = 1 << self::$rPosition; - - while (0 < $rNumBits) { - $rNumBits -= 1; - $rNum <<= 1; - - if (!(self::$rByte & $rMask)) { - $rMask >>= 1; - - - self::$rPosition -= 1; - - if (self::$rPosition < 0) { - self::$rPosition = 7; - $rMask = 1 << self::$rPosition; - - if (self::$rIndex < strlen(self::$rBuffer)) { - list(self::$rByte) = array_values(unpack('C', self::$rBuffer[self::$rIndex])); - } else { - self::$rByte = 0; - } - } - } else { - $rNum |= 1; - } - - self::$rIndex += 1; - } - - return $rNum; - } - - public function parsePacket() { - $rReturn = array('sync_byte' => self::getBits(8), 'transport_error_indicator' => self::getBits(1), 'payload_unit_start_indicator' => self::getBits(1), 'transport_priority' => self::getBits(1), 'pid' => self::getBits(13), 'scrambling_control' => self::getBits(2), 'adaptation_field_exist' => self::getBits(2), 'continuity_counter' => self::getBits(4)); - - if ($rReturn['adaptation_field_exist']) { - $rTell = self::$rIndex; - $rReturn['adaptation_field_length'] = self::getBits(8); - - if ($rReturn['adaptation_field_length'] == 7) { - $rReturn = array_merge($rReturn, array('discontinuity_indicator' => self::getBits(1), 'random_access_indicator' => self::getBits(1), 'priority_indicator' => self::getBits(1), 'pcr_flag' => self::getBits(1), 'opcr_flag' => self::getBits(1), 'splicing_point_flag' => self::getBits(1), 'transport_private_data_flag' => self::getBits(1), 'adaptation_field_extension_flag' => self::getBits(1))); - - if ($rReturn['pcr_flag']) { - $rReturn = array_merge($rReturn, array('program_clock_reference_base' => self::getBits(33), 'reserved_pcr' => self::getBits(6), 'program_clock_reference_extension' => self::getBits(9))); - $rReturn['pcr'] = ($rReturn['program_clock_reference_base'] * 300 + $rReturn['program_clock_reference_extension']) / 27000000; - } - - if ($rReturn['opcr_flag']) { - $rReturn = array_merge($rReturn, array('original_program_clock_reference_base' => self::getBits(33), 'reserved_opcr' => self::getBits(6), 'original_program_clock_reference_extension' => self::getBits(9))); - $rReturn['opcr'] = ($rReturn['original_program_clock_reference_base'] * 300 + $rReturn['original_program_clock_reference_extension']) / 27000000; - } - - if ($rReturn['splicing_point_flag']) { - $rReturn['splice_countdown'] = self::getBits(8); - } - - if ($rReturn['transport_private_data_flag']) { - $rReturn['transport_private_data_length'] = self::getBits(8); - self::stepBytes($rReturn['transport_private_data_length']); - } - } else { - unset($rReturn['adaptation_field_length']); - } - } - - if ($rReturn['pid'] == 0) { - $rReturn['pointer_field'] = self::getBits(8); - - if ($rReturn['pointer_field']) { - self::stepBytes($rReturn['pointer_field']); - } - - $rReturn = array_merge($rReturn, array('type' => 'pat', 'table_id' => self::getBits(8), 'section_syntax_indicator' => self::getBits(1), 'marker' => self::getBits(1), 'reserved_1' => self::getBits(2), 'section_length' => self::getBits(12), 'transport_stream_id' => self::getBits(16), 'reserved_2' => self::getBits(2), 'version_number' => self::getBits(5), 'current_next_indicator' => self::getBits(1), 'section_number' => self::getBits(8), 'last_section_number' => self::getBits(8))); - } else { - if ($rReturn['payload_unit_start_indicator']) { - self::$rBuffer = substr(self::$rBuffer, self::$rIndex, 188); - self::$rIndex = 0; - $rReturn = array_merge($rReturn, array('type' => 'pes', 'packet_start_prefix' => self::getBits(24), 'stream_id' => self::getBits(8), 'pes_packet_length' => self::getBits(16), 'marker_bits' => self::getBits(2), 'scrambling_control' => self::getBits(2), 'priority' => self::getBits(1), 'data_alignment_indicator' => self::getBits(1), 'copyright' => self::getBits(1), 'original_or_copy' => self::getBits(1), 'pts_dts_indicator' => self::getBits(2), 'escr_flag' => self::getBits(1), 'es_rate_flag' => self::getBits(1), 'dsm_trick_mode_flag' => self::getBits(1), 'additional_copy_info_flag' => self::getBits(1), 'crc_flag' => self::getBits(1), 'extension_flag' => self::getBits(1), 'pes_header_length' => self::getBits(8))); - - if (($rReturn['pts_dts_indicator'] == 2 || $rReturn['pts_dts_indicator'] == 3)) { - self::getBits(4); - $rPTSA = self::getBits(3); - self::getBits(1); - $rPTSB = self::getBits(15); - self::getBits(1); - $rPTSC = self::getBits(15); - self::getBits(1); - $rReturn['pts'] = ($rPTSA << 30) + ($rPTSB << 15) + $rPTSC; - } - - if ($rReturn['pts_dts_indicator'] == 3) { - self::getBits(4); - $rDTSA = self::getBits(3); - self::getBits(1); - $rDTSB = self::getBits(15); - self::getBits(1); - $rDTSC = self::getBits(15); - self::getBits(1); - $rReturn['dts'] = ($rDTSA << 30) + ($rDTSB << 15) + $rDTSC; - } - } - } - - return $rReturn; - } - - public function stepBytes($rBytes) { - $rData = substr(self::$rBuffer, self::$rIndex - 1, $rBytes); - - foreach (range(0, $rBytes) as $i) { - self::getBits(8); - } - - return $rData; - } -}