Files
easystream-main/f_core/f_classes/class.livestreaming.php
SamiAhmed7777 0b7e2d0a5b feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory
- Added PROJECT_OVERVIEW.md with feature highlights and getting started guide
- Added ARCHITECTURE.md with system design and technical details
- Added SECURITY.md with comprehensive security implementation guide
- Added DEVELOPMENT.md with development workflows and best practices
- Added DEPLOYMENT.md with production deployment instructions
- Added API.md with complete REST API documentation
- Added CONTRIBUTING.md with contribution guidelines
- Added CHANGELOG.md with version history and migration notes
- Reorganized all documentation files into docs/ directory for better organization
- Updated README.md with proper documentation links and quick navigation
- Enhanced project structure with professional documentation standards
2025-10-21 00:39:45 -07:00

463 lines
15 KiB
PHP

<?php
/*******************************************************************************************************************
| Software Name : EasyStream
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
| Software Author : (c) Sami Ahmed
|*******************************************************************************************************************
|
|*******************************************************************************************************************
| This source file is subject to the EasyStream Proprietary License Agreement.
|
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|*******************************************************************************************************************
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|*******************************************************************************************************************/
defined('_ISVALID') or header('Location: /error');
/**
* Live Streaming System with SRS Integration
*/
class VLiveStreaming
{
private $logger;
private $db;
private $queue;
private $srsConfig;
// SRS default configuration
private $srsHost = 'localhost';
private $srsRtmpPort = 1935;
private $srsHttpPort = 8080;
private $srsApiPort = 1985;
public function __construct()
{
$this->logger = VLogger::getInstance();
$this->db = VDatabase::getInstance();
$this->queue = new VQueueManager();
$this->loadSRSConfig();
}
/**
* Create live stream
* @param int $userId User ID
* @param array $streamData Stream configuration
* @return array Stream creation result
*/
public function createStream($userId, $streamData)
{
try {
$streamKey = $this->generateStreamKey();
$stream = [
'stream_key' => $streamKey,
'user_id' => $userId,
'title' => $streamData['title'] ?? 'Live Stream',
'description' => $streamData['description'] ?? '',
'category' => $streamData['category'] ?? 'general',
'privacy' => $streamData['privacy'] ?? 'public',
'status' => 'created',
'rtmp_url' => "rtmp://{$this->srsHost}:{$this->srsRtmpPort}/live/{$streamKey}",
'hls_url' => "http://{$this->srsHost}:{$this->srsHttpPort}/live/{$streamKey}.m3u8",
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
];
$streamId = $this->db->doInsert('db_live_streams', $stream);
if (!$streamId) {
throw new Exception('Failed to create stream record');
}
$this->logger->info('Live stream created', [
'stream_id' => $streamId,
'stream_key' => $streamKey,
'user_id' => $userId
]);
return [
'success' => true,
'stream_id' => $streamId,
'stream_key' => $streamKey,
'rtmp_url' => $stream['rtmp_url'],
'hls_url' => $stream['hls_url'],
'stream_data' => $stream
];
} catch (Exception $e) {
$this->logger->error('Failed to create live stream', [
'user_id' => $userId,
'error' => $e->getMessage()
]);
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Start live stream
* @param string $streamKey Stream key
* @return array Start result
*/
public function startStream($streamKey)
{
try {
$stream = $this->getStreamByKey($streamKey);
if (!$stream) {
throw new Exception('Stream not found');
}
// Update stream status
$this->db->doUpdate('db_live_streams', 'id', [
'status' => 'live',
'started_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
], $stream['id']);
// Notify subscribers
$this->notifyStreamStart($stream);
$this->logger->info('Live stream started', [
'stream_key' => $streamKey,
'stream_id' => $stream['id']
]);
return [
'success' => true,
'message' => 'Stream started successfully'
];
} catch (Exception $e) {
$this->logger->error('Failed to start stream', [
'stream_key' => $streamKey,
'error' => $e->getMessage()
]);
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Stop live stream
* @param string $streamKey Stream key
* @param bool $saveRecording Whether to save recording
* @return array Stop result
*/
public function stopStream($streamKey, $saveRecording = true)
{
try {
$stream = $this->getStreamByKey($streamKey);
if (!$stream) {
throw new Exception('Stream not found');
}
// Update stream status
$updateData = [
'status' => 'ended',
'ended_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
];
if ($saveRecording) {
// Queue recording processing job
$this->queue->enqueue('StreamRecordingJob', [
'stream_id' => $stream['id'],
'stream_key' => $streamKey,
'user_id' => $stream['user_id']
], 'video_processing', 0, 1);
$updateData['recording_queued'] = 1;
}
$this->db->doUpdate('db_live_streams', 'id', $updateData, $stream['id']);
$this->logger->info('Live stream stopped', [
'stream_key' => $streamKey,
'stream_id' => $stream['id'],
'save_recording' => $saveRecording
]);
return [
'success' => true,
'message' => 'Stream stopped successfully',
'recording_queued' => $saveRecording
];
} catch (Exception $e) {
$this->logger->error('Failed to stop stream', [
'stream_key' => $streamKey,
'error' => $e->getMessage()
]);
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Get live streams
* @param array $filters Filters
* @param int $limit Limit
* @param int $offset Offset
* @return array Streams
*/
public function getLiveStreams($filters = [], $limit = 20, $offset = 0)
{
try {
$whereClause = "WHERE 1=1";
$params = [];
if (isset($filters['status'])) {
$whereClause .= " AND status = ?";
$params[] = $filters['status'];
}
if (isset($filters['user_id'])) {
$whereClause .= " AND user_id = ?";
$params[] = $filters['user_id'];
}
if (isset($filters['category'])) {
$whereClause .= " AND category = ?";
$params[] = $filters['category'];
}
$query = "SELECT ls.*, au.usr_user, au.usr_avatar
FROM db_live_streams ls
JOIN db_accountuser au ON ls.user_id = au.usr_id
{$whereClause}
ORDER BY ls.created_at DESC
LIMIT ? OFFSET ?";
$params[] = $limit;
$params[] = $offset;
$result = $this->db->doQuery($query, $params);
$streams = [];
while ($row = $this->db->doFetch($result)) {
$streams[] = $this->formatStreamData($row);
}
return [
'success' => true,
'streams' => $streams,
'total' => $this->getStreamCount($filters)
];
} catch (Exception $e) {
$this->logger->error('Failed to get live streams', [
'filters' => $filters,
'error' => $e->getMessage()
]);
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Get stream statistics
* @param string $streamKey Stream key
* @return array Statistics
*/
public function getStreamStats($streamKey)
{
try {
$stream = $this->getStreamByKey($streamKey);
if (!$stream) {
throw new Exception('Stream not found');
}
// Get viewer count from SRS API
$viewerCount = $this->getSRSViewerCount($streamKey);
// Get chat message count
$chatCount = $this->getChatMessageCount($stream['id']);
// Calculate duration
$duration = 0;
if ($stream['started_at']) {
$endTime = $stream['ended_at'] ? strtotime($stream['ended_at']) : time();
$duration = $endTime - strtotime($stream['started_at']);
}
return [
'success' => true,
'stats' => [
'viewer_count' => $viewerCount,
'chat_messages' => $chatCount,
'duration' => $duration,
'status' => $stream['status'],
'started_at' => $stream['started_at'],
'ended_at' => $stream['ended_at']
]
];
} catch (Exception $e) {
$this->logger->error('Failed to get stream stats', [
'stream_key' => $streamKey,
'error' => $e->getMessage()
]);
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* Private helper methods
*/
private function loadSRSConfig()
{
// Load SRS configuration from database or config file
$this->srsConfig = [
'host' => $this->srsHost,
'rtmp_port' => $this->srsRtmpPort,
'http_port' => $this->srsHttpPort,
'api_port' => $this->srsApiPort
];
}
private function generateStreamKey()
{
return uniqid('stream_', true) . '_' . time();
}
private function getStreamByKey($streamKey)
{
$query = "SELECT * FROM db_live_streams WHERE stream_key = ?";
$result = $this->db->doQuery($query, [$streamKey]);
return $this->db->doFetch($result);
}
private function notifyStreamStart($stream)
{
// Get user's followers
$query = "SELECT follower_id FROM db_user_follows WHERE user_id = ?";
$result = $this->db->doQuery($query, [$stream['user_id']]);
while ($row = $this->db->doFetch($result)) {
// Create notification
$notification = [
'user_id' => $row['follower_id'],
'type' => 'live_stream_started',
'title' => 'Live Stream Started',
'message' => "User is now live streaming: {$stream['title']}",
'data' => json_encode([
'stream_id' => $stream['id'],
'stream_key' => $stream['stream_key'],
'streamer' => $stream['user_id']
]),
'created_at' => date('Y-m-d H:i:s'),
'read_status' => 0
];
$this->db->doInsert('db_notifications', $notification);
}
}
private function getSRSViewerCount($streamKey)
{
try {
// Call SRS API to get viewer count
$url = "http://{$this->srsHost}:{$this->srsApiPort}/api/v1/streams/";
$response = file_get_contents($url);
if ($response) {
$data = json_decode($response, true);
// Parse SRS response to get viewer count for specific stream
// This is a simplified implementation
return $data['streams'][$streamKey]['clients'] ?? 0;
}
return 0;
} catch (Exception $e) {
return 0;
}
}
private function getChatMessageCount($streamId)
{
try {
$query = "SELECT COUNT(*) as count FROM db_stream_chat WHERE stream_id = ?";
$result = $this->db->doQuery($query, [$streamId]);
$row = $this->db->doFetch($result);
return (int)($row['count'] ?? 0);
} catch (Exception $e) {
return 0;
}
}
private function getStreamCount($filters)
{
try {
$whereClause = "WHERE 1=1";
$params = [];
if (isset($filters['status'])) {
$whereClause .= " AND status = ?";
$params[] = $filters['status'];
}
if (isset($filters['user_id'])) {
$whereClause .= " AND user_id = ?";
$params[] = $filters['user_id'];
}
$query = "SELECT COUNT(*) as count FROM db_live_streams {$whereClause}";
$result = $this->db->doQuery($query, $params);
$row = $this->db->doFetch($result);
return (int)($row['count'] ?? 0);
} catch (Exception $e) {
return 0;
}
}
private function formatStreamData($row)
{
return [
'id' => $row['id'],
'stream_key' => $row['stream_key'],
'title' => $row['title'],
'description' => $row['description'],
'category' => $row['category'],
'privacy' => $row['privacy'],
'status' => $row['status'],
'rtmp_url' => $row['rtmp_url'],
'hls_url' => $row['hls_url'],
'created_at' => $row['created_at'],
'started_at' => $row['started_at'],
'ended_at' => $row['ended_at'],
'user' => [
'id' => $row['user_id'],
'username' => $row['usr_user'],
'avatar' => $row['usr_avatar']
]
];
}
}