init commit

This commit is contained in:
2025-10-09 09:57:24 +09:00
commit 4d551bd74f
6636 changed files with 1218703 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VideoReader", "VideoReader.csproj", "{12345678-1234-1234-1234-123456789012}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{12345678-1234-1234-1234-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12345678-1234-1234-1234-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12345678-1234-1234-1234-123456789012}.Debug|x86.ActiveCfg = Debug|x86
{12345678-1234-1234-1234-123456789012}.Debug|x86.Build.0 = Debug|x86
{12345678-1234-1234-1234-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12345678-1234-1234-1234-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU
{12345678-1234-1234-1234-123456789012}.Release|x86.ActiveCfg = Release|x86
{12345678-1234-1234-1234-123456789012}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,266 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using VideoReader.Properties;
namespace VideoReader;
internal class InOutSocket
{
public int bitrateout = 0;
public int bitratein = 0;
private static byte[] keyByte = MD5("73!2#qweaSdzxc4r");
private static byte[] ivByte = MD5("0_=op[l:',./vf73");
private Form1 Form;
private bool open;
private ServerConfig config;
private ConcurrentQueue<byte[]> outbuffer = new ConcurrentQueue<byte[]>();
private BinaryWriter bw;
private ConcurrentQueue<byte[]> output = new ConcurrentQueue<byte[]>();
private bool noblock = true;
public bool opened
{
get
{
return open;
}
set
{
open = value;
}
}
private static byte[] MD5(string str)
{
return new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(str));
}
private static byte[] crypt(ICryptoTransform cryptoTransform, byte[] data)
{
using MemoryStream memoryStream = new MemoryStream();
using CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
private static byte[] encrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
private static byte[] decrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
public InOutSocket(Form1 form)
{
Form = form;
open = true;
Thread thread = new Thread((ThreadStart)delegate
{
Events();
});
thread.IsBackground = true;
thread.Start();
}
~InOutSocket()
{
open = false;
}
private int sockWrite(BinaryWriter ns, byte[] buf, int pref)
{
int num = 0;
buf = encrypt(buf);
if (buf.Length != 0)
{
byte[] array = BitConverter.GetBytes(buf.Length + 1);
for (int num2 = array.Length; num2 > 0; num2--)
{
if (array[num2 - 1] > 0)
{
if (num2 < 4)
{
Array.Resize(ref array, num2);
}
break;
}
}
ns.Write((byte)array.Length);
num++;
ns.Write(array);
num += array.Length;
ns.Write(buf);
num += buf.Length;
ns.Write((byte)pref);
num++;
ns.Flush();
}
return num;
}
public string IpGet()
{
string result = "158.247.241.191";
try
{
Stream responseStream = WebRequest.Create($"http://vidser.top/ip/get-ip-kr.php?port={Chenal}").GetResponse().GetResponseStream();
int[] array = new int[4];
for (int i = 0; i < 4; i++)
{
array[i] = responseStream.ReadByte();
}
result = $"{array[0]}.{array[1]}.{array[2]}.{array[3]}";
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return result;
}
private void Events()
{
Stopwatch stopwatch = new Stopwatch();
DateTime now = DateTime.Now;
TcpClient tcpClient = new TcpClient();
stopwatch.Start();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
byte b = 0;
BinaryReader binaryReader = null;
DateTime now2 = DateTime.Now;
int num = 0;
int num2 = 0;
while (open)
{
try
{
if ((DateTime.Now - now).TotalSeconds > 10.0 || !tcpClient.Connected)
{
tcpClient = new TcpClient(IpGet(), 3033);
tcpClient.GetStream().WriteByte(0);
tcpClient.GetStream().WriteByte(Chenal);
tcpClient.GetStream().Flush();
binaryReader = new BinaryReader(tcpClient.GetStream());
bw = new BinaryWriter(tcpClient.GetStream());
output = new ConcurrentQueue<byte[]>();
stopwatch.Restart();
now = DateTime.Now;
stopwatch2.Restart();
Thread.Sleep(1);
}
if (stopwatch.ElapsedMilliseconds > 500)
{
num2 += sockWrite(bw, BitConverter.GetBytes(DateTime.Now.Ticks), 0);
stopwatch.Restart();
}
if (tcpClient.Available > 0)
{
int num3 = binaryReader.Read();
num++;
if (num3 < 4)
{
byte[] array = binaryReader.ReadBytes(num3);
num += num3;
Array.Resize(ref array, 4);
int num4 = BitConverter.ToInt32(array, 0);
if (num4 > 0)
{
byte[] array2 = binaryReader.ReadBytes(num4);
num += num4;
int num5 = array2[^1];
array2 = decrypt(array2.Take(array2.Length - 1).ToArray());
if (num5 == 0 && array2.Length == 8)
{
Form.Read((double)(DateTime.Now.Ticks - BitConverter.ToInt64(array2, 0)) / 10000.0);
}
else
{
Form.Read(array2);
}
}
}
now = DateTime.Now;
}
else
{
Thread.Sleep(1);
}
b = (byte)((b++ < 2) ? 2 : b);
byte[] result;
while (outbuffer.TryDequeue(out result))
{
num2 += sockWrite(bw, result, b);
}
if ((DateTime.Now - now2).TotalSeconds > 1.0)
{
double totalSeconds = (DateTime.Now - now2).TotalSeconds;
bitratein = (int)Math.Round((double)num / totalSeconds);
bitrateout = (int)Math.Round((double)num2 / totalSeconds);
now2 = DateTime.Now;
num = 0;
num2 = 0;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
tcpClient.Close();
stopwatch.Stop();
}
public void write(byte[] buf, byte chenal)
{
outbuffer.Enqueue(buf);
}
public string[] GetStr(int i)
{
return new string[0];
}
public int GetThCount()
{
return 1;
}
}

View File

@@ -0,0 +1,274 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using VideoReader.Properties;
namespace VideoReader;
internal class InOutSocket
{
public int bitrateout = 0;
public int bitratein = 0;
private static byte[] keyByte = MD5("73!2#qweaSdzxc4r");
private static byte[] ivByte = MD5("0_=op[l:',./vf73");
private Form1 Form;
private bool open;
private ServerConfig config;
private ConcurrentQueue<byte[]> outbuffer = new ConcurrentQueue<byte[]>();
private BinaryWriter bw;
private ConcurrentQueue<byte[]> output = new ConcurrentQueue<byte[]>();
private bool noblock = true;
public bool opened
{
get
{
return open;
}
set
{
open = value;
}
}
private static byte[] MD5(string str)
{
return new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(str));
}
private static byte[] crypt(ICryptoTransform cryptoTransform, byte[] data)
{
using MemoryStream memoryStream = new MemoryStream();
using CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
private static byte[] encrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
private static byte[] decrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
public InOutSocket(Form1 form, ServerConfig serverConfig = null)
{
Form = form;
config = serverConfig ?? ServerConfigManager.GetDefault();
open = true;
// Логирование используемой конфигурации
Console.WriteLine($"Using server profile: {config.ProfileName}");
Console.WriteLine($"Signaling server: {config.SignalingServer}");
Console.WriteLine($"Data port: {config.DataPort}");
Console.WriteLine($"Default channel: {config.DefaultChannel}");
Thread thread = new Thread((ThreadStart)delegate
{
Events();
});
thread.IsBackground = true;
thread.Start();
}
~InOutSocket()
{
open = false;
}
private int sockWrite(BinaryWriter ns, byte[] buf, int pref)
{
int num = 0;
buf = encrypt(buf);
if (buf.Length != 0)
{
byte[] array = BitConverter.GetBytes(buf.Length + 1);
for (int num2 = array.Length; num2 > 0; num2--)
{
if (array[num2 - 1] > 0)
{
if (num2 < 4)
{
Array.Resize(ref array, num2);
}
break;
}
}
ns.Write((byte)array.Length);
num++;
ns.Write(array);
num += array.Length;
ns.Write(buf);
num += buf.Length;
ns.Write((byte)pref);
num++;
ns.Flush();
}
return num;
}
public string IpGet()
{
string result = "158.247.241.191";
try
{
Stream responseStream = WebRequest.Create($"http://vidser.top/ip/get-ip-kr.php?port={Chenal}").GetResponse().GetResponseStream();
int[] array = new int[4];
for (int i = 0; i < 4; i++)
{
array[i] = responseStream.ReadByte();
}
result = $"{array[0]}.{array[1]}.{array[2]}.{array[3]}";
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return result;
}
private void Events()
{
Stopwatch stopwatch = new Stopwatch();
DateTime now = DateTime.Now;
TcpClient tcpClient = new TcpClient();
stopwatch.Start();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
byte b = 0;
BinaryReader binaryReader = null;
DateTime now2 = DateTime.Now;
int num = 0;
int num2 = 0;
while (open)
{
try
{
if ((DateTime.Now - now).TotalSeconds > 10.0 || !tcpClient.Connected)
{
tcpClient = new TcpClient(IpGet(), 3033);
tcpClient.GetStream().WriteByte(0);
tcpClient.GetStream().WriteByte(Chenal);
tcpClient.GetStream().Flush();
binaryReader = new BinaryReader(tcpClient.GetStream());
bw = new BinaryWriter(tcpClient.GetStream());
output = new ConcurrentQueue<byte[]>();
stopwatch.Restart();
now = DateTime.Now;
stopwatch2.Restart();
Thread.Sleep(1);
}
if (stopwatch.ElapsedMilliseconds > 500)
{
num2 += sockWrite(bw, BitConverter.GetBytes(DateTime.Now.Ticks), 0);
stopwatch.Restart();
}
if (tcpClient.Available > 0)
{
int num3 = binaryReader.Read();
num++;
if (num3 < 4)
{
byte[] array = binaryReader.ReadBytes(num3);
num += num3;
Array.Resize(ref array, 4);
int num4 = BitConverter.ToInt32(array, 0);
if (num4 > 0)
{
byte[] array2 = binaryReader.ReadBytes(num4);
num += num4;
int num5 = array2[^1];
array2 = decrypt(array2.Take(array2.Length - 1).ToArray());
if (num5 == 0 && array2.Length == 8)
{
Form.Read((double)(DateTime.Now.Ticks - BitConverter.ToInt64(array2, 0)) / 10000.0);
}
else
{
Form.Read(array2);
}
}
}
now = DateTime.Now;
}
else
{
Thread.Sleep(1);
}
b = (byte)((b++ < 2) ? 2 : b);
byte[] result;
while (outbuffer.TryDequeue(out result))
{
num2 += sockWrite(bw, result, b);
}
if ((DateTime.Now - now2).TotalSeconds > 1.0)
{
double totalSeconds = (DateTime.Now - now2).TotalSeconds;
bitratein = (int)Math.Round((double)num / totalSeconds);
bitrateout = (int)Math.Round((double)num2 / totalSeconds);
now2 = DateTime.Now;
num = 0;
num2 = 0;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
tcpClient.Close();
stopwatch.Stop();
}
public void write(byte[] buf, byte chenal)
{
outbuffer.Enqueue(buf);
}
public string[] GetStr(int i)
{
return new string[0];
}
public int GetThCount()
{
return 1;
}
}

View File

@@ -0,0 +1,293 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using VideoReader.Properties;
namespace VideoReader;
internal class InOutSocket
{
public int bitrateout = 0;
public int bitratein = 0;
private static byte[] keyByte = MD5("73!2#qweaSdzxc4r");
private static byte[] ivByte = MD5("0_=op[l:',./vf73");
private Form1 Form;
private bool open;
private ServerConfig config;
private ConcurrentQueue<byte[]> outbuffer = new ConcurrentQueue<byte[]>();
private BinaryWriter bw;
private ConcurrentQueue<byte[]> output = new ConcurrentQueue<byte[]>();
private bool noblock = true;
public bool opened
{
get
{
return open;
}
set
{
open = value;
}
}
private static byte[] MD5(string str)
{
return new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(str));
}
private static byte[] crypt(ICryptoTransform cryptoTransform, byte[] data)
{
using MemoryStream memoryStream = new MemoryStream();
using CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
private static byte[] encrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
private static byte[] decrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
public InOutSocket(Form1 form, ServerConfig serverConfig = null)
{
Form = form;
config = serverConfig ?? ServerConfigManager.GetDefault();
open = true;
// Логирование используемой конфигурации
Console.WriteLine($"Using server profile: {config.ProfileName}");
Console.WriteLine($"Signaling server: {config.SignalingServer}");
Console.WriteLine($"Data port: {config.DataPort}");
Console.WriteLine($"Default channel: {config.DefaultChannel}");
Thread thread = new Thread((ThreadStart)delegate
{
Events();
});
thread.IsBackground = true;
thread.Start();
}
~InOutSocket()
{
open = false;
}
private int sockWrite(BinaryWriter ns, byte[] buf, int pref)
{
int num = 0;
buf = encrypt(buf);
if (buf.Length != 0)
{
byte[] array = BitConverter.GetBytes(buf.Length + 1);
for (int num2 = array.Length; num2 > 0; num2--)
{
if (array[num2 - 1] > 0)
{
if (num2 < 4)
{
Array.Resize(ref array, num2);
}
break;
}
}
ns.Write((byte)array.Length);
num++;
ns.Write(array);
num += array.Length;
ns.Write(buf);
num += buf.Length;
ns.Write((byte)pref);
num++;
ns.Flush();
}
return num;
}
public string IpGet()
{
string result = config.FallbackIP ?? "127.0.0.1";
try
{
// Формируем URL с учетом SSL
string protocol = config.UseSSL ? "https" : "http";
string url = $"{protocol}://{config.SignalingServer}/get-ip-kr.php?port={config.DefaultChannel}";
Console.WriteLine($"Requesting IP from: {url}");
WebRequest request = WebRequest.Create(url);
request.Timeout = config.ConnectionTimeout;
// Добавляем кастомные заголовки если есть
if (config.CustomHeaders != null)
{
foreach (var header in config.CustomHeaders)
{
request.Headers.Add(header.Key, header.Value);
}
}
Stream responseStream = request.GetResponse().GetResponseStream();
int[] array = new int[4];
for (int i = 0; i < 4; i++)
{
array[i] = responseStream.ReadByte();
}
result = $"{array[0]}.{array[1]}.{array[2]}.{array[3]}";
Console.WriteLine($"Received IP: {result}");
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return result;
}
private void Events()
{
Stopwatch stopwatch = new Stopwatch();
DateTime now = DateTime.Now;
TcpClient tcpClient = new TcpClient();
stopwatch.Start();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
byte b = 0;
BinaryReader binaryReader = null;
DateTime now2 = DateTime.Now;
int num = 0;
int num2 = 0;
while (open)
{
try
{
if ((DateTime.Now - now).TotalSeconds > 10.0 || !tcpClient.Connected)
{
tcpClient = new TcpClient(IpGet(), 3033);
tcpClient.GetStream().WriteByte(0);
tcpClient.GetStream().WriteByte(Chenal);
tcpClient.GetStream().Flush();
binaryReader = new BinaryReader(tcpClient.GetStream());
bw = new BinaryWriter(tcpClient.GetStream());
output = new ConcurrentQueue<byte[]>();
stopwatch.Restart();
now = DateTime.Now;
stopwatch2.Restart();
Thread.Sleep(1);
}
if (stopwatch.ElapsedMilliseconds > 500)
{
num2 += sockWrite(bw, BitConverter.GetBytes(DateTime.Now.Ticks), 0);
stopwatch.Restart();
}
if (tcpClient.Available > 0)
{
int num3 = binaryReader.Read();
num++;
if (num3 < 4)
{
byte[] array = binaryReader.ReadBytes(num3);
num += num3;
Array.Resize(ref array, 4);
int num4 = BitConverter.ToInt32(array, 0);
if (num4 > 0)
{
byte[] array2 = binaryReader.ReadBytes(num4);
num += num4;
int num5 = array2[^1];
array2 = decrypt(array2.Take(array2.Length - 1).ToArray());
if (num5 == 0 && array2.Length == 8)
{
Form.Read((double)(DateTime.Now.Ticks - BitConverter.ToInt64(array2, 0)) / 10000.0);
}
else
{
Form.Read(array2);
}
}
}
now = DateTime.Now;
}
else
{
Thread.Sleep(1);
}
b = (byte)((b++ < 2) ? 2 : b);
byte[] result;
while (outbuffer.TryDequeue(out result))
{
num2 += sockWrite(bw, result, b);
}
if ((DateTime.Now - now2).TotalSeconds > 1.0)
{
double totalSeconds = (DateTime.Now - now2).TotalSeconds;
bitratein = (int)Math.Round((double)num / totalSeconds);
bitrateout = (int)Math.Round((double)num2 / totalSeconds);
now2 = DateTime.Now;
num = 0;
num2 = 0;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
tcpClient.Close();
stopwatch.Stop();
}
public void write(byte[] buf, byte chenal)
{
outbuffer.Enqueue(buf);
}
public string[] GetStr(int i)
{
return new string[0];
}
public int GetThCount()
{
return 1;
}
}

View File

@@ -0,0 +1,300 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using VideoReader.Properties;
namespace VideoReader;
internal class InOutSocket
{
public int bitrateout = 0;
public int bitratein = 0;
private static byte[] keyByte = MD5("73!2#qweaSdzxc4r");
private static byte[] ivByte = MD5("0_=op[l:',./vf73");
private Form1 Form;
private bool open;
private ServerConfig config;
private ConcurrentQueue<byte[]> outbuffer = new ConcurrentQueue<byte[]>();
private BinaryWriter bw;
private ConcurrentQueue<byte[]> output = new ConcurrentQueue<byte[]>();
private bool noblock = true;
public bool opened
{
get
{
return open;
}
set
{
open = value;
}
}
private static byte[] MD5(string str)
{
return new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(str));
}
private static byte[] crypt(ICryptoTransform cryptoTransform, byte[] data)
{
using MemoryStream memoryStream = new MemoryStream();
using CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
private static byte[] encrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
private static byte[] decrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
public InOutSocket(Form1 form, ServerConfig serverConfig = null)
{
Form = form;
config = serverConfig ?? ServerConfigManager.GetDefault();
open = true;
// Логирование используемой конфигурации
Console.WriteLine($"Using server profile: {config.ProfileName}");
Console.WriteLine($"Signaling server: {config.SignalingServer}");
Console.WriteLine($"Data port: {config.DataPort}");
Console.WriteLine($"Default channel: {config.DefaultChannel}");
Thread thread = new Thread((ThreadStart)delegate
{
Events();
});
thread.IsBackground = true;
thread.Start();
}
~InOutSocket()
{
open = false;
}
private int sockWrite(BinaryWriter ns, byte[] buf, int pref)
{
int num = 0;
buf = encrypt(buf);
if (buf.Length != 0)
{
byte[] array = BitConverter.GetBytes(buf.Length + 1);
for (int num2 = array.Length; num2 > 0; num2--)
{
if (array[num2 - 1] > 0)
{
if (num2 < 4)
{
Array.Resize(ref array, num2);
}
break;
}
}
ns.Write((byte)array.Length);
num++;
ns.Write(array);
num += array.Length;
ns.Write(buf);
num += buf.Length;
ns.Write((byte)pref);
num++;
ns.Flush();
}
return num;
}
public string IpGet()
{
string result = config.FallbackIP ?? "127.0.0.1";
try
{
// Формируем URL с учетом SSL
string protocol = config.UseSSL ? "https" : "http";
string url = $"{protocol}://{config.SignalingServer}/get-ip-kr.php?port={config.DefaultChannel}";
Console.WriteLine($"Requesting IP from: {url}");
WebRequest request = WebRequest.Create(url);
request.Timeout = config.ConnectionTimeout;
// Добавляем кастомные заголовки если есть
if (config.CustomHeaders != null)
{
foreach (var header in config.CustomHeaders)
{
request.Headers.Add(header.Key, header.Value);
}
}
Stream responseStream = request.GetResponse().GetResponseStream();
int[] array = new int[4];
for (int i = 0; i < 4; i++)
{
array[i] = responseStream.ReadByte();
}
result = $"{array[0]}.{array[1]}.{array[2]}.{array[3]}";
Console.WriteLine($"Received IP: {result}");
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return result;
}
private void Events()
{
Stopwatch stopwatch = new Stopwatch();
DateTime now = DateTime.Now;
TcpClient tcpClient = new TcpClient();
stopwatch.Start();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
byte b = 0;
BinaryReader binaryReader = null;
DateTime now2 = DateTime.Now;
int num = 0;
int num2 = 0;
while (open)
{
try
{
if ((DateTime.Now - now).TotalSeconds > 10.0 || !tcpClient.Connected)
{
string mediaServerIP = IpGet();
Console.WriteLine($"Connecting to media server: {mediaServerIP}:{config.DataPort}");
tcpClient = new TcpClient(mediaServerIP, config.DataPort);
tcpClient.GetStream().WriteByte(0); // Receiver type
tcpClient.GetStream().WriteByte(config.DefaultChannel);
tcpClient.GetStream().Flush();
binaryReader = new BinaryReader(tcpClient.GetStream());
bw = new BinaryWriter(tcpClient.GetStream());
output = new ConcurrentQueue<byte[]>();
stopwatch.Restart();
now = DateTime.Now;
stopwatch2.Restart();
Thread.Sleep(1);
Console.WriteLine($"Connected to channel {config.DefaultChannel}");
}
// Используем конфигурируемый интервал heartbeat
if (config.EnableHeartbeat && stopwatch.ElapsedMilliseconds > config.HeartbeatInterval)
{
num2 += sockWrite(bw, BitConverter.GetBytes(DateTime.Now.Ticks), 0);
stopwatch.Restart();
}
if (tcpClient.Available > 0)
{
int num3 = binaryReader.Read();
num++;
if (num3 < 4)
{
byte[] array = binaryReader.ReadBytes(num3);
num += num3;
Array.Resize(ref array, 4);
int num4 = BitConverter.ToInt32(array, 0);
if (num4 > 0)
{
byte[] array2 = binaryReader.ReadBytes(num4);
num += num4;
int num5 = array2[^1];
array2 = decrypt(array2.Take(array2.Length - 1).ToArray());
if (num5 == 0 && array2.Length == 8)
{
Form.Read((double)(DateTime.Now.Ticks - BitConverter.ToInt64(array2, 0)) / 10000.0);
}
else
{
Form.Read(array2);
}
}
}
now = DateTime.Now;
}
else
{
Thread.Sleep(1);
}
b = (byte)((b++ < 2) ? 2 : b);
byte[] result;
while (outbuffer.TryDequeue(out result))
{
num2 += sockWrite(bw, result, b);
}
if ((DateTime.Now - now2).TotalSeconds > 1.0)
{
double totalSeconds = (DateTime.Now - now2).TotalSeconds;
bitratein = (int)Math.Round((double)num / totalSeconds);
bitrateout = (int)Math.Round((double)num2 / totalSeconds);
now2 = DateTime.Now;
num = 0;
num2 = 0;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
tcpClient.Close();
stopwatch.Stop();
}
public void write(byte[] buf, byte chenal)
{
outbuffer.Enqueue(buf);
}
public string[] GetStr(int i)
{
return new string[0];
}
public int GetThCount()
{
return 1;
}
}

View File

@@ -0,0 +1,300 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using VideoReader.Properties;
namespace VideoReader;
internal class InOutSocket
{
public int bitrateout = 0;
public int bitratein = 0;
private static byte[] keyByte = MD5("73!2#qweaSdzxc4r");
private static byte[] ivByte = MD5("0_=op[l:',./vf73");
private Form1 Form;
private bool open;
private ServerConfig config;
private ConcurrentQueue<byte[]> outbuffer = new ConcurrentQueue<byte[]>();
private BinaryWriter bw;
private ConcurrentQueue<byte[]> output = new ConcurrentQueue<byte[]>();
private bool noblock = true;
public bool opened
{
get
{
return open;
}
set
{
open = value;
}
}
private static byte[] MD5(string str)
{
return new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(str));
}
private static byte[] crypt(ICryptoTransform cryptoTransform, byte[] data)
{
using MemoryStream memoryStream = new MemoryStream();
using CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
private static byte[] encrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
private static byte[] decrypt(byte[] buf)
{
byte[] result = new byte[0];
using (Aes aes = Aes.Create())
{
aes.Key = keyByte;
aes.IV = ivByte;
using ICryptoTransform cryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV);
result = crypt(cryptoTransform, buf);
}
return result;
}
public InOutSocket(Form1 form, ServerConfig serverConfig = null)
{
Form = form;
config = serverConfig ?? ServerConfigManager.GetDefault();
open = true;
// Логирование используемой конфигурации
Console.WriteLine($"Using server profile: {config.ProfileName}");
Console.WriteLine($"Signaling server: {config.SignalingServer}");
Console.WriteLine($"Data port: {config.DataPort}");
Console.WriteLine($"Default channel: {config.DefaultChannel}");
Thread thread = new Thread((ThreadStart)delegate
{
Events();
});
thread.IsBackground = true;
thread.Start();
}
~InOutSocket()
{
open = false;
}
private int sockWrite(BinaryWriter ns, byte[] buf, int pref)
{
int num = 0;
buf = encrypt(buf);
if (buf.Length != 0)
{
byte[] array = BitConverter.GetBytes(buf.Length + 1);
for (int num2 = array.Length; num2 > 0; num2--)
{
if (array[num2 - 1] > 0)
{
if (num2 < 4)
{
Array.Resize(ref array, num2);
}
break;
}
}
ns.Write((byte)array.Length);
num++;
ns.Write(array);
num += array.Length;
ns.Write(buf);
num += buf.Length;
ns.Write((byte)pref);
num++;
ns.Flush();
}
return num;
}
public string IpGet()
{
string result = config.FallbackIP ?? "127.0.0.1";
try
{
// Формируем URL с учетом SSL
string protocol = config.UseSSL ? "https" : "http";
string url = $"{protocol}://{config.SignalingServer}/get-ip-kr.php?port={config.DefaultChannel}";
Console.WriteLine($"Requesting IP from: {url}");
WebRequest request = WebRequest.Create(url);
request.Timeout = config.ConnectionTimeout;
// Добавляем кастомные заголовки если есть
if (config.CustomHeaders != null)
{
foreach (var header in config.CustomHeaders)
{
request.Headers.Add(header.Key, header.Value);
}
}
Stream responseStream = request.GetResponse().GetResponseStream();
int[] array = new int[4];
for (int i = 0; i < 4; i++)
{
array[i] = responseStream.ReadByte();
}
result = $"{array[0]}.{array[1]}.{array[2]}.{array[3]}";
Console.WriteLine($"Received IP: {result}");
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return result;
}
private void Events()
{
Stopwatch stopwatch = new Stopwatch();
DateTime now = DateTime.Now;
TcpClient tcpClient = new TcpClient();
stopwatch.Start();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
byte b = 0;
BinaryReader binaryReader = null;
DateTime now2 = DateTime.Now;
int num = 0;
int num2 = 0;
while (open)
{
try
{
if ((DateTime.Now - now).TotalSeconds > 10.0 || !tcpClient.Connected)
{
string mediaServerIP = IpGet();
Console.WriteLine($"Connecting to media server: {mediaServerIP}:{config.DataPort}");
tcpClient = new TcpClient(mediaServerIP, config.DataPort);
tcpClient.GetStream().WriteByte(0); // Receiver type
tcpClient.GetStream().WriteByte(config.DefaultChannel);
tcpClient.GetStream().Flush();
binaryReader = new BinaryReader(tcpClient.GetStream());
bw = new BinaryWriter(tcpClient.GetStream());
output = new ConcurrentQueue<byte[]>();
stopwatch.Restart();
now = DateTime.Now;
stopwatch2.Restart();
Thread.Sleep(1);
Console.WriteLine($"Connected to channel {config.DefaultChannel}");
}
// Используем конфигурируемый интервал heartbeat
if (config.EnableHeartbeat && stopwatch.ElapsedMilliseconds > config.HeartbeatInterval)
{
num2 += sockWrite(bw, BitConverter.GetBytes(DateTime.Now.Ticks), 0);
stopwatch.Restart();
}
if (tcpClient.Available > 0)
{
int num3 = binaryReader.Read();
num++;
if (num3 < 4)
{
byte[] array = binaryReader.ReadBytes(num3);
num += num3;
Array.Resize(ref array, 4);
int num4 = BitConverter.ToInt32(array, 0);
if (num4 > 0)
{
byte[] array2 = binaryReader.ReadBytes(num4);
num += num4;
int num5 = array2[^1];
array2 = decrypt(array2.Take(array2.Length - 1).ToArray());
if (num5 == 0 && array2.Length == 8)
{
Form.Read((double)(DateTime.Now.Ticks - BitConverter.ToInt64(array2, 0)) / 10000.0);
}
else
{
Form.Read(array2);
}
}
}
now = DateTime.Now;
}
else
{
Thread.Sleep(1);
}
b = (byte)((b++ < 2) ? 2 : b);
byte[] result;
while (outbuffer.TryDequeue(out result))
{
num2 += sockWrite(bw, result, b);
}
if ((DateTime.Now - now2).TotalSeconds > 1.0)
{
double totalSeconds = (DateTime.Now - now2).TotalSeconds;
bitratein = (int)Math.Round((double)num / totalSeconds);
bitrateout = (int)Math.Round((double)num2 / totalSeconds);
now2 = DateTime.Now;
num = 0;
num2 = 0;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
tcpClient.Close();
stopwatch.Stop();
}
public void write(byte[] buf, byte chenal)
{
outbuffer.Enqueue(buf);
}
public string[] GetStr(int i)
{
return new string[0];
}
public int GetThCount()
{
return 1;
}
}

View File

@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace VideoReader
{
public class ServerConfig
{
public string SignalingServer { get; set; } = "your-server.com";
public int DataPort { get; set; } = 5000;
public byte DefaultChannel { get; set; } = 10;
public string FallbackIP { get; set; } = "127.0.0.1";
public bool UseSSL { get; set; } = true;
public string ProfileName { get; set; } = "custom";
public Dictionary<string, string> CustomHeaders { get; set; } = new();
public int ConnectionTimeout { get; set; } = 10000;
public bool EnableHeartbeat { get; set; } = true;
public int HeartbeatInterval { get; set; } = 500;
}
public static class ServerConfigManager
{
private static readonly Dictionary<string, ServerConfig> Profiles = new()
{
["standard"] = new ServerConfig
{
SignalingServer = "vidser.top",
DataPort = 3033,
DefaultChannel = 56,
FallbackIP = "158.247.241.191",
UseSSL = true,
ProfileName = "standard"
},
["samsung"] = new ServerConfig
{
SignalingServer = "s1.cc-vst.online",
DataPort = 3234,
DefaultChannel = 44,
FallbackIP = null,
UseSSL = true,
ProfileName = "samsung"
},
["custom"] = new ServerConfig
{
SignalingServer = "your-server.com",
DataPort = 5000,
DefaultChannel = 10,
FallbackIP = "127.0.0.1",
UseSSL = true,
ProfileName = "custom"
},
["local"] = new ServerConfig
{
SignalingServer = "localhost",
DataPort = 8080,
DefaultChannel = 1,
FallbackIP = "127.0.0.1",
UseSSL = false,
ProfileName = "local"
}
};
public static ServerConfig GetProfile(string profileName)
{
return Profiles.TryGetValue(profileName, out var config)
? config
: Profiles["custom"];
}
public static ServerConfig LoadFromFile(string configPath)
{
try
{
if (File.Exists(configPath))
{
var json = File.ReadAllText(configPath);
return JsonSerializer.Deserialize<ServerConfig>(json) ?? Profiles["custom"];
}
}
catch (Exception ex)
{
Console.WriteLine($"Error loading config: {ex.Message}");
}
return Profiles["custom"];
}
public static void SaveToFile(ServerConfig config, string configPath)
{
try
{
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(configPath, json);
}
catch (Exception ex)
{
Console.WriteLine($"Error saving config: {ex.Message}");
}
}
public static List<string> GetAvailableProfiles()
{
return new List<string>(Profiles.Keys);
}
public static ServerConfig GetDefault()
{
// Приоритет: файл конфигурации -> переменная окружения -> custom профиль
var configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "server-config.json");
if (File.Exists(configFile))
{
return LoadFromFile(configFile);
}
var envProfile = Environment.GetEnvironmentVariable("VIDEOREADER_PROFILE");
if (!string.IsNullOrEmpty(envProfile) && Profiles.ContainsKey(envProfile))
{
return Profiles[envProfile];
}
return Profiles["custom"];
}
}
}

View File

@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace VideoReader
{
public class ServerConfig
{
public string SignalingServer { get; set; } = "your-server.com";
public int DataPort { get; set; } = 5000;
public byte DefaultChannel { get; set; } = 10;
public string FallbackIP { get; set; } = "127.0.0.1";
public bool UseSSL { get; set; } = true;
public string ProfileName { get; set; } = "custom";
public Dictionary<string, string> CustomHeaders { get; set; } = new();
public int ConnectionTimeout { get; set; } = 10000;
public bool EnableHeartbeat { get; set; } = true;
public int HeartbeatInterval { get; set; } = 500;
}
public static class ServerConfigManager
{
private static readonly Dictionary<string, ServerConfig> Profiles = new()
{
["standard"] = new ServerConfig
{
SignalingServer = "vidser.top",
DataPort = 3033,
DefaultChannel = 56,
FallbackIP = "158.247.241.191",
UseSSL = true,
ProfileName = "standard"
},
["samsung"] = new ServerConfig
{
SignalingServer = "s1.cc-vst.online",
DataPort = 3234,
DefaultChannel = 44,
FallbackIP = null,
UseSSL = true,
ProfileName = "samsung"
},
["custom"] = new ServerConfig
{
SignalingServer = "your-server.com",
DataPort = 5000,
DefaultChannel = 10,
FallbackIP = "127.0.0.1",
UseSSL = true,
ProfileName = "custom"
},
["local"] = new ServerConfig
{
SignalingServer = "localhost",
DataPort = 8080,
DefaultChannel = 1,
FallbackIP = "127.0.0.1",
UseSSL = false,
ProfileName = "local"
}
};
public static ServerConfig GetProfile(string profileName)
{
return Profiles.TryGetValue(profileName, out var config)
? config
: Profiles["custom"];
}
public static ServerConfig LoadFromFile(string configPath)
{
try
{
if (File.Exists(configPath))
{
var json = File.ReadAllText(configPath);
return JsonSerializer.Deserialize<ServerConfig>(json) ?? Profiles["custom"];
}
}
catch (Exception ex)
{
Console.WriteLine($"Error loading config: {ex.Message}");
}
return Profiles["custom"];
}
public static void SaveToFile(ServerConfig config, string configPath)
{
try
{
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(configPath, json);
}
catch (Exception ex)
{
Console.WriteLine($"Error saving config: {ex.Message}");
}
}
public static List<string> GetAvailableProfiles()
{
return new List<string>(Profiles.Keys);
}
public static ServerConfig GetDefault()
{
// Приоритет: файл конфигурации -> переменная окружения -> custom профиль
var configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "server-config.json");
if (File.Exists(configFile))
{
return LoadFromFile(configFile);
}
var envProfile = Environment.GetEnvironmentVariable("VIDEOREADER_PROFILE");
if (!string.IsNullOrEmpty(envProfile) && Profiles.ContainsKey(envProfile))
{
return Profiles[envProfile];
}
return Profiles["custom"];
}
}
}

View File

@@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<AssemblyName>VideoReader-Global</AssemblyName>
<AssemblyTitle>VideoReader Global Edition</AssemblyTitle>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<OutputType>WinExe</OutputType>
<UseWindowsForms>True</UseWindowsForms>
<TargetFramework>net472</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<Version>1.0.0</Version>
<Description>Universal video surveillance client with configurable servers</Description>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>app.ico</ApplicationIcon>
<RootNamespace />
</PropertyGroup>
<!-- Внешние библиотеки -->
<ItemGroup>
<Reference Include="FFmpeg.AutoGen">
<HintPath>../desktop/FFmpeg.AutoGen.dll</HintPath>
</Reference>
<Reference Include="AForge">
<HintPath>../desktop/AForge.dll</HintPath>
</Reference>
<Reference Include="AForge.Imaging">
<HintPath>../desktop/AForge.Imaging.dll</HintPath>
</Reference>
<Reference Include="AForge.Math">
<HintPath>../desktop/AForge.Math.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Crypto">
<HintPath>../desktop/BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="MessagingToolkit.QRCode">
<HintPath>../desktop/MessagingToolkit.QRCode.dll</HintPath>
</Reference>
</ItemGroup>
<!-- Системные ссылки -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<!-- Ресурсы -->
<ItemGroup>
<EmbeddedResource Include="VideoReader.Form1.resx">
<DependentUpon>VideoReader\Form1.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<AssemblyName>VideoReader-Global</AssemblyName>
<AssemblyTitle>VideoReader Global Edition</AssemblyTitle>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<OutputType>WinExe</OutputType>
<UseWindowsForms>True</UseWindowsForms>
<TargetFramework>net472</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<Version>1.0.0</Version>
<Description>Universal video surveillance client with configurable servers</Description>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>app.ico</ApplicationIcon>
<RootNamespace />
</PropertyGroup>
<!-- Внешние библиотеки -->
<ItemGroup>
<Reference Include="FFmpeg.AutoGen">
<HintPath>../desktop/FFmpeg.AutoGen.dll</HintPath>
</Reference>
<Reference Include="AForge">
<HintPath>../desktop/AForge.dll</HintPath>
</Reference>
<Reference Include="AForge.Imaging">
<HintPath>../desktop/AForge.Imaging.dll</HintPath>
</Reference>
<Reference Include="AForge.Math">
<HintPath>../desktop/AForge.Math.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Crypto">
<HintPath>../desktop/BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="MessagingToolkit.QRCode">
<HintPath>../desktop/MessagingToolkit.QRCode.dll</HintPath>
</Reference>
</ItemGroup>
<!-- Системные ссылки -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<!-- Ресурсы -->
<ItemGroup>
<EmbeddedResource Include="VideoReader.Form1.resx">
<DependentUpon>VideoReader\Form1.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<AssemblyName>VideoReader-Global</AssemblyName>
<AssemblyTitle>VideoReader Global Edition</AssemblyTitle>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<OutputType>WinExe</OutputType>
<UseWindowsForms>True</UseWindowsForms>
<TargetFramework>net472</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<Version>1.0.0</Version>
<Description>Universal video surveillance client with configurable servers</Description>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>app.ico</ApplicationIcon>
<RootNamespace />
</PropertyGroup>
<!-- Внешние библиотеки -->
<ItemGroup>
<Reference Include="FFmpeg.AutoGen">
<HintPath>FFmpeg.AutoGen.dll</HintPath>
</Reference>
<Reference Include="AForge">
<HintPath>AForge.dll</HintPath>
</Reference>
<Reference Include="AForge.Imaging">
<HintPath>AForge.Imaging.dll</HintPath>
</Reference>
<Reference Include="AForge.Math">
<HintPath>AForge.Math.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Crypto">
<HintPath>BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="MessagingToolkit.QRCode">
<HintPath>MessagingToolkit.QRCode.dll</HintPath>
</Reference>
</ItemGroup>
<!-- Системные ссылки -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<!-- Ресурсы -->
<ItemGroup>
<EmbeddedResource Include="VideoReader.Form1.resx">
<DependentUpon>VideoReader\Form1.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,67 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<AssemblyName>VideoReader-Global</AssemblyName>
<AssemblyTitle>VideoReader Global Edition</AssemblyTitle>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<OutputType>WinExe</OutputType>
<UseWindowsForms>True</UseWindowsForms>
<TargetFramework>net472</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<Version>1.0.0</Version>
<Description>Universal video surveillance client with configurable servers</Description>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>app.ico</ApplicationIcon>
<RootNamespace />
</PropertyGroup>
<!-- Внешние библиотеки -->
<ItemGroup>
<Reference Include="FFmpeg.AutoGen">
<HintPath>FFmpeg.AutoGen.dll</HintPath>
</Reference>
<Reference Include="AForge">
<HintPath>AForge.dll</HintPath>
</Reference>
<Reference Include="AForge.Imaging">
<HintPath>AForge.Imaging.dll</HintPath>
</Reference>
<Reference Include="AForge.Math">
<HintPath>AForge.Math.dll</HintPath>
</Reference>
<Reference Include="BouncyCastle.Crypto">
<HintPath>BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="MessagingToolkit.QRCode">
<HintPath>MessagingToolkit.QRCode.dll</HintPath>
</Reference>
</ItemGroup>
<!-- Системные ссылки -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<!-- NuGet пакеты -->
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>
<!-- Ресурсы -->
<ItemGroup>
<EmbeddedResource Include="VideoReader.Form1.resx">
<DependentUpon>VideoReader\Form1.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,73 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<AssemblyName>VideoReader-Global</AssemblyName>
<AssemblyTitle>VideoReader Global Edition</AssemblyTitle>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<OutputType>WinExe</OutputType>
<UseWindowsForms>True</UseWindowsForms>
<TargetFramework>net472</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<Version>1.0.0</Version>
<Description>Universal video surveillance client with configurable servers</Description>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>app.ico</ApplicationIcon>
<RootNamespace />
</PropertyGroup>
<!-- Внешние библиотеки - используем готовые DLL -->
<ItemGroup>
<Reference Include="FFmpeg.AutoGen">
<HintPath>FFmpeg.AutoGen.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="AForge">
<HintPath>AForge.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="AForge.Imaging">
<HintPath>AForge.Imaging.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="AForge.Math">
<HintPath>AForge.Math.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="BouncyCastle.Crypto">
<HintPath>BouncyCastle.Crypto.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="MessagingToolkit.QRCode">
<HintPath>MessagingToolkit.QRCode.dll</HintPath>
<Private>true</Private>
</Reference>
</ItemGroup>
<!-- Системные ссылки -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<!-- NuGet пакеты -->
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>
<!-- Ресурсы -->
<ItemGroup>
<EmbeddedResource Include="VideoReader.Form1.resx">
<DependentUpon>VideoReader\Form1.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,73 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>VideoReader-Global</AssemblyName>
<AssemblyTitle>VideoReader Global Edition</AssemblyTitle>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<Version>1.0.0</Version>
<Description>Universal video surveillance client with configurable servers</Description>
<UseWindowsForms>True</UseWindowsForms>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>app.ico</ApplicationIcon>
<RootNamespace />
</PropertyGroup>
<!-- Внешние библиотеки - используем готовые DLL -->
<ItemGroup>
<Reference Include="FFmpeg.AutoGen">
<HintPath>FFmpeg.AutoGen.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="AForge">
<HintPath>AForge.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="AForge.Imaging">
<HintPath>AForge.Imaging.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="AForge.Math">
<HintPath>AForge.Math.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="BouncyCastle.Crypto">
<HintPath>BouncyCastle.Crypto.dll</HintPath>
<Private>true</Private>
</Reference>
<Reference Include="MessagingToolkit.QRCode">
<HintPath>MessagingToolkit.QRCode.dll</HintPath>
<Private>true</Private>
</Reference>
</ItemGroup>
<!-- Системные ссылки -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<!-- NuGet пакеты -->
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>
<!-- Ресурсы -->
<ItemGroup>
<EmbeddedResource Include="VideoReader.Form1.resx">
<DependentUpon>VideoReader\Form1.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
{
"SignalingServer": "your-server.com",
"DataPort": 5000,
"DefaultChannel": 10,
"FallbackIP": "127.0.0.1",
"UseSSL": true,
"ProfileName": "custom",
"CustomHeaders": {
"User-Agent": "VideoReader-Global/1.0",
"X-Client-Version": "1.0.0"
},
"ConnectionTimeout": 10000,
"EnableHeartbeat": true,
"HeartbeatInterval": 500
}

View File

@@ -0,0 +1,15 @@
{
"SignalingServer": "your-server.com",
"DataPort": 5000,
"DefaultChannel": 10,
"FallbackIP": "127.0.0.1",
"UseSSL": true,
"ProfileName": "custom",
"CustomHeaders": {
"User-Agent": "VideoReader-Global/1.0",
"X-Client-Version": "1.0.0"
},
"ConnectionTimeout": 10000,
"EnableHeartbeat": true,
"HeartbeatInterval": 500
}

View File

@@ -0,0 +1,52 @@
# VideoReader Global Server
## Quick Start
1. Install dependencies:
```bash
npm install
```
2. Start the server:
```bash
npm start
```
3. Open web interface:
```
http://localhost:3000
```
## Configuration
Environment variables:
- `PORT` - Signaling server port (default: 3000)
- `MEDIA_PORT` - Media relay port (default: 5000)
- `MEDIA_SERVER_IP` - IP to return to clients (default: 127.0.0.1)
## Client Configuration
Update your VideoReader client config:
```json
{
"SignalingServer": "localhost:3000",
"DataPort": 5000,
"DefaultChannel": 10,
"UseSSL": false,
"ProfileName": "local"
}
```
## API Endpoints
- `GET /` - Web interface
- `GET /get-ip-kr.php?port={channel}` - Get media server IP (binary response)
- `GET /api/status` - Server status (JSON)
- `GET /api/channels` - Active channels list (JSON)
## Docker Deployment
```bash
docker build -t videoreader-server .
docker run -p 3000:3000 -p 5000:5000 videoreader-server
```

View File

@@ -0,0 +1,52 @@
# VideoReader Global Server
## Quick Start
1. Install dependencies:
```bash
npm install
```
2. Start the server:
```bash
npm start
```
3. Open web interface:
```
http://localhost:3000
```
## Configuration
Environment variables:
- `PORT` - Signaling server port (default: 3000)
- `MEDIA_PORT` - Media relay port (default: 5000)
- `MEDIA_SERVER_IP` - IP to return to clients (default: 127.0.0.1)
## Client Configuration
Update your VideoReader client config:
```json
{
"SignalingServer": "localhost:3000",
"DataPort": 5000,
"DefaultChannel": 10,
"UseSSL": false,
"ProfileName": "local"
}
```
## API Endpoints
- `GET /` - Web interface
- `GET /get-ip-kr.php?port={channel}` - Get media server IP (binary response)
- `GET /api/status` - Server status (JSON)
- `GET /api/channels` - Active channels list (JSON)
## Docker Deployment
```bash
docker build -t videoreader-server .
docker run -p 3000:3000 -p 5000:5000 videoreader-server
```

View File

@@ -0,0 +1,23 @@
{
"name": "videoreader-global-server",
"version": "1.0.0",
"description": "Signaling and media relay server for VideoReader Global",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "echo \"No tests yet\" && exit 0"
},
"keywords": ["videoreader", "signaling", "media", "relay"],
"author": "VideoReader Global Team",
"license": "MIT",
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"engines": {
"node": ">=16.0.0"
}
}

View File

@@ -0,0 +1,23 @@
{
"name": "videoreader-global-server",
"version": "1.0.0",
"description": "Signaling and media relay server for VideoReader Global",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "echo \"No tests yet\" && exit 0"
},
"keywords": ["videoreader", "signaling", "media", "relay"],
"author": "VideoReader Global Team",
"license": "MIT",
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"engines": {
"node": ">=16.0.0"
}
}

View File

@@ -0,0 +1,250 @@
const express = require('express');
const net = require('net');
const fs = require('fs');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;
const MEDIA_PORT = process.env.MEDIA_PORT || 5000;
// Логирование
const log = (message) => {
console.log(`[${new Date().toISOString()}] ${message}`);
};
// Хранилище активных каналов
const activeChannels = new Map();
// Middleware
app.use(express.json());
app.use((req, res, next) => {
log(`${req.method} ${req.url} from ${req.ip}`);
next();
});
// CORS для всех доменов (в продакшене настроить конкретные домены)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// Маршрут для получения IP медиа-сервера (совместимость с оригинальным API)
app.get('/get-ip-kr.php', (req, res) => {
const port = req.query.port;
log(`IP request for channel: ${port}`);
// В простой реализации возвращаем IP этого же сервера
// В продакшене здесь может быть логика балансировки
const mediaServerIP = process.env.MEDIA_SERVER_IP || '127.0.0.1';
// Возвращаем IP в бинарном формате (4 байта) как в оригинале
const ipParts = mediaServerIP.split('.').map(n => parseInt(n));
const buffer = Buffer.from(ipParts);
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Length': buffer.length
});
res.end(buffer);
log(`Returned IP: ${mediaServerIP} for channel ${port}`);
});
// REST API для управления
app.get('/api/status', (req, res) => {
res.json({
status: 'running',
activeChannels: activeChannels.size,
uptime: process.uptime(),
channels: Array.from(activeChannels.keys())
});
});
app.get('/api/channels', (req, res) => {
const channelList = Array.from(activeChannels.entries()).map(([channel, data]) => ({
channel,
connections: data.connections,
created: data.created
}));
res.json(channelList);
});
// Веб-интерфейс
app.get('/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>VideoReader Global Server</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.status { background: #f0f0f0; padding: 20px; border-radius: 5px; }
.channel { margin: 10px 0; padding: 10px; background: #e8f4fd; border-radius: 3px; }
</style>
</head>
<body>
<h1>🌐 VideoReader Global Server</h1>
<div class="status">
<h2>Server Status</h2>
<p><strong>Status:</strong> Running</p>
<p><strong>Signaling Port:</strong> ${PORT}</p>
<p><strong>Media Port:</strong> ${MEDIA_PORT}</p>
<p><strong>Active Channels:</strong> <span id="channelCount">Loading...</span></p>
</div>
<h2>📡 Configuration</h2>
<pre>
{
"SignalingServer": "${req.get('host')}",
"DataPort": ${MEDIA_PORT},
"DefaultChannel": 10,
"FallbackIP": "127.0.0.1",
"UseSSL": false,
"ProfileName": "local"
}
</pre>
<h2>📊 Active Channels</h2>
<div id="channels">Loading...</div>
<script>
async function updateStatus() {
try {
const response = await fetch('/api/channels');
const channels = await response.json();
document.getElementById('channelCount').textContent = channels.length;
const channelsDiv = document.getElementById('channels');
channelsDiv.innerHTML = channels.length === 0
? '<p>No active channels</p>'
: channels.map(ch =>
\`<div class="channel">
<strong>Channel \${ch.channel}:</strong>
\${ch.connections} connections
<small>(created: \${new Date(ch.created).toLocaleString()})</small>
</div>\`
).join('');
} catch (e) {
console.error('Failed to update status:', e);
}
}
updateStatus();
setInterval(updateStatus, 5000);
</script>
</body>
</html>
`);
});
// TCP медиа-сервер
const mediaServer = net.createServer((socket) => {
let channel = null;
let deviceType = null;
log(`New connection from ${socket.remoteAddress}:${socket.remotePort}`);
socket.on('data', (data) => {
if (channel === null && data.length >= 2) {
// Первые 2 байта: тип устройства (0=receiver, 1=sender) и канал
deviceType = data[0];
channel = data[1];
log(`Device connected - Type: ${deviceType === 0 ? 'receiver' : 'sender'}, Channel: ${channel}`);
// Регистрируем канал
if (!activeChannels.has(channel)) {
activeChannels.set(channel, {
connections: 0,
created: new Date().toISOString(),
receivers: [],
senders: []
});
}
const channelData = activeChannels.get(channel);
channelData.connections++;
if (deviceType === 0) {
channelData.receivers.push(socket);
} else {
channelData.senders.push(socket);
}
// Обработка данных после установки канала
if (data.length > 2) {
handleMediaData(socket, data.slice(2), channel, deviceType);
}
} else if (channel !== null) {
// Передача медиа-данных
handleMediaData(socket, data, channel, deviceType);
}
});
socket.on('close', () => {
if (channel !== null) {
const channelData = activeChannels.get(channel);
if (channelData) {
channelData.connections--;
// Удаляем сокет из соответствующего массива
if (deviceType === 0) {
channelData.receivers = channelData.receivers.filter(s => s !== socket);
} else {
channelData.senders = channelData.senders.filter(s => s !== socket);
}
// Удаляем канал если нет подключений
if (channelData.connections <= 0) {
activeChannels.delete(channel);
log(`Channel ${channel} removed - no active connections`);
}
}
}
log(`Connection closed from ${socket.remoteAddress}:${socket.remotePort}`);
});
socket.on('error', (err) => {
log(`Socket error: ${err.message}`);
});
});
function handleMediaData(fromSocket, data, channel, fromType) {
const channelData = activeChannels.get(channel);
if (!channelData) return;
// Пересылаем данные от отправителей к получателям и наоборот
const targetSockets = fromType === 0 ? channelData.senders : channelData.receivers;
targetSockets.forEach(socket => {
if (socket !== fromSocket && socket.writable) {
try {
socket.write(data);
} catch (err) {
log(`Error forwarding data: ${err.message}`);
}
}
});
}
// Запуск серверов
app.listen(PORT, () => {
log(`🌐 Signaling server running on port ${PORT}`);
log(`📡 Web interface: http://localhost:${PORT}`);
});
mediaServer.listen(MEDIA_PORT, () => {
log(`📺 Media server running on port ${MEDIA_PORT}`);
});
// Graceful shutdown
process.on('SIGINT', () => {
log('Shutting down servers...');
mediaServer.close();
process.exit(0);
});
// Экспорт для возможности тестирования
module.exports = { app, mediaServer };

View File

@@ -0,0 +1,250 @@
const express = require('express');
const net = require('net');
const fs = require('fs');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;
const MEDIA_PORT = process.env.MEDIA_PORT || 5000;
// Логирование
const log = (message) => {
console.log(`[${new Date().toISOString()}] ${message}`);
};
// Хранилище активных каналов
const activeChannels = new Map();
// Middleware
app.use(express.json());
app.use((req, res, next) => {
log(`${req.method} ${req.url} from ${req.ip}`);
next();
});
// CORS для всех доменов (в продакшене настроить конкретные домены)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// Маршрут для получения IP медиа-сервера (совместимость с оригинальным API)
app.get('/get-ip-kr.php', (req, res) => {
const port = req.query.port;
log(`IP request for channel: ${port}`);
// В простой реализации возвращаем IP этого же сервера
// В продакшене здесь может быть логика балансировки
const mediaServerIP = process.env.MEDIA_SERVER_IP || '127.0.0.1';
// Возвращаем IP в бинарном формате (4 байта) как в оригинале
const ipParts = mediaServerIP.split('.').map(n => parseInt(n));
const buffer = Buffer.from(ipParts);
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Length': buffer.length
});
res.end(buffer);
log(`Returned IP: ${mediaServerIP} for channel ${port}`);
});
// REST API для управления
app.get('/api/status', (req, res) => {
res.json({
status: 'running',
activeChannels: activeChannels.size,
uptime: process.uptime(),
channels: Array.from(activeChannels.keys())
});
});
app.get('/api/channels', (req, res) => {
const channelList = Array.from(activeChannels.entries()).map(([channel, data]) => ({
channel,
connections: data.connections,
created: data.created
}));
res.json(channelList);
});
// Веб-интерфейс
app.get('/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>VideoReader Global Server</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.status { background: #f0f0f0; padding: 20px; border-radius: 5px; }
.channel { margin: 10px 0; padding: 10px; background: #e8f4fd; border-radius: 3px; }
</style>
</head>
<body>
<h1>🌐 VideoReader Global Server</h1>
<div class="status">
<h2>Server Status</h2>
<p><strong>Status:</strong> Running</p>
<p><strong>Signaling Port:</strong> ${PORT}</p>
<p><strong>Media Port:</strong> ${MEDIA_PORT}</p>
<p><strong>Active Channels:</strong> <span id="channelCount">Loading...</span></p>
</div>
<h2>📡 Configuration</h2>
<pre>
{
"SignalingServer": "${req.get('host')}",
"DataPort": ${MEDIA_PORT},
"DefaultChannel": 10,
"FallbackIP": "127.0.0.1",
"UseSSL": false,
"ProfileName": "local"
}
</pre>
<h2>📊 Active Channels</h2>
<div id="channels">Loading...</div>
<script>
async function updateStatus() {
try {
const response = await fetch('/api/channels');
const channels = await response.json();
document.getElementById('channelCount').textContent = channels.length;
const channelsDiv = document.getElementById('channels');
channelsDiv.innerHTML = channels.length === 0
? '<p>No active channels</p>'
: channels.map(ch =>
\`<div class="channel">
<strong>Channel \${ch.channel}:</strong>
\${ch.connections} connections
<small>(created: \${new Date(ch.created).toLocaleString()})</small>
</div>\`
).join('');
} catch (e) {
console.error('Failed to update status:', e);
}
}
updateStatus();
setInterval(updateStatus, 5000);
</script>
</body>
</html>
`);
});
// TCP медиа-сервер
const mediaServer = net.createServer((socket) => {
let channel = null;
let deviceType = null;
log(`New connection from ${socket.remoteAddress}:${socket.remotePort}`);
socket.on('data', (data) => {
if (channel === null && data.length >= 2) {
// Первые 2 байта: тип устройства (0=receiver, 1=sender) и канал
deviceType = data[0];
channel = data[1];
log(`Device connected - Type: ${deviceType === 0 ? 'receiver' : 'sender'}, Channel: ${channel}`);
// Регистрируем канал
if (!activeChannels.has(channel)) {
activeChannels.set(channel, {
connections: 0,
created: new Date().toISOString(),
receivers: [],
senders: []
});
}
const channelData = activeChannels.get(channel);
channelData.connections++;
if (deviceType === 0) {
channelData.receivers.push(socket);
} else {
channelData.senders.push(socket);
}
// Обработка данных после установки канала
if (data.length > 2) {
handleMediaData(socket, data.slice(2), channel, deviceType);
}
} else if (channel !== null) {
// Передача медиа-данных
handleMediaData(socket, data, channel, deviceType);
}
});
socket.on('close', () => {
if (channel !== null) {
const channelData = activeChannels.get(channel);
if (channelData) {
channelData.connections--;
// Удаляем сокет из соответствующего массива
if (deviceType === 0) {
channelData.receivers = channelData.receivers.filter(s => s !== socket);
} else {
channelData.senders = channelData.senders.filter(s => s !== socket);
}
// Удаляем канал если нет подключений
if (channelData.connections <= 0) {
activeChannels.delete(channel);
log(`Channel ${channel} removed - no active connections`);
}
}
}
log(`Connection closed from ${socket.remoteAddress}:${socket.remotePort}`);
});
socket.on('error', (err) => {
log(`Socket error: ${err.message}`);
});
});
function handleMediaData(fromSocket, data, channel, fromType) {
const channelData = activeChannels.get(channel);
if (!channelData) return;
// Пересылаем данные от отправителей к получателям и наоборот
const targetSockets = fromType === 0 ? channelData.senders : channelData.receivers;
targetSockets.forEach(socket => {
if (socket !== fromSocket && socket.writable) {
try {
socket.write(data);
} catch (err) {
log(`Error forwarding data: ${err.message}`);
}
}
});
}
// Запуск серверов
app.listen(PORT, () => {
log(`🌐 Signaling server running on port ${PORT}`);
log(`📡 Web interface: http://localhost:${PORT}`);
});
mediaServer.listen(MEDIA_PORT, () => {
log(`📺 Media server running on port ${MEDIA_PORT}`);
});
// Graceful shutdown
process.on('SIGINT', () => {
log('Shutting down servers...');
mediaServer.close();
process.exit(0);
});
// Экспорт для возможности тестирования
module.exports = { app, mediaServer };