Butter Mountain Rotating Header Image

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<br /> <style>!-- @page { size: 8.27in 11.69in; margin: 0.79in } P { margin-bottom: 0.08in } --></style> <p><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<br /> <style> <!-- @page { size: 8.27in 11.69in; margin: 0.79in } P { margin-bottom: 0.08in } --</style> <p>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> </div> <p class="entry-meta"><span class="entry-categories">Posted in: <a href="http://blog.buttermountain.co.uk/category/linux/" title="View all posts in Linux" rel="category tag">Linux</a>, <a href="http://blog.buttermountain.co.uk/category/hacks/" title="View all posts in hacks" rel="category tag">hacks</a>, <a href="http://blog.buttermountain.co.uk/category/v4l/" title="View all posts in v4l" rel="category tag">v4l</a>.</span><br /> <span class="entry-tags"></span> </p> </div><!--.entry--> <div class="navigation"> <div class="navleft">← <a href="http://blog.buttermountain.co.uk/2006/11/13/disabled-parking/">Disabled Parking</a></div> <div class="navright"><a href="http://blog.buttermountain.co.uk/2007/03/17/fifty-things-an-englishman-may-learn-about-paris-or-france/">Fifty Things an Englishman may learn about Paris (or France)</a> →</div> <div class="clear"></div> </div> <!-- You can start editing here. --> <div id="comments"> <h3 class="comments-number">1 Comment on “Console gaming on a remote X display”</h3> <ol class="commentlist"> <li class="alt" id="comment-2"> <span class="comment-counter"><a href="#comment-2" title="Permalink to this comment" rel="nofollow">#1</a></span> <img alt='' src='http://www.gravatar.com/avatar/e5d0876101234d80ebceb38be0ca6a58?s=32&d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G' class='avatar avatar-32 photo' height='32' width='32' /> <span class="commentauthor">Yanster</span><br/> <span class="comment-meta"> on Dec 13th, 2006 at 3:32 pm</span> <div class="comment-content"> <p>Simple,</p> <p>buy wii and 42inch xd engine lg lcd </p> <p>thanks</p> <p>-ip</p> </div> </li> </ol> <!--reply form--> <h3 id="respond">Leave a Comment</h3> <form action="http://blog.buttermountain.co.uk/wp-comments-post.php" method="post" id="commentform"> <p><input type="text" name="author" id="author" value="" size="22" tabindex="1" /> <label for="author"><strong>Name</strong> (required)</label></p> <p><input type="text" name="email" id="email" value="" size="22" tabindex="2" /> <label for="email"><strong>Email</strong> (required) (will not be published)</label></p> <p><input type="text" name="url" id="url" value="" size="22" tabindex="3" /> <label for="url"><strong>Website</strong> (optional)</label></p> <!--<p><small><strong>XHTML:</strong> You can use these tags: <code><a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> </code></small></p>--> <p><textarea name="comment" id="comment" cols="100%" rows="10" tabindex="4"></textarea></p> <p><input name="submit" type="submit" id="submit" tabindex="5" value="Submit" /> <input type="hidden" name="comment_post_ID" value="18" /> </p> </form> </div><!--#comments--> </div><!--#primary--> <div id="secondary"> <div id="pp-subscribe" class="clearfix"> <ul class="clearfix"> <li id="pp-feed"> <a href="http://blog.buttermountain.co.uk/feed/" title="Subscribe to this Feed via RSS">Subscribe <span class="email-narrow">to our Feed</span> via RSS</a> </li> </ul> </div> <div id="pp-sidebars" class="clearfix"> <div id="sidebar-1" class="sidebar"> <ul class="xoxo sidebar-items"> <!--sidebar-1 widgets start--> <li class="widget"> <h2 class="widgettitle">Categories</h2> <ul> <li class="cat-item cat-item-6"><a href="http://blog.buttermountain.co.uk/category/apache/" title="View all posts filed under Apache">Apache</a> </li> <li class="cat-item cat-item-3"><a href="http://blog.buttermountain.co.uk/category/code/" title="View all posts filed under code">code</a> </li> <li class="cat-item cat-item-8"><a href="http://blog.buttermountain.co.uk/category/cooking/" title="View all posts filed under Cooking">Cooking</a> </li> <li class="cat-item cat-item-18"><a href="http://blog.buttermountain.co.uk/category/debug/" title="View all posts filed under debug">debug</a> </li> <li class="cat-item cat-item-19"><a href="http://blog.buttermountain.co.uk/category/django/" title="View all posts filed under Django">Django</a> </li> <li class="cat-item cat-item-15"><a href="http://blog.buttermountain.co.uk/category/entropy/" title="View all posts filed under entropy">entropy</a> </li> <li class="cat-item cat-item-7"><a href="http://blog.buttermountain.co.uk/category/france/" title="View all posts filed under France">France</a> </li> <li class="cat-item cat-item-17"><a href="http://blog.buttermountain.co.uk/category/gtk/" title="View all posts filed under gtk">gtk</a> </li> <li class="cat-item cat-item-11"><a href="http://blog.buttermountain.co.uk/category/hacks/" title="View all posts filed under hacks">hacks</a> </li> <li class="cat-item cat-item-13"><a href="http://blog.buttermountain.co.uk/category/hardware/" title="View all posts filed under hardware">hardware</a> </li> <li class="cat-item cat-item-2"><a href="http://blog.buttermountain.co.uk/category/junk/" title="View all posts filed under junk">junk</a> </li> <li class="cat-item cat-item-16"><a href="http://blog.buttermountain.co.uk/category/jython/" title="View all posts filed under jython">jython</a> </li> <li class="cat-item cat-item-9"><a href="http://blog.buttermountain.co.uk/category/linux/" title="View all posts filed under Linux">Linux</a> </li> <li class="cat-item cat-item-20"><a href="http://blog.buttermountain.co.uk/category/mysql/" title="View all posts filed under MySQL">MySQL</a> </li> <li class="cat-item cat-item-25"><a href="http://blog.buttermountain.co.uk/category/net-boot/" title="View all posts filed under Net Boot">Net Boot</a> </li> <li class="cat-item cat-item-14"><a href="http://blog.buttermountain.co.uk/category/networking/" title="View all posts filed under networking">networking</a> </li> <li class="cat-item cat-item-12"><a href="http://blog.buttermountain.co.uk/category/nontech/" title="View all posts filed under nontech">nontech</a> </li> <li class="cat-item cat-item-23"><a href="http://blog.buttermountain.co.uk/category/open-solaris/" title="View all posts filed under Open Solaris">Open Solaris</a> </li> <li class="cat-item cat-item-24"><a href="http://blog.buttermountain.co.uk/category/pxe/" title="View all posts filed under PXE">PXE</a> </li> <li class="cat-item cat-item-4"><a href="http://blog.buttermountain.co.uk/category/python/" title="View all posts filed under python">python</a> </li> <li class="cat-item cat-item-27"><a href="http://blog.buttermountain.co.uk/category/snowboard/" title="View all posts filed under snowboard">snowboard</a> </li> <li class="cat-item cat-item-21"><a href="http://blog.buttermountain.co.uk/category/solaris/" title="View all posts filed under Solaris">Solaris</a> </li> <li class="cat-item cat-item-1"><a href="http://blog.buttermountain.co.uk/category/uncategorized/" title="View all posts filed under Uncategorized">Uncategorized</a> </li> <li class="cat-item cat-item-10"><a href="http://blog.buttermountain.co.uk/category/v4l/" title="View all posts filed under v4l">v4l</a> </li> <li class="cat-item cat-item-5"><a href="http://blog.buttermountain.co.uk/category/xml-rpc/" title="View all posts filed under xml-rpc">xml-rpc</a> </li> <li class="cat-item cat-item-22"><a href="http://blog.buttermountain.co.uk/category/zfs/" title="View all posts filed under ZFS">ZFS</a> </li> </ul> </li> <li class="widget"> <h2 class="widgettitle">Archives</h2> <ul> <li><a href='http://blog.buttermountain.co.uk/2008/12/' title='December 2008'>December 2008</a></li> <li><a href='http://blog.buttermountain.co.uk/2008/10/' title='October 2008'>October 2008</a></li> <li><a href='http://blog.buttermountain.co.uk/2008/09/' title='September 2008'>September 2008</a></li> <li><a href='http://blog.buttermountain.co.uk/2008/05/' title='May 2008'>May 2008</a></li> <li><a href='http://blog.buttermountain.co.uk/2008/04/' title='April 2008'>April 2008</a></li> <li><a href='http://blog.buttermountain.co.uk/2008/03/' title='March 2008'>March 2008</a></li> <li><a href='http://blog.buttermountain.co.uk/2007/10/' title='October 2007'>October 2007</a></li> <li><a href='http://blog.buttermountain.co.uk/2007/09/' title='September 2007'>September 2007</a></li> <li><a href='http://blog.buttermountain.co.uk/2007/05/' title='May 2007'>May 2007</a></li> <li><a href='http://blog.buttermountain.co.uk/2007/03/' title='March 2007'>March 2007</a></li> <li><a href='http://blog.buttermountain.co.uk/2006/11/' title='November 2006'>November 2006</a></li> <li><a href='http://blog.buttermountain.co.uk/2006/09/' title='September 2006'>September 2006</a></li> </ul> </li> <!--sidebar-1 widgets end--> </ul> </div><!--#sidebar-1--><div id="sidebar-2" class="sidebar"> <ul class="xoxo sidebar-items"> <!--sidebar-2 widgets start--> <li class="widget"> <h2 class="widgettitle">Calendar</h2> <div id="calendar_wrap"> <table id="wp-calendar" summary="Calendar"> <caption>November 2006</caption> <thead> <tr> <th abbr="Monday" scope="col" title="Monday">M</th> <th abbr="Tuesday" scope="col" title="Tuesday">T</th> <th abbr="Wednesday" scope="col" title="Wednesday">W</th> <th abbr="Thursday" scope="col" title="Thursday">T</th> <th abbr="Friday" scope="col" title="Friday">F</th> <th abbr="Saturday" scope="col" title="Saturday">S</th> <th abbr="Sunday" scope="col" title="Sunday">S</th> </tr> </thead> <tfoot> <tr> <td abbr="September" colspan="3" id="prev"><a href="http://blog.buttermountain.co.uk/2006/09/" title="View posts for September 2006">« Sep</a></td> <td class="pad"> </td> <td abbr="March" colspan="3" id="next"><a href="http://blog.buttermountain.co.uk/2007/03/" title="View posts for March 2007">Mar »</a></td> </tr> </tfoot> <tbody> <tr> <td colspan="2" class="pad"> </td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td> </tr> <tr> <td><a href="http://blog.buttermountain.co.uk/2006/11/06/" title="Accessing Persistent Objects with Apache ws-xmlrpc 3.0 - A Documented Example">6</a></td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td> </tr> <tr> <td><a href="http://blog.buttermountain.co.uk/2006/11/13/" title="Disabled Parking">13</a></td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td> </tr> <tr> <td>20</td><td>21</td><td>22</td><td>23</td><td><a href="http://blog.buttermountain.co.uk/2006/11/24/" title="Console gaming on a remote X display">24</a></td><td>25</td><td>26</td> </tr> <tr> <td>27</td><td>28</td><td>29</td><td>30</td> <td class="pad" colspan="3"> </td> </tr> </tbody> </table> </div> </li> <li class="widget"> <h2 class="widgettitle">Pages</h2> <ul> <li class="page_item page-item-2"><a href="http://blog.buttermountain.co.uk/about/" title="About">About</a></li> </ul> </li> <li class="widget"> <h2 class="widgettitle">Meta</h2> <ul> <li><a href="http://blog.buttermountain.co.uk/wp-login.php">Log in</a></li> </ul> </li> <!--sidebar-2 widgets end--> </ul> </div><!--#sidebar-2--></div><!--#pp-sidebars--> </div><!--#secondary--> <div id="footer"> <p class="left">© 2009 <strong>Butter Mountain</strong> | Powered by <strong><a href="http://wordpress.org/">WordPress</a></strong></p> <p class="right">A <strong><a href="http://www.techtrot.com/primepress/" title="PrimePress theme homepage">WordPress theme</a></strong> by <strong><a href="http://www.techtrot.com" title="PrimePress author homepage">Ravi Varma</a></strong></p> </div><!--#footer--> </div><!--#container--> <div class="clear"></div> </div><!--#page--> </body> </html>