├── LICENSE ├── NetworkVideoEncoder ├── .gitignore ├── Client │ ├── App.config │ ├── Client.csproj │ ├── JobHandler.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Resources.cs │ └── RunFFMPEG.cs ├── NetworkVideoEncoder.sln ├── Server │ ├── App.config │ ├── ClientDataBlock.cs │ ├── ClientObject.cs │ ├── DLL │ │ └── AbstractTCPlib.dll │ ├── DownStream.cs │ ├── Job.cs │ ├── JobDataBlock.cs │ ├── JobProvider.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Server.csproj │ ├── StreamHelper.cs │ └── UpStream.cs └── SharedTypes │ ├── Headers.cs │ ├── Properties │ └── AssemblyInfo.cs │ └── SharedTypes.csproj └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jonathan Van den broeck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs -------------------------------------------------------------------------------- /NetworkVideoEncoder/Client/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Client/Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {6B1FA431-2D8A-4DC0-8633-A8D8182D0014} 8 | Exe 9 | Client 10 | Client 11 | v4.5.2 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\Server\DLL\AbstractTCPlib.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {825f525f-54b6-4fb3-9339-9b17e1ad922b} 60 | SharedTypes 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Client/JobHandler.cs: -------------------------------------------------------------------------------- 1 | using AbstractTCPlib; 2 | using SharedTypes; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | 9 | namespace Client 10 | { 11 | public class JobHandler 12 | { 13 | private TCPgeneral gen; 14 | private string ffmpegCommand; 15 | private string job; 16 | private FileStream stream; 17 | private RunFFMPEG ffmpeg; 18 | private string outputFile; 19 | private bool sendCompleted; 20 | private ManualResetEvent reset; 21 | 22 | public JobHandler(TCPgeneral gen) 23 | { 24 | reset = new ManualResetEvent(false); 25 | sendCompleted = false; 26 | this.gen = gen; 27 | gen.OnRawDataRecieved += OnRecieved; 28 | gen.OnError += OnError; 29 | } 30 | public void Start() 31 | { 32 | gen.Start(); 33 | 34 | 35 | reset.WaitOne(); 36 | 37 | reset.Reset(); 38 | 39 | } 40 | private void SendVidPiece() 41 | { 42 | if (sendCompleted) 43 | { 44 | gen.SendTCP(Headers.SendCompleted); 45 | stream.Close(); 46 | File.Delete(Path.Combine(Resources.OutputFolder, outputFile)); 47 | 48 | sendCompleted = false; 49 | ffmpegCommand = null; 50 | job = null; 51 | 52 | reset.Set(); 53 | 54 | } 55 | else 56 | { 57 | int l = 10000000; // = 10MB 58 | byte[] data = new byte[l]; 59 | 60 | int read = stream.Read(data, 0, l); 61 | if (read != l) 62 | { 63 | byte[] cropped = new byte[read]; 64 | Array.Copy(data, 0, cropped, 0, read); 65 | gen.SendTCP(Headers.AssembleHeader(Headers.PieceOfVideo, cropped)); 66 | sendCompleted = true; 67 | } 68 | else 69 | { 70 | gen.SendTCP(Headers.AssembleHeader(Headers.PieceOfVideo, data)); 71 | } 72 | } 73 | } 74 | private void OnRecieved(int id, byte[] rawData) 75 | { 76 | byte[] header = Headers.GetHeaderFromData(rawData); 77 | 78 | if (Headers.FfmpegCommand.SequenceEqual(header)) 79 | { 80 | byte[] data; 81 | Headers.SplitData(rawData, out header, out data); 82 | ffmpegCommand = @Encoding.ASCII.GetString(data); 83 | 84 | string formatted = ffmpegCommand.Replace(Resources.ffmpegDATA, Path.Combine(Resources.InputFolder, @job)); 85 | 86 | Guid uniqueName = Guid.NewGuid(); 87 | outputFile = uniqueName.ToString(); 88 | int ind = formatted.IndexOf(Resources.ffmpegOUT); 89 | ind += 3; 90 | 91 | for (int i = ind; i < formatted.Length; i++) 92 | { 93 | if (formatted[i] != '"') 94 | { 95 | outputFile += formatted[i]; 96 | } 97 | else 98 | { 99 | break; 100 | } 101 | } 102 | 103 | formatted = formatted.Replace(Resources.ffmpegOUT, Path.Combine(Resources.OutputFolder, uniqueName.ToString())); 104 | 105 | ffmpeg = new RunFFMPEG(formatted); 106 | Console.WriteLine("command:" + formatted); 107 | } 108 | else if (Headers.Job.SequenceEqual(header)) 109 | { 110 | byte[] data; 111 | Headers.SplitData(rawData, out header, out data); 112 | stream = new FileStream(Path.Combine(Resources.InputFolder, Encoding.ASCII.GetString(data)), FileMode.Append); 113 | job = @Encoding.ASCII.GetString(data); 114 | Console.WriteLine("job " + job); 115 | } 116 | else if (Headers.PieceOfVideo.SequenceEqual(header)) 117 | { 118 | byte[] vid; 119 | Headers.SplitData(rawData, out header, out vid); 120 | stream.Write(vid, 0, vid.Length); 121 | gen.SendTCP(Headers.SendNext); 122 | } 123 | else if (Headers.SendCompleted.SequenceEqual(header)) 124 | { 125 | stream.Close(); 126 | stream = null; 127 | Console.WriteLine("Recieved all"); 128 | ffmpeg.Start(); 129 | stream = new FileStream(Path.Combine(Resources.OutputFolder, outputFile), FileMode.Open); 130 | File.Delete(Path.Combine(Resources.InputFolder, @job)); 131 | gen.SendTCP(Headers.RenderCompleted); 132 | } 133 | else if (Headers.SendNext.SequenceEqual(header)) 134 | { 135 | SendVidPiece(); 136 | } 137 | } 138 | 139 | private void OnError(int id, ErrorTypes type, string error) 140 | { 141 | Console.WriteLine("error: " + type.ToString() + " " + error); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AbstractTCPlib; 3 | using AbstractTCPlib.UDPdiscovery; 4 | using System.Net.Sockets; 5 | using System.IO; 6 | 7 | namespace Client 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | if (args.Length != 1) 14 | { 15 | Console.WriteLine(Resources.Usage); 16 | Console.ReadLine(); 17 | Environment.Exit(0); 18 | } 19 | int port; 20 | 21 | bool ok = int.TryParse(args[0], out port); 22 | 23 | if (!ok) 24 | { 25 | Console.WriteLine(Resources.Usage); 26 | Console.ReadLine(); 27 | Environment.Exit(0); 28 | } 29 | 30 | UDPclient udp = new UDPclient(Resources.BroadCast, port); 31 | TcpClient client; 32 | while (true) 33 | { 34 | client = udp.Broadcast(); 35 | 36 | if (client != null) 37 | { 38 | client.SendBufferSize = 64000; 39 | client.ReceiveBufferSize = 64000; //needed for linux 40 | break; 41 | } 42 | } 43 | 44 | CreateFolders(Resources.InputFolder); 45 | CreateFolders(Resources.OutputFolder); 46 | 47 | ClearFolder(Resources.InputFolder); 48 | ClearFolder(Resources.OutputFolder); 49 | 50 | TCPgeneral gen = new TCPgeneral(client, 0); 51 | 52 | JobHandler job = new JobHandler(gen); 53 | 54 | while (true) 55 | { 56 | job.Start(); 57 | } 58 | } 59 | 60 | private static void CreateFolders(string folder) 61 | { 62 | if (!Directory.Exists(folder)) 63 | { 64 | Directory.CreateDirectory(folder); 65 | } 66 | } 67 | 68 | private static void ClearFolder(string folder) 69 | { 70 | var files = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folder)); 71 | 72 | foreach (var file in files) 73 | { 74 | File.Delete(file); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Client/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Client")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Client")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("6b1fa431-2d8a-4dc0-8633-a8d8182d0014")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Client/Resources.cs: -------------------------------------------------------------------------------- 1 | namespace Client 2 | { 3 | public static class Resources 4 | { 5 | public static string BroadCast = "networkVideoEncoder"; 6 | public static string Usage = "command line input: udpPort"; 7 | public static string OutputFolder = "OUTPUT"; 8 | public static string InputFolder = "INPUT"; 9 | public static string ffmpegDATA = "DATA"; 10 | public static string ffmpegOUT = "OUT"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Client/RunFFMPEG.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Client 5 | { 6 | public class RunFFMPEG 7 | { 8 | private string binLocationL = @"/usr/bin/ffmpeg"; 9 | private Process ffmpegProcess; 10 | private string binaryW = @"ffmpeg.exe"; 11 | 12 | public RunFFMPEG(string command) 13 | { 14 | ffmpegProcess = new Process(); 15 | if (IsLinux()) 16 | { 17 | ffmpegProcess.StartInfo.FileName = binLocationL; 18 | ffmpegProcess.StartInfo.CreateNoWindow = true; 19 | ffmpegProcess.StartInfo.UseShellExecute = false; 20 | } 21 | else 22 | { 23 | ffmpegProcess.StartInfo.FileName = binaryW; 24 | ffmpegProcess.StartInfo.CreateNoWindow = false; 25 | ffmpegProcess.StartInfo.UseShellExecute = true; 26 | } 27 | ffmpegProcess.StartInfo.Arguments = command; 28 | } 29 | 30 | public void Start() 31 | { 32 | ffmpegProcess.Start(); 33 | ffmpegProcess.WaitForExit(); 34 | } 35 | 36 | private bool IsLinux() 37 | { 38 | int p = (int)Environment.OSVersion.Platform; 39 | return (p == 4) || (p == 6) || (p == 128); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/NetworkVideoEncoder.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.13 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{A4A128D8-1E40-49C7-876A-A1376188C1B7}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedTypes", "SharedTypes\SharedTypes.csproj", "{825F525F-54B6-4FB3-9339-9B17E1AD922B}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{6B1FA431-2D8A-4DC0-8633-A8D8182D0014}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {A4A128D8-1E40-49C7-876A-A1376188C1B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {A4A128D8-1E40-49C7-876A-A1376188C1B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {A4A128D8-1E40-49C7-876A-A1376188C1B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {A4A128D8-1E40-49C7-876A-A1376188C1B7}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {825F525F-54B6-4FB3-9339-9B17E1AD922B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {825F525F-54B6-4FB3-9339-9B17E1AD922B}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {825F525F-54B6-4FB3-9339-9B17E1AD922B}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {825F525F-54B6-4FB3-9339-9B17E1AD922B}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {6B1FA431-2D8A-4DC0-8633-A8D8182D0014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {6B1FA431-2D8A-4DC0-8633-A8D8182D0014}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {6B1FA431-2D8A-4DC0-8633-A8D8182D0014}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {6B1FA431-2D8A-4DC0-8633-A8D8182D0014}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/ClientDataBlock.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Server 4 | { 5 | public static class ClientDataBlock 6 | { 7 | private static object locker = new object(); 8 | 9 | private static List _clients = new List(); 10 | 11 | public static List Clients 12 | { 13 | get 14 | { 15 | lock (locker) 16 | { 17 | return _clients; 18 | } 19 | } 20 | set 21 | { 22 | lock (locker) 23 | { 24 | _clients = value; 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/ClientObject.cs: -------------------------------------------------------------------------------- 1 | using AbstractTCPlib; 2 | using System; 3 | 4 | namespace Server 5 | { 6 | public class ClientObject 7 | { 8 | public Action OnFinished; 9 | 10 | public TCPgeneral socket { get; set; } 11 | public bool HasJob { get; set; } 12 | public DateTime LastSeen { get; set; } 13 | public string CurrentJob { get; set; } 14 | public DateTime JobStarted { get; set; } 15 | public bool isDone { get; set; } 16 | 17 | public void Finished() 18 | { 19 | if (OnFinished != null) 20 | { 21 | OnFinished(this); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/DLL/AbstractTCPlib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathan2266/NetworkVideoEncoder/5ca53c008085cd99f1a4cad66359756194082ca6/NetworkVideoEncoder/Server/DLL/AbstractTCPlib.dll -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/DownStream.cs: -------------------------------------------------------------------------------- 1 | using AbstractTCPlib; 2 | using SharedTypes; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading; 7 | 8 | namespace Server 9 | { 10 | public class DownStream 11 | { 12 | private ClientObject obj; 13 | private FileStream stream; 14 | private byte[] pieceOfVideo; 15 | private string output; 16 | private AutoResetEvent waitHandle; 17 | 18 | public DownStream(ClientObject obj, string output, string extenstion) 19 | { 20 | waitHandle = new AutoResetEvent(false); 21 | this.output = output; 22 | pieceOfVideo = null; 23 | this.obj = obj; 24 | stream = File.OpenWrite(Path.Combine(output, Path.GetFileNameWithoutExtension(obj.CurrentJob) + extenstion)); 25 | obj.socket.OnRawDataRecieved += OnRecieved; 26 | obj.socket.OnError += OnError; 27 | } 28 | public void Start() 29 | { 30 | obj.socket.SendTCP(Headers.SendNext); 31 | 32 | waitHandle.WaitOne(); 33 | 34 | stream.Close(); 35 | 36 | } 37 | private void OnRecieved(int id, byte[] rawData) 38 | { 39 | byte[] header = Headers.GetHeaderFromData(rawData); 40 | 41 | if (Headers.PieceOfVideo.SequenceEqual(header)) 42 | { 43 | Headers.SplitData(rawData, out header, out pieceOfVideo); 44 | 45 | try 46 | { 47 | stream.Write(pieceOfVideo, 0, pieceOfVideo.Length); 48 | obj.socket.SendTCP(Headers.SendNext); 49 | } 50 | catch (Exception e) 51 | { 52 | Console.WriteLine("Downstream write exception: " + e.Message); 53 | stream.Close(); 54 | } 55 | } 56 | else if (Headers.SendCompleted.SequenceEqual(header)) 57 | { 58 | waitHandle.Set(); 59 | 60 | lock (ClientDataBlock.Clients) 61 | { 62 | obj.socket.OnRawDataRecieved -= OnRecieved; 63 | obj.socket.OnError -= OnError; 64 | obj.isDone = true; 65 | obj.Finished(); 66 | } 67 | } 68 | } 69 | private void OnError(int id, ErrorTypes type, string message) 70 | { 71 | waitHandle.Set(); 72 | 73 | lock (ClientDataBlock.Clients) 74 | { 75 | obj.socket.OnRawDataRecieved -= OnRecieved; 76 | obj.socket.OnError -= OnError; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/Job.cs: -------------------------------------------------------------------------------- 1 | namespace Server 2 | { 3 | public class Job 4 | { 5 | public string JobURL { get; set; } 6 | public int ClientID { get; set; } 7 | public bool IsGivenAsJob { get; set; } 8 | public bool IsDone { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/JobDataBlock.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Server 4 | { 5 | public static class JobDataBlock 6 | { 7 | private static object locker = new object(); 8 | 9 | private static List _jobs = new List(); 10 | public static List Jobs 11 | { 12 | get 13 | { 14 | lock (locker) 15 | { 16 | return _jobs; 17 | } 18 | } 19 | set 20 | { 21 | lock (locker) 22 | { 23 | _jobs = value; 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/JobProvider.cs: -------------------------------------------------------------------------------- 1 | using AbstractTCPlib; 2 | using System; 3 | using System.Text; 4 | using System.IO; 5 | using SharedTypes; 6 | using System.Linq; 7 | using System.Threading; 8 | 9 | namespace Server 10 | { 11 | public class JobProvider 12 | { 13 | private string output; 14 | private string ffmpegCommand; 15 | private StreamHelper streamer; 16 | private ManualResetEvent mainLoopWait; 17 | private string extension; 18 | 19 | public JobProvider(string ffmpeg, string source, string output) 20 | { 21 | this.output = output; 22 | 23 | string[] files = Directory.GetFiles(source); 24 | 25 | for (int i = 0; i < files.Length; i++) 26 | { 27 | files[i] = Path.GetFileName(files[i]); 28 | } 29 | 30 | for (int i = 0; i < files.Length; i++) 31 | { 32 | JobDataBlock.Jobs.Add(new Job 33 | { 34 | IsGivenAsJob = false, 35 | JobURL = files[i], 36 | ClientID = -1, 37 | IsDone = false 38 | }); 39 | } 40 | 41 | StreamReader reader = new StreamReader(ffmpeg); 42 | ffmpegCommand = reader.ReadToEnd(); 43 | reader.Close(); 44 | 45 | int ind = ffmpegCommand.IndexOf("OUT"); 46 | ind += 3; 47 | 48 | for (int i = ind; i < ffmpegCommand.Length; i++) 49 | { 50 | if (ffmpegCommand[i] != '"') 51 | { 52 | extension += ffmpegCommand[i]; 53 | } 54 | else 55 | { 56 | break; 57 | } 58 | } 59 | 60 | streamer = new StreamHelper(4, source, output, extension); 61 | 62 | mainLoopWait = new ManualResetEvent(false); 63 | } 64 | 65 | public void RunJobs() 66 | { 67 | GiveJobs(); 68 | 69 | mainLoopWait.WaitOne(); 70 | 71 | streamer.Dispose(); 72 | } 73 | private void GiveJobs() 74 | { 75 | lock (ClientDataBlock.Clients) 76 | { 77 | lock (JobDataBlock.Jobs) 78 | { 79 | foreach (var client in ClientDataBlock.Clients) 80 | { 81 | if (!client.HasJob && client.socket.IsAlive) 82 | { 83 | foreach (var job in JobDataBlock.Jobs) 84 | { 85 | if (!job.IsGivenAsJob) 86 | { 87 | job.IsGivenAsJob = true; 88 | job.ClientID = client.socket.ID; 89 | client.HasJob = true; 90 | client.CurrentJob = job.JobURL; 91 | client.socket.SendTCP(Headers.AssembleHeader(Headers.Job, Encoding.ASCII.GetBytes(job.JobURL))); 92 | client.socket.SendTCP(Headers.AssembleHeader(Headers.FfmpegCommand, Encoding.ASCII.GetBytes(ffmpegCommand))); 93 | streamer.AddClientToSendQeue(client); 94 | break; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } 102 | 103 | public void AddClient(TCPgeneral client) 104 | { 105 | lock (ClientDataBlock.Clients) 106 | { 107 | client.OnError = OnError; 108 | client.OnRawDataRecieved += RecievedData; 109 | client.Start(); 110 | 111 | ClientObject obj = new ClientObject 112 | { 113 | socket = client, 114 | HasJob = false, 115 | LastSeen = DateTime.Now, 116 | JobStarted = DateTime.MinValue, 117 | isDone = false 118 | }; 119 | obj.OnFinished += ClientFinished; 120 | ClientDataBlock.Clients.Add(obj); 121 | 122 | GiveJobs(); 123 | } 124 | } 125 | 126 | private void RecievedData(int id, byte[] data) 127 | { 128 | byte[] header = Headers.GetHeaderFromData(data); 129 | 130 | if (header.SequenceEqual(Headers.HelloUpdate)) 131 | { 132 | lock (ClientDataBlock.Clients) 133 | { 134 | ClientObject obj = ClientDataBlock.Clients.Where(s => s.socket.ID == id).FirstOrDefault(); 135 | obj.LastSeen = DateTime.Now; 136 | } 137 | } 138 | else if (header.SequenceEqual(Headers.RenderCompleted)) 139 | { 140 | lock (ClientDataBlock.Clients) 141 | { 142 | streamer.AddClientToRecieveQeue(ClientDataBlock.Clients.Where(s => s.socket.ID == id).FirstOrDefault()); 143 | Console.WriteLine("ID: " + id + " renderCompleted"); 144 | } 145 | } 146 | else if (header.SequenceEqual(Headers.RenderError)) 147 | { 148 | lock (ClientDataBlock.Clients) 149 | { 150 | lock (JobDataBlock.Jobs) 151 | { 152 | // log job from being bad or maybe ffmpeg command being bad 153 | Console.WriteLine("ID: " + id + " renderError"); 154 | 155 | ClientObject obj = ClientDataBlock.Clients.Where(client => client.socket.ID == id).FirstOrDefault(); 156 | if (obj != null) 157 | { 158 | Job jo = JobDataBlock.Jobs.Where(j => j.ClientID == obj.socket.ID).FirstOrDefault(); 159 | 160 | if (jo != null) 161 | { 162 | jo.IsGivenAsJob = false; 163 | jo.IsDone = false; 164 | jo.ClientID = -1; 165 | } 166 | } 167 | } 168 | } 169 | } 170 | } 171 | 172 | private void OnError(int id, ErrorTypes type, string message) 173 | { 174 | lock (ClientDataBlock.Clients) 175 | { 176 | lock (JobDataBlock.Jobs) 177 | { 178 | Console.WriteLine("lost connection with:" + id + " " + type.ToString() + " " + message); 179 | 180 | ClientObject obj = ClientDataBlock.Clients.Where(client => client.socket.ID == id).FirstOrDefault(); 181 | if (obj != null) 182 | { 183 | lock (obj) 184 | { 185 | Job jo = JobDataBlock.Jobs.Where(j => j.ClientID == obj.socket.ID).FirstOrDefault(); 186 | 187 | if (jo != null) 188 | { 189 | jo.IsGivenAsJob = false; 190 | jo.IsDone = false; 191 | jo.ClientID = -1; 192 | } 193 | obj.socket.Dispose(); 194 | 195 | ClientDataBlock.Clients.Remove(obj); 196 | } 197 | } 198 | } 199 | } 200 | } 201 | private void ClientFinished(ClientObject obj) 202 | { 203 | lock (ClientDataBlock.Clients) 204 | { 205 | lock (JobDataBlock.Jobs) 206 | { 207 | Job jo = JobDataBlock.Jobs.Where(j => j.ClientID == obj.socket.ID).FirstOrDefault(); 208 | 209 | if (jo != null) 210 | { 211 | Console.WriteLine("job: " + jo.JobURL + " is done"); 212 | jo.IsDone = true; 213 | jo.ClientID = -1; 214 | obj.HasJob = false; 215 | obj.isDone = true; 216 | obj.JobStarted = DateTime.MinValue; 217 | obj.LastSeen = DateTime.Now; 218 | } 219 | } 220 | } 221 | 222 | GiveJobs(); 223 | 224 | IsAllDone(); 225 | } 226 | private void IsAllDone() 227 | { 228 | lock (JobDataBlock.Jobs) 229 | { 230 | int incompleted = JobDataBlock.Jobs.Where(job => job.IsDone == false).Count(); 231 | 232 | if (incompleted > 0) 233 | { 234 | return; 235 | } 236 | 237 | int stillWorking = ClientDataBlock.Clients.Where(client => client.HasJob == true).Count(); 238 | 239 | if (stillWorking > 0) 240 | { 241 | return; 242 | } 243 | 244 | Console.WriteLine("All jobs completed"); 245 | 246 | mainLoopWait.Set(); 247 | } 248 | } 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using AbstractTCPlib; 4 | using AbstractTCPlib.UDPdiscovery; 5 | using System.Threading; 6 | using System.Net.Sockets; 7 | 8 | namespace Server 9 | { 10 | class Program 11 | { 12 | static int currentID = 0; 13 | static string usage = "command line input: ffmpeg_command_file source_folder output_folder udpPort"; 14 | static string broadCast = "networkVideoEncoder"; 15 | static string ffmpeg; 16 | static string source; 17 | static string output; 18 | static int port; 19 | 20 | static JobProvider provider; 21 | 22 | static void Main(string[] args) 23 | { 24 | if (args.Length != 4) 25 | { 26 | Console.WriteLine(usage); 27 | Console.ReadLine(); 28 | Environment.Exit(0); 29 | } 30 | 31 | ffmpeg = args[0]; 32 | source = args[1]; 33 | if (!Directory.Exists(source)) 34 | { 35 | Console.WriteLine("input directory does not excist"); 36 | Console.WriteLine(usage); 37 | Environment.Exit(0); 38 | } 39 | output = args[2]; 40 | if (!Directory.Exists(output)) 41 | { 42 | Console.WriteLine("output directory does not excist"); 43 | Console.WriteLine(usage); 44 | Environment.Exit(0); 45 | } 46 | if (!int.TryParse(args[3], out port)) 47 | { 48 | Console.WriteLine("port should be in an int"); 49 | Console.WriteLine(usage); 50 | Environment.Exit(0); 51 | } 52 | 53 | provider = new JobProvider(ffmpeg, source, output); 54 | 55 | Thread listentoClients = new Thread(new ThreadStart(Listen)); 56 | listentoClients.IsBackground = true; 57 | listentoClients.Start(); 58 | 59 | provider.RunJobs(); 60 | 61 | } 62 | 63 | private static void Listen() 64 | { 65 | UDPmaster master = new UDPmaster(broadCast, port); 66 | 67 | while (true) 68 | { 69 | TcpClient client = master.Listen(); 70 | if (client != null) 71 | { 72 | Console.WriteLine("new client found"); 73 | client.SendBufferSize = 64000; 74 | client.ReceiveBufferSize = 64000; //needed for linux 75 | provider.AddClient(new TCPgeneral(client, currentID++)); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Server")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Server")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a4a128d8-1e40-49c7-876a-a1376188c1b7")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A4A128D8-1E40-49C7-876A-A1376188C1B7} 8 | Exe 9 | Server 10 | Server 11 | v4.5.2 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | False 37 | DLL\AbstractTCPlib.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {825F525F-54B6-4FB3-9339-9B17E1AD922B} 69 | SharedTypes 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/StreamHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading; 4 | 5 | namespace Server 6 | { 7 | public class StreamHelper: IDisposable 8 | { 9 | private ConcurrentQueue SendWaiting; 10 | private ConcurrentQueue RecieveWaiting; 11 | private int maxStreams; 12 | private Thread streamSendWorker; 13 | private Thread streamRecieveWorker; 14 | private volatile int currentStreams; 15 | private string source; 16 | private string output; 17 | private string extenstion; 18 | private ManualResetEvent recieveBlock; 19 | private ManualResetEvent sendBlock; 20 | 21 | public StreamHelper(int maxConcurrentStreams, string source, string output, string extenstion) 22 | { 23 | this.source = source; 24 | this.output = output; 25 | this.extenstion = extenstion; 26 | SendWaiting = new ConcurrentQueue(); 27 | RecieveWaiting = new ConcurrentQueue(); 28 | maxStreams = maxConcurrentStreams; 29 | currentStreams = 0; 30 | 31 | recieveBlock = new ManualResetEvent(false); 32 | sendBlock = new ManualResetEvent(false); 33 | 34 | streamSendWorker = new Thread(new ThreadStart(SendWorker)) 35 | { 36 | IsBackground = true 37 | }; 38 | streamSendWorker.Start(); 39 | 40 | streamRecieveWorker = new Thread(new ThreadStart(RecieveWorker)) 41 | { 42 | IsBackground = true 43 | }; 44 | streamRecieveWorker.Start(); 45 | } 46 | private void RecieveWorker() 47 | { 48 | ClientObject obj; 49 | 50 | while (true) 51 | { 52 | recieveBlock.WaitOne(); 53 | 54 | if (currentStreams < maxStreams && RecieveWaiting.Count > 0 && RecieveWaiting.TryDequeue(out obj)) 55 | { 56 | currentStreams++; 57 | Thread streamThread = new Thread(() => { 58 | recieveBlock.Reset(); 59 | new DownStream(obj, output, extenstion).Start(); 60 | currentStreams--; 61 | }); 62 | streamThread.IsBackground = true; 63 | streamThread.Start(); 64 | 65 | lock (RecieveWaiting) 66 | { 67 | if (RecieveWaiting.IsEmpty) 68 | { 69 | recieveBlock.Reset(); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | private void SendWorker() 76 | { 77 | ClientObject obj; 78 | 79 | while (true) 80 | { 81 | sendBlock.WaitOne(); 82 | 83 | if (currentStreams < maxStreams && SendWaiting.Count > 0 && SendWaiting.TryDequeue(out obj)) 84 | { 85 | currentStreams++; 86 | Thread streamThread = new Thread(() => { 87 | new UpStream(obj, source).Start(); }); 88 | currentStreams--; 89 | streamThread.IsBackground = true; 90 | streamThread.Start(); 91 | 92 | lock (SendWaiting) 93 | { 94 | if (SendWaiting.IsEmpty) 95 | { 96 | sendBlock.Reset(); 97 | } 98 | } 99 | } 100 | } 101 | } 102 | public void AddClientToSendQeue(ClientObject obj) 103 | { 104 | if (obj != null) 105 | { 106 | lock (SendWaiting) 107 | { 108 | SendWaiting.Enqueue(obj); 109 | sendBlock.Set(); 110 | } 111 | } 112 | } 113 | public void AddClientToRecieveQeue(ClientObject obj) 114 | { 115 | if (obj != null) 116 | { 117 | lock (RecieveWaiting) 118 | { 119 | RecieveWaiting.Enqueue(obj); 120 | recieveBlock.Set(); 121 | } 122 | } 123 | } 124 | public void Dispose() 125 | { 126 | streamSendWorker.Abort(); 127 | streamRecieveWorker.Abort(); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/Server/UpStream.cs: -------------------------------------------------------------------------------- 1 | using AbstractTCPlib; 2 | using SharedTypes; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading; 7 | 8 | namespace Server 9 | { 10 | public class UpStream 11 | { 12 | private ClientObject obj; 13 | private FileStream stream; 14 | private volatile bool isDone; 15 | private int l = 10000000; // = 10MB 16 | private byte[] data; 17 | private AutoResetEvent reset; 18 | 19 | public UpStream(ClientObject obj, string source) 20 | { 21 | reset = new AutoResetEvent(false); 22 | data = new byte[l]; 23 | this.obj = obj; 24 | obj.socket.OnRawDataRecieved += OnRecieve; 25 | obj.socket.OnError += OnError; 26 | try 27 | { 28 | stream = File.OpenRead(Path.Combine(source, obj.CurrentJob)); 29 | } 30 | catch (Exception) 31 | { 32 | //stop 33 | } 34 | 35 | isDone = false; 36 | } 37 | public void Start() 38 | { 39 | SendNextPiece(); 40 | 41 | reset.WaitOne(); 42 | 43 | stream.Close(); 44 | 45 | lock (ClientDataBlock.Clients) 46 | { 47 | obj.socket.OnRawDataRecieved -= OnRecieve; 48 | obj.socket.OnError -= OnError; 49 | } 50 | } 51 | private void SendNextPiece() 52 | { 53 | if (isDone) 54 | { 55 | obj.socket.SendTCP(Headers.SendCompleted); 56 | obj.JobStarted = DateTime.Now; 57 | reset.Set(); 58 | } 59 | else 60 | { 61 | try 62 | { 63 | int read = stream.Read(data, 0, l); 64 | if (read != l) 65 | { 66 | byte[] cropped = new byte[read]; 67 | Array.Copy(data, 0, cropped, 0, read); 68 | obj.socket.SendTCP(Headers.AssembleHeader(Headers.PieceOfVideo, cropped)); 69 | isDone = true; 70 | } 71 | else 72 | { 73 | obj.socket.SendTCP(Headers.AssembleHeader(Headers.PieceOfVideo, data)); 74 | } 75 | } 76 | catch (Exception e) 77 | { 78 | Console.WriteLine("Exception in upstream: " + e.Message); 79 | stream.Close(); 80 | } 81 | } 82 | 83 | 84 | } 85 | private void OnRecieve(int id, byte[] rawData) 86 | { 87 | byte[] header = Headers.GetHeaderFromData(rawData); 88 | 89 | if (Headers.SendNext.SequenceEqual(header)) 90 | { 91 | SendNextPiece(); 92 | } 93 | } 94 | private void OnError(int id, ErrorTypes type, string message) 95 | { 96 | reset.Set(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/SharedTypes/Headers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharedTypes 4 | { 5 | public static class Headers 6 | { 7 | public static byte[] ID { get { return new byte[] { 0, 0, 0, 0 }; } } 8 | public static byte[] Job { get { return new byte[] { 0, 0, 0, 1 }; } } 9 | public static byte[] PieceOfVideo { get { return new byte[] { 0, 0, 0, 2 }; } } 10 | public static byte[] SendNext { get { return new byte[] { 0, 0, 0, 3 }; } } 11 | public static byte[] HelloUpdate { get { return new byte[] { 0, 0, 0, 4 }; } } 12 | public static byte[] SendCompleted { get { return new byte[] { 0, 0, 0, 5 }; } } 13 | public static byte[] RenderCompleted { get { return new byte[] { 0, 0, 0, 6 }; } } 14 | public static byte[] RenderError { get { return new byte[] { 0, 0, 0, 7 }; } } 15 | public static byte[] FfmpegCommand { get { return new byte[] { 0, 0, 0, 8 }; } } 16 | 17 | public static byte[] AssembleHeader(byte[] header, byte[] data) 18 | { 19 | byte[] fin = new byte[header.Length + data.Length]; 20 | Array.Copy(header, 0, fin, 0, header.Length); 21 | Array.Copy(data, 0, fin, header.Length, data.Length); 22 | return fin; 23 | } 24 | public static byte[] GetHeaderFromData(byte[] data) 25 | { 26 | byte[] header = new byte[4]; 27 | 28 | if (data.Length >= 4) 29 | { 30 | Array.Copy(data, 0, header, 0, 4); 31 | return header; 32 | } 33 | else 34 | { 35 | return null; 36 | } 37 | } 38 | public static void SplitData(byte[] rawData, out byte[] header, out byte[] data) 39 | { 40 | header = new byte[4]; 41 | data = new byte[rawData.Length - 4]; 42 | 43 | if (rawData.Length >= 4) 44 | { 45 | Array.Copy(rawData, 0, header, 0, 4); 46 | Array.Copy(rawData, 4, data, 0, rawData.Length - 4); 47 | } 48 | else 49 | { 50 | header = null; 51 | data = null; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/SharedTypes/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SharedTypes")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SharedTypes")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("825f525f-54b6-4fb3-9339-9b17e1ad922b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /NetworkVideoEncoder/SharedTypes/SharedTypes.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {825F525F-54B6-4FB3-9339-9B17E1AD922B} 8 | Library 9 | Properties 10 | SharedTypes 11 | SharedTypes 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetworkVideoEncoder 2 | Using c# and ffmpeg to encode a batch of video's or music over a network both in mono and windows. 3 | 4 | **early version but works!!** 5 | 6 | ## Usage 7 | 8 | * Linux requires that the ffmpeg binary is under /usr/bin/ 9 | * Windows requires that the ffmpeg.exe binary is supplied locally next to the client.exe 10 | * Running the **Server** or the **Client** binaries will output the correct input arguments 11 | * ffmpeg command file example 12 | * File location given to the **Server** binary. The Client then uses this as input arguments for ffmpeg. 13 | ```bash 14 | -i "DATA" -c:v libx264 -preset fast -crf 50 -map 0:0 -map 0:1 -c:s copy -c:a copy "OUT.extension" 15 | ``` 16 | * Both "DATA" and "OUT" have to be in the command. The program will replace them when needed. The extension has be changed to mp4, mkv,... determines the extension in the result. 17 | 18 | ### Server 19 | 20 | Server c:\ffmpeg.txt D:\videoFolder D:\outputFolder 8081 21 | 22 | Server ffmpeg_command videoFolder outputFolder tcpPort 23 | 24 | 25 | ### Client 26 | 27 | Client 8081 28 | 29 | Client tcpPort 30 | 31 | ## Limitations 32 | 33 | * Video to pictures is not supported 34 | * The Server has to be started **before** the Clients. 35 | * UDP discovery can be improved 36 | 37 | ## Future 38 | 39 | * detect and handle an ffmpeg crash or y/n statement 40 | --------------------------------------------------------------------------------