Skip to content

Commit ba381ee

Browse files
committed
Former-commit-id: f136c44
1 parent 990fb84 commit ba381ee

12 files changed

Lines changed: 307 additions & 275 deletions

File tree

.gitignore

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,4 @@ ASALocalRun/
337337
.localhistory/
338338

339339
# BeatPulse healthcheck temp database
340-
healthchecksdb
341-
342-
# Allow
343-
!/MediaRouter/Libraries/
344-
!/UI Example/Libraries/
340+
healthchecksdb

MediaRouter/Libraries/Codecs/FFmpeg/x64/FFmpeg.AutoGen.dll renamed to MediaRouter/Libraries/Codecs/FFmpeg/FFmpeg.AutoGen.dll

File renamed without changes.
-747 KB
Binary file not shown.

MediaRouter/MediaRouter.csproj

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,8 @@
7777
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
7878
</PropertyGroup>
7979
<ItemGroup>
80-
<Reference Include="FFmpeg.AutoGen, Version=4.2.0.0, Culture=neutral, PublicKeyToken=9b7632533a381715, processorArchitecture=MSIL">
81-
<HintPath>..\packages\FFmpeg.AutoGen.4.2.0\lib\net45\FFmpeg.AutoGen.dll</HintPath>
82-
<Private>False</Private>
83-
<EmbedInteropTypes>False</EmbedInteropTypes>
80+
<Reference Include="FFmpeg.AutoGen">
81+
<HintPath>Libraries\Codecs\FFmpeg\FFmpeg.AutoGen.dll</HintPath>
8482
</Reference>
8583
<Reference Include="System" />
8684
<Reference Include="System.Core" />

README.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,50 @@
1-
# MediaRouter
1+
<div style="text-align:center"><img src="readme2.png" /></div>
22

3-
TODO
3+
# Media Router
4+
5+
<div style="text-align:center"><img src="readme1.png" /></div>
6+
7+
8+
## Introduction
9+
The purpose of Media Router is to be a "mediator" between a frontend GUI Audio / Video player and a backend Multimedia Framework. It will be responsible to satisfy frontend's needs such as Open/Play/Pause/Seek/Stop functionalities but also to serve __accurate__ with the right __control flow__ and __synchronized__ the requested media frames such as Audio, Video and Subtitles.
10+
11+
__Accurate__ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: Media frames will be served at the exact timestamp that they supposed to.
12+
13+
__Control Flow__ &nbsp;: The incoming flow from Multimedia Framework and outcoming to GUI will be kept low (CPU/GPU/RAM).
14+
15+
__Synchronized__ : Ensures that all time the served media frames will be syncronized between them.
16+
17+
<br/>
18+
19+
## Design
20+
21+
#### Layer 1 - Multimedia Framework (FFmpeg.cs)
22+
23+
> <a href="https://www.ffmpeg.org/">FFmpeg 4.2.1</a> library (implemented with C# bindings <a href="https://github.com/Ruslan-B/FFmpeg.AutoGen">FFmpeg.AutoGen</a> 4.2.0)
24+
25+
Demuxers the input file and configures the included media streams. It creates one thread per media stream for decoding. Additionally, it supports hardware acceleration (partially) and accurate seeking by decoding from the previous key/I frame until the requested (in case of B/P frames).
26+
27+
#### Layer 2 - Media Router (MediaRouter.cs)
28+
29+
The main implementation is within the "Screamer" method that routes media frames accurately (based on frame timestamp) to the frontend. It supports Audio and Subtitles synchronization with the Video frames. Additionally, it tries to keep the frame queues low so the backend decoder will run only when required (to keep CPU/GPU/RAM low).
30+
31+
#### Layer 3 - User Interface (UserInterface.cs)
32+
33+
> <a href="http://www.monogame.net/">Monogame</a> 3.7.1 & <a href="https://github.com/naudio/NAudio">NAudio</a> 1.9.0 library & <a href="https://www.codeproject.com/Tips/1193311/Csharp-Slider-Trackbar-Control-using-Windows-Forms">ColorSlider</a>
34+
35+
A sample GUI has been created to demonstrate Media Router's functionality. It works with both Game Engine (for taking the advantage of GPU and Game Loop) and a classic Windows Form. For subtitles will work with BOM specified, UTF-8 formats otherwise with the default system codepage (lazy support for ASS/SSA). For audio it simple runs with the NAudio library. It currently supports :-
36+
37+
Drag & Drop to Open
38+
39+
(P or Space) for Pause / Play,
40+
(S) for Stop,
41+
(R) for Keep Ratio,
42+
(F or Escape) toggles Fullscreen
43+
(Left/Right) for Seeking
44+
45+
(Up/Down) for Volume adjustment
46+
([/]) for Audio adjustment
47+
(;/') for Subtitles adjustment
48+
49+
## Final Words
50+
I have worked on this project for education, fun and programming exercise and I made it available to the public in case you will find it useful for similar reasons. It's always fun as programmers to have our own media player and play around. Any suggestions are always welcome!

UI Example/Subtitles.cs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
using System;
2+
using System.Linq;
3+
using System.Text;
4+
using System.IO;
5+
6+
namespace PartyTime
7+
{
8+
public class Subtitles
9+
{
10+
public static string Convert(string fileName, Encoding input, Encoding output)
11+
{
12+
string tmpFile = null;
13+
try
14+
{
15+
tmpFile = Path.GetTempPath() + Guid.NewGuid().ToString() + ".srt";
16+
StreamReader sr = new StreamReader(new FileStream(fileName, FileMode.Open), input);
17+
StreamWriter sw = new StreamWriter(new FileStream(tmpFile, FileMode.CreateNew), output);
18+
19+
sw.Write(sr.ReadToEnd());
20+
21+
} catch (Exception) { return null; }
22+
23+
return tmpFile;
24+
}
25+
public static Encoding Detect(string fileName)
26+
{
27+
Encoding ret = Encoding.Default;
28+
29+
// Check if BOM
30+
if ((ret = CheckByBOM(fileName)) != Encoding.Default) return ret;
31+
32+
// Check if UTF-8
33+
using (BufferedStream fstream = new BufferedStream(File.OpenRead(fileName)))
34+
{
35+
if (IsUtf8(fstream)) ret = Encoding.UTF8;
36+
}
37+
38+
return ret;
39+
}
40+
public static Encoding CheckByBOM(string path)
41+
{
42+
if (path == null) throw new System.ArgumentNullException("path");
43+
44+
var encodings = Encoding.GetEncodings()
45+
.Select(e => e.GetEncoding())
46+
.Select(e => new { Encoding = e, Preamble = e.GetPreamble() })
47+
.Where(e => e.Preamble.Any())
48+
.ToArray();
49+
50+
var maxPrembleLength = encodings.Max(e => e.Preamble.Length);
51+
byte[] buffer = new byte[maxPrembleLength];
52+
53+
using (var stream = File.OpenRead(path))
54+
{
55+
stream.Read(buffer, 0, (int)Math.Min(maxPrembleLength, stream.Length));
56+
}
57+
58+
return encodings
59+
.Where(enc => enc.Preamble.SequenceEqual(buffer.Take(enc.Preamble.Length)))
60+
.Select(enc => enc.Encoding)
61+
.FirstOrDefault() ?? Encoding.Default;
62+
}
63+
public static bool IsUtf8(Stream stream)
64+
{
65+
int count = 4 * 1024;
66+
byte[] buffer;
67+
int read;
68+
while (true)
69+
{
70+
buffer = new byte[count];
71+
stream.Seek(0, SeekOrigin.Begin);
72+
read = stream.Read(buffer, 0, count);
73+
if (read < count)
74+
{
75+
break;
76+
}
77+
buffer = null;
78+
count *= 2;
79+
}
80+
return IsUtf8(buffer, read);
81+
}
82+
public static bool IsUtf8(byte[] buffer, int length)
83+
{
84+
int position = 0;
85+
int bytes = 0;
86+
while (position < length)
87+
{
88+
if (!IsValid(buffer, position, length, ref bytes))
89+
{
90+
return false;
91+
}
92+
position += bytes;
93+
}
94+
return true;
95+
}
96+
public static bool IsValid(byte[] buffer, int position, int length, ref int bytes)
97+
{
98+
if (length > buffer.Length)
99+
{
100+
throw new ArgumentException("Invalid length");
101+
}
102+
103+
if (position > length - 1)
104+
{
105+
bytes = 0;
106+
return true;
107+
}
108+
109+
byte ch = buffer[position];
110+
111+
if (ch <= 0x7F)
112+
{
113+
bytes = 1;
114+
return true;
115+
}
116+
117+
if (ch >= 0xc2 && ch <= 0xdf)
118+
{
119+
if (position >= length - 2)
120+
{
121+
bytes = 0;
122+
return false;
123+
}
124+
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf)
125+
{
126+
bytes = 0;
127+
return false;
128+
}
129+
bytes = 2;
130+
return true;
131+
}
132+
133+
if (ch == 0xe0)
134+
{
135+
if (position >= length - 3)
136+
{
137+
bytes = 0;
138+
return false;
139+
}
140+
141+
if (buffer[position + 1] < 0xa0 || buffer[position + 1] > 0xbf ||
142+
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
143+
{
144+
bytes = 0;
145+
return false;
146+
}
147+
bytes = 3;
148+
return true;
149+
}
150+
151+
152+
if (ch >= 0xe1 && ch <= 0xef)
153+
{
154+
if (position >= length - 3)
155+
{
156+
bytes = 0;
157+
return false;
158+
}
159+
160+
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
161+
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
162+
{
163+
bytes = 0;
164+
return false;
165+
}
166+
167+
bytes = 3;
168+
return true;
169+
}
170+
171+
if (ch == 0xf0)
172+
{
173+
if (position >= length - 4)
174+
{
175+
bytes = 0;
176+
return false;
177+
}
178+
179+
if (buffer[position + 1] < 0x90 || buffer[position + 1] > 0xbf ||
180+
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
181+
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
182+
{
183+
bytes = 0;
184+
return false;
185+
}
186+
187+
bytes = 4;
188+
return true;
189+
}
190+
191+
if (ch == 0xf4)
192+
{
193+
if (position >= length - 4)
194+
{
195+
bytes = 0;
196+
return false;
197+
}
198+
199+
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0x8f ||
200+
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
201+
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
202+
{
203+
bytes = 0;
204+
return false;
205+
}
206+
207+
bytes = 4;
208+
return true;
209+
}
210+
211+
if (ch >= 0xf1 && ch <= 0xf3)
212+
{
213+
if (position >= length - 4)
214+
{
215+
bytes = 0;
216+
return false;
217+
}
218+
219+
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
220+
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
221+
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
222+
{
223+
bytes = 0;
224+
return false;
225+
}
226+
227+
bytes = 4;
228+
return true;
229+
}
230+
231+
return false;
232+
}
233+
}
234+
}

UI Example/UI Example.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
<Compile Include="frmDisplay.cs" />
9898
<Compile Include="Program.cs" />
9999
<Compile Include="Properties\AssemblyInfo.cs" />
100+
<Compile Include="Subtitles.cs" />
100101
<Compile Include="UserInterface.cs" />
101102
</ItemGroup>
102103
<ItemGroup>

0 commit comments

Comments
 (0)