I'm writing software for streaming MPEG-TS files with multicast. There is two problems I have regarding streaming - bitrate control and even distribution of packets per second.
MPEG-TS have fixed packet size - 188bytes. Thanks to that I know how many packets I have to send per second to have bitrate of input file.
So for the first problem I've found simple but not good solution using stopWatch:
private readonly IPAddress ipAddress;
private readonly int port;
private readonly UdpClient udpClient;
private IPEndPoint endPoint;
private byte[] mpegTsChunk;
private bool disposed = false;
public Server(IPAddress ipAddress, int port, IPAddress localAddress)
{
this.ipAddress = ipAddress;
this.port = port;
udpClient = new UdpClient();
udpClient.JoinMulticastGroup(ipAddress, localAddress);
endPoint = new IPEndPoint(ipAddress, port);
mpegTsChunk = new byte[188];
}
private void SendMpegTsStream(Stream fileStream, int bitRate)
{
int index = 0, packetCounter = 0;
int maxPacketPerSecond = bitRate/(mpegTsChunk.Length*8);
Stopwatch stopWatch = new Stopwatch();
var spin = new SpinWait();
stopWatch.Start();
while (true)
{
while (index < mpegTsChunk.Length)
{
int bytesRead = fileStream.Read(mpegTsChunk, index, mpegTsChunk.Length - index );
if (bytesRead == 0) return;
index += bytesRead;
}
if (index != 0) udpClient.Send(mpegTsChunk, mpegTsChunk.Length, endPoint);
if (index != mpegTsChunk.Length) return;
++packetCounter;
if (packetCounter >= maxPacketPerSecond)
{
/////To fix
while (stopWatch.ElapsedMilliseconds < 1000) { spin.SpinOnce(); }
packetCounter = 0;
stopWatch.Restart();
}
index = 0;
}
}
There are two problems with it: - while(x) {} is very CPU heavy - This creates very unevenly distributed packets I.e: I have 40Mbps MPEG-TS. I want to send it over, using this way, my program will try to send as many packets in the beginning as it can creating peaks - in this case it can easly be more than 100Mbps which will create a problem if I have 100mbps ports. Wireshark screen to show what I mean with my second problem:
How can I evenly distribute thousands of packets in a second? For instance for 10Mbps there are 6900packets to send per second - and evenly distribute them over that one second.
EDIT:
Aboud while(x) {} - i've added SpinOnce() to it and the CPU dropped a lot, however I still don't thinks thats a good way about it. - No spinning - 35-50% CPU - SpinOnce() - 10-12% (while debugging)
EDIT2: No, it's not the same question as one that is linked. I now don't care what happens when packets leave my computer - for now I want all the packets to leave in an even way and worry about receiving later. Only resolution in linked question regarding my problem is similar to my solutions - sending as many packets as I can and then sleep the rest of time - still it makes uneven packet distribution.
EDIT3/Not Perfect answer:
After some time I did this which works:
private void SendMpegTsStream(Stream fileStream, int bitRate)
{
int index = 0, packetCounter = 0;
var maxPacketPerSecond = (float) bitRate/(mpegTsChunk.Length*8) < 1
? (float) bitRate/(mpegTsChunk.Length*8)
: bitRate/(mpegTsChunk.Length*8);
//To do: adapt speed depending on pc
int timeToSleep = (float) bitRate/(mpegTsChunk.Length*8) < 1 ? 100 : bitRate > 10000000 ? 1 : 10;
var stopWatch = new Stopwatch();
stopWatch.Start();
while (streaming)
{
while (index < mpegTsChunk.Length)
{
int bytesRead = fileStream.Read(mpegTsChunk, index, mpegTsChunk.Length - index);
if (bytesRead == 0) return;
index += bytesRead;
}
if (index != 0) udpClient.Send(mpegTsChunk, mpegTsChunk.Length, endPoint);
if (index != mpegTsChunk.Length) return;
++packetCounter;
index = 0;
while (streaming && packetCounter > stopWatch.ElapsedMilliseconds*maxPacketPerSecond/1000)
{
Thread.Sleep(timeToSleep);
if (packetCounter > 2000000000)
{
packetCounter = 0;
stopWatch.Restart();
}
}
}
}