0

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:

Wireshark packets/s screen

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();
                }
            }
        }
    }
Pumar
  • 91
  • 2
  • 13
  • As for the CPU: Perhaps you should add `Threading.Thread.Sleep(0);` in the loop? – Visual Vincent Apr 23 '16 at 14:13
  • 2
    You now have what is called a "busy loop", causing your cpu problems. I suggest using a timer or some other form of scheduling method. – David Libido Apr 23 '16 at 14:16
  • 1
    You can't get better then the PC timer tick accuracy which is handles event and moving UDP packets to the Ethernet driver. Using a timer tick event is the best you can achieve. Since you have an average requirement you can cheat and send larger chunks which will give you the same average. You will never be able to perfectly distribute packets in a windows computer there is no way of you getting to the low level interrupt without going through the timer tick. The timer tick will give you uneven flow. – jdweng Apr 23 '16 at 14:16
  • There's not enough code in your question. You've provided a loop, with no other context. – Robert Harvey Apr 23 '16 at 15:37
  • VisualVincent - Sleep(0) doesn't change CPU utilization. DavidLibido - I know but the problem is I have to do it under 1 second, which limits what I can do. Especially that I would like to have it evenly distributed. jdweng as I said, I would still like to have it as even as I can - sending larger chunks can in the long run interfere with each other (as there is always 100mb/1gb limit, that can be hit even if average is below it) @RobertHarvey I've provided more code, I hope it's enough context – Pumar Apr 23 '16 at 17:03
  • Arguably this question is too broad, but it's been asked before and received the disparate answers one would expect for a "too broad" question. Please see the marked duplicate; pay particular attention to http://stackoverflow.com/a/4183205, which correctly points out that throttling UDP has problems that are solved using TCP. Peaks in your transmission rate are not necessarily a problem, as long as the overall network system is buffering instead of dropping packets. The bottom line is to just measure what you're sending, and don't send too much. – Peter Duniho Apr 23 '16 at 20:23
  • @PeterDuniho First of all, it's for IPTV purposes and no, TCP is not an option. I'm not here to argue which protocol is better - I have to adapt to what was created before. Peaks in my transmission rate is a problem I've come upon in my work - with around 800Mbps of this kind of transmission it started causing problems, and it's not buffer overflow - it's causing problems on routers with 1Gb/s ports. – Pumar Apr 24 '16 at 10:47
  • _"for now I want all the packets to leave in an even way"_ -- frankly, you have very little control over that. Your computer's network adapter has to negotiate with the next device down the chain (router, switch, whatever) and may have to buffer or discard datagrams if the network is overloaded. There's also inherent latency in the various components, much of which is non-deterministic in a non-real-time system like all the mainstream desktop OSs (including Windows). You can even out your own code's transmissions, but beyond that you're in the realm of network management, not programming. – Peter Duniho Apr 24 '16 at 18:53
  • @PeterDuniho any that's exactly what I want to do and have a problem with which resulting in me writing my question because I couldn't find similar one. Network management is another thing. I've tweaked my code a little by adding stop after every percent of packet per second which made packets more even but still I think I'm going the wrong way with thread.sleep/spinning. I will post my changes tomorrow. – Pumar Apr 24 '16 at 21:23

0 Answers0