Console gaming on a remote X display

I have recently ended up in a situation where creating something like this (see diagram) would allow me to play my Game Cube on my laptop display before I finalise my purchase of a nice new LCD display for my desktop system.
Game Cube -> Tv Card -> Linux Box (no display) -> 100Base-t Network -> Laptop (with display)

gamecube-to-laptop

Playing a console game over a remote X display is slightly more challenging than simply watching TV. With TV you can capture your video and audio input, compress it, send it over the network in a nice format and decompress it at the other end. Sure, you’ll be watching TV that is maybe one second to a minute behind whats actually being broadcast but its in sync and it looks good. If you’re attempting to play a game in a similar fashion even 500ms (1/2 a second) delay would make most console games totally un-playable, with this in mind I set about console gaming over remote X with ssh.
It should be noted that:

  1. While attempting this I had no access to the internet, just to whatever documentation came with my debian / ubuntu installs
  2. My desktop runs debian testing and my laptop runs ubuntu edgy eft
  3. If you just want the solution, skip down to Attempt 4.

Attempt 1 - tvtime

Usually when playing Game Cube on a local display attached to my desktop I use a fantastic application called tvtime, To run this I SSH’ed from my laptop to my desktop with X display forwarding and compression enabled:

iain@laptop ~> ssh -X -C -o CompressionLevel=9 desktop

iain@desktop ~> tvtime

Unfortunately for me tvtime does not appear to work via X display forwarding, it appears to be looking for an xvideo port to connect to and fails upon startup, most likely tvtime was not designed for this.

Attempt 2 - xawtv

Xawtv is a tried and tested linux tv viewing tool, its almost guaranteed to work and it did:

iain@laptop ~> ssh -X -C -o CompressionLevel=9 desktop

iain@desktop ~> xawtv -remote

<meta content="StarOffice 7 (Solaris Sparc)" name="GENERATOR" /><meta content="20061124;15040900" name="CREATED" /><meta content="16010101;0" name="CHANGED" />After re-sizing xawtv to a fairly tiny window of roughly 320×240px the output was in real time, with practically no<style>!– @page { size: 8.27in 11.69in; margin: 0.79in } P { margin-bottom: 0.08in } –></style><span lang="en-GB"> perceivable</span> delay. Next I attempted to set xawtv to full screen, a big mistake. It upped the resolution of the tv window to 1024×768 (the resolution of my laptop display) and attempted to pipe all 786432 pixels per frame uncompressed across the network in real time, nice. The end result being a slide-show about 5 seconds behind real time, completely unplayable.</p> <p><strong>Attempt 3 - ffmpeg, mplayer, pipes & a network (madness)<br /> </strong></p> <p>After the failure of xawtv I had determined that the bottleneck was the network. I could see from my network / cpu utilisation graphs that the cpu was hardly getting used and the network was pegged at 100% usage. This made me think of compressing the video “on the fly” and then sending it over the network to my laptop - due to the latency involved in encoding this was not my greatest idea but getting it to work was an interesting experiment.</p> <p>It was obvious that I needed to keep the latency as low as possible, this meant getting as few components in my encoding -> network -> decoding chain as possible. After the reading of some man pages I realised that <a href="http://ffmpeg.mplayerhq.hu/">ffmpeg</a> and <a href="http://www.mplayerhq.hu/">mplayer</a> could encode and play from standard input / output, this sent the unix geek in me a little bit crazy. Even though the sane voice in my head was saying “this is stupid, its never going to work as you want it” but the unix geek in me simply retorted with “but wouldn’t it be cool if it worked!”. In the end unix geek won, it was a sad moment for my sanity.</p> <p>Some time and many many command line arguments later, it was working. By using ssh keys to avoid any input on my part I had managed to redirect the standard output of a remote ffmpeg command over ssh and into the standard input of mplayer, the command looks something like this…</p> <blockquote><p>iain@laptop ~> ssh desktop “/home/iain/src/ffmpeg-0.4.9-pre1/ffmpeg -vd /dev/video0 -an -f mpeg1video -aspect 4:3 -tvstd pal - 2> /dev/null” | mplayer -</p></blockquote> <p><meta content="text/html; charset=utf-8" http-equiv="CONTENT-TYPE" /><title /><meta content="StarOffice 7 (Solaris Sparc)" name="GENERATOR" /><meta content="20061124;15040900" name="CREATED" /><meta content="16010101;0" name="CHANGED" />Now, be warned this does actually work (as it should, of course) infact for standard tv this works really well, although its probably easier for one to use the ffserver tool which is bundled with ffmpeg (I did’nt for a number of reasons which I wont go into now). For playing games, it still does’nt work, there’s a constant 1/2 second or so delay. I assume this is how much ffmpeg needs in its buffer to encode the stream before sending it. I attempted a number of different codecs and resolutions; an mpeg2 transport stream at a dvd like resolution looks <style> <!-- @page { size: 8.27in 11.69in; margin: 0.79in } P { margin-bottom: 0.08in } --</style>particularly lovely but still suffers from the same delay.</p> <p><strong>Attempt 4 - mplayer, or what should really have been Attempt 3</strong></p> <p>After reading the <a href="http://www.mplayerhq.hu/">mplayer</a> manual (the parts useful to me anyway) I noticed that mplayer could play from <a href="http://www.linuxtv.org/">v4l2 (video for linux)</a> devices. Hoping that it would employ a more intelligent technique than xawtv when displaying in full screen mode I set about configuring mplayer. It is fairly easy to configure and work with once you’ve found the relevant options, I opted to put the options in a config file (usually located in ~/.mplayer/config) instead of typing them each time I wanted to use a v4l device. The entry in the config file looks something like this:</p> <blockquote><p>tv=driver=v4l2:input=2:width=256:height=192:device=/dev/video0:normid=1:noaudio=1:fps=30</p></blockquote> <p>As you can see, that’s going to play at a fairly small resolution (256×192) and limit the fps to 30 with no audio. The steps to run this are fairly similar to xawtv:</p> <blockquote><p>iain@laptop ~> ssh -X -C -o CompressionLevel=9 desktop</p> <p>iain@desktop ~> mplayer tv://</p></blockquote> <p>After mplayer had started and expecting the worst I tapped ‘f’ to put mplayer into full screen mode. Shockingly it worked, some how mplayer appears to get the remote x server to stretch the image to full screen size, keeping the source resolution fixed. This method works really well, I played about with some higher resolutions but the delay increased too much, 256×192 appears to be a happy medium. After several hours of gaming I’m happy to say that it is possible to play console games on a remote X display </p> <!-- <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"> <rdf:Description rdf:about="http://blog.buttermountain.co.uk/2006/11/24/console-gaming-on-a-remote-x-display/" dc:identifier="http://blog.buttermountain.co.uk/2006/11/24/console-gaming-on-a-remote-x-display/" dc:title="Console gaming on a remote X display" trackback:ping="http://blog.buttermountain.co.uk/2006/11/24/console-gaming-on-a-remote-x-display/trackback/" /> </rdf:RDF> --> </div><!-- END POST-ENTRY --> </div><!-- END POST-CONTENT --> </div><!-- END-CONTAINER --> <div class="post-header"> <h3 class="post-date">2006 11 24</h3> <p class="post-categories"><a href="http://blog.buttermountain.co.uk/category/linux/" title="View all posts in Linux" rel="category tag">Linux</a><br/> <a href="http://blog.buttermountain.co.uk/category/v4l/" title="View all posts in v4l" rel="category tag">v4l</a><br/> <a href="http://blog.buttermountain.co.uk/category/hacks/" title="View all posts in hacks" rel="category tag">hacks</a></p> <p class="post-comments"><a href="http://blog.buttermountain.co.uk/2006/11/24/console-gaming-on-a-remote-x-display/#comments" title="Comment on Console gaming on a remote X display">Comments (1)</a></p> <p class="post-permalink"><a href="http://blog.buttermountain.co.uk/2006/11/24/console-gaming-on-a-remote-x-display/" title="Permalink to Console gaming on a remote X display" rel="permalink">Permalink</a></p> </div><!-- END POST-FOOTER --> </div><!-- END POST --> <div class="navigation"> <div class="alignleft"></div> <div class="alignright"></div> </div><!-- END NAVIGATION --> </div><!-- END CONTENT --> </div><!-- END CONTAINER --> <div id="sidebar"> <ul> <li id="home-link"> <h2><a href="http://blog.buttermountain.co.uk/" title="Butter Mountain">Home</a></h2> </li> <li class="pagenav"><h2>Pages</h2><ul><li class="page_item"><a href="http://blog.buttermountain.co.uk/about/" title="About">About</a></li> </ul></li> <li id="category-links"> <h2>Categories</h2> <ul> <li><a href="http://blog.buttermountain.co.uk/category/apache/" title="View all posts filed under Apache">Apache</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/code/" title="View all posts filed under code">code</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/cooking/" title="View all posts filed under Cooking">Cooking</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/debug/" title="View all posts filed under debug">debug</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/django/" title="View all posts filed under Django">Django</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/entropy/" title="View all posts filed under entropy">entropy</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/france/" title="View all posts filed under France">France</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/gtk/" title="View all posts filed under gtk">gtk</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/hacks/" title="View all posts filed under hacks">hacks</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/hardware/" title="View all posts filed under hardware">hardware</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/junk/" title="View all posts filed under junk">junk</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/jython/" title="View all posts filed under jython">jython</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/linux/" title="View all posts filed under Linux">Linux</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/mysql/" title="View all posts filed under MySQL">MySQL</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/net-boot/" title="View all posts filed under Net Boot">Net Boot</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/networking/" title="View all posts filed under networking">networking</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/nontech/" title="View all posts filed under nontech">nontech</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/open-solaris/" title="View all posts filed under Open Solaris">Open Solaris</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/pxe/" title="View all posts filed under PXE">PXE</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/python/" title="View all posts filed under python">python</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/solaris/" title="View all posts filed under Solaris">Solaris</a> </li> <li class="current-cat"><a href="http://blog.buttermountain.co.uk/category/v4l/" title="View all posts filed under v4l">v4l</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/xml-rpc/" title="View all posts filed under xml-rpc">xml-rpc</a> </li> <li><a href="http://blog.buttermountain.co.uk/category/zfs/" title="View all posts filed under ZFS">ZFS</a> </li> </ul> </li> <li id="search"> <h2><label for="s">Search</label></h2> <form id="searchform" method="get" action="http://blog.buttermountain.co.uk/"> <div> <input id="s" name="s" type="text" value="" tabindex="1" size="10" /> <br/> <input id="searchsubmit" name="searchsubmit" type="submit" value="Find" tabindex="2" /> </div> </form> </li> </ul> </div> <div id="footer"> <p> © 2008 iain | Thanks, <a href="http://wordpress.org/" title="WordPress">WordPress</a> | <a href="http://www.plaintxt.org/themes/barthelme/" title="Barthelme for WordPress" rel="follow">Barthelme</a> theme by <a href="http://scottwallick.com/" title="scottwallick.com" rel="follow">Scott</a> | <!-- The following is a link to the theme author's sponsor. Please consider its smallness and that Scott was nice enough to make this theme publically available. --> Sponsor: <a href="http://www.active-sandals.com/womreefsan.html" title="Support: Reef Sandals">Reef Sandals</a> | Valid <a href="http://validator.w3.org/check?uri=http://blog.buttermountain.co.uk&outline=1&verbose=1" title="Valid XHTML 1.0 Strict" rel="nofollow">XHTML</a> & <a href="http://jigsaw.w3.org/css-validator/validator?uri=http://blog.buttermountain.co.uk/wp-content/themes/Barthelme/style.css&profile=css2&warning=no" title="Valid CSS" rel="nofollow">CSS</a> | RSS: <a href="http://blog.buttermountain.co.uk/feed/" title="Butter Mountain RSS 2.0 (XML) Feed" rel="alternate" type="application/rss+xml">Posts</a> & <a href="http://blog.buttermountain.co.uk/comments/feed/" title="Butter Mountain Comments RSS 2.0 (XML) Feed" rel="alternate" type="application/rss+xml">Comments</a> </p> </div> </div><!-- END WRAPPER --> <!-- Somehow 16 queries occured in -1.300 seconds. Magic! --> <!-- The "Barthelme" theme copyright (c) 2006 Scott Allan Wallick - http://www.plaintxt.org/themes/ --> <script src="http://www.google-analytics.com/urchin.js" type="text/javascript"> </script> <script type="text/javascript"> _uacct = "UA-2761551-1"; urchinTracker(); </script> </body> </html>