Radius Radius - 23 days ago 8
C# Question

How to stream video from videogame to another computer in realtime?

I just started coding an application, my idea is to create something like StreamMyGame but for personal use, there is already a project called Single Player Game Transmiter that achieves some of this (http://sourceforge.net/projects/spgt/files/).

So far I'm not encoding nor streaming, I'm just capturing video and displaying it in my app window.

So how could I handle encoding/streaming? I was thinking an UDP stream of raw JPEGs would be the easiest path but I'm not sure.

Also how could I optimize what I have so far? It works fine for video playback but when capturing game windows it doesn't seem as snappy as the original video, I think it might be due to the fact that it's running in the background. That leads me to another question, how can I capture the Window with the specified title instead of the active Window?

https://github.com/fr500/desktop_streamer

EDIT:

Some tests with the current Capture Method:

Video Player Capture (960x720p)

starting benchmark
================================================
looking for window: test.mp4
looking for window: test.mp4
looking for window: test.mp4
looking for window: test.mp4
looking for window: test.mp4
================================================
starting single thread capture only test

Time Elapsed: 19480 milliseconds
Frame Time: 32 milliseconds
Rough FPS: 30
Sleeping 2 seconds

starting single thread capture and save as bmp test

Time Elapsed: 19768 milliseconds
Frame Time: 32 milliseconds
Rough FPS: 30
Sleeping 2 seconds

starting single thread capture and save as jpg test

Time Elapsed: 28593 milliseconds
Frame Time: 47 milliseconds
Rough FPS: 20
Sleeping 2 seconds

starting dual thread capture only test

Time Elapsed: 19515 milliseconds
Frame Time: 32 milliseconds
Rough FPS: 30
Sleeping 2 seconds

starting quad thread capture only test

Time Elapsed: 19481 milliseconds
Frame Time: 32 milliseconds
Rough FPS: 30
Sleeping 2 seconds


Crysis 2 Capture (1024x768p)

starting benchmark
================================================
looking for window: Crysis 2 (TM)
looking for window: Crysis 2 (TM)
looking for window: Crysis 2 (TM)
looking for window: Crysis 2 (TM)
looking for window: Crysis 2 (TM)
looking for window: Crysis 2 (TM)
looking for window: Crysis 2 (TM)
================================================
starting single thread capture only test

Time Elapsed: 20003 milliseconds
Frame Time: 33 milliseconds
Rough FPS: 29
Sleeping 2 seconds

starting single thread capture and save as bmp test

Time Elapsed: 20105 milliseconds
Frame Time: 33 milliseconds
Rough FPS: 29
Sleeping 2 seconds

starting single thread capture and save as jpg test

Time Elapsed: 17353 milliseconds
Frame Time: 28 milliseconds
Rough FPS: 34
Sleeping 2 seconds

starting dual thread capture only test

Time Elapsed: 19991 milliseconds
Frame Time: 33 milliseconds
Rough FPS: 30
Sleeping 2 seconds

starting quad thread capture only test

Time Elapsed: 19983 milliseconds
Frame Time: 33 milliseconds
Rough FPS: 30
Sleeping 2 seconds


Saving image to BMP doesn't really add any overhead but saving as JPG does which gives me an idea of the overhead encoding to video could have. Still the biggest issue is getting the frames themselves it's too slow as it is now, it can't keep up and as a result some frames are missing. If frames could be captured at 60+ fps the encoding + streaming delay could be really manageable for single player games.

I will try a DX hook approach to get the frames.

Answer

I'm not sure if performance/quality issues are effect of running in the background, however I'll try to answer your question.

Capturing window by its name

I suppose you can try to use FindWindow function instead of GetForegroundWindow that is used in the application. It allows you to get handle to a window by its title. In order to do this (in the desktop_streamer project you've posted a link to), go to the ScreenCapture class and:

  • change

    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();
    

    to

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
  • in Capture method change the line

    var foregroundWindowsHandle = GetForegroundWindow();
    

    to

    var foregroundWindowsHandle = FindWindow(null, "mywindowName");
    

I haven't tested this, so if you have any issues with running the code, let me know, so we can look for a solution.

For more information about FindWindow function, take a look here: http://www.pinvoke.net/default.aspx/user32.findwindow.

Performance and images quality

When talking about performance, you may want to try measure network speed and latency in real time and then use those information to adjust images quality (compression level and resolution) appropriately. This can make the transmitted image pixelated from time to time, but should decrease effect of laggy gameplay.

You may also want to take a look at this page giving some performance comparison of two of the screen capturing methods: http://blog.bobcravens.com/2009/04/fastest-screen-capture-using-c-vista-vs-win7/. It may help you improve te performance of screen capturing process, but as you can see, frame rates achieved are still too low to provide high quality gaming experience.