====== XBMC Media Center on Linux ====== [[http://xbmc.org/]] Xbox Media Center is an open-source "home theatre PC" project that was originally developed for Microsoft's first Xbox console (hacked to run Linux of course). It has since been ported to run on ordinary desktop Linux and even Windows and Mac. ===== Standlone Mode ===== *Challenge*: Get XBMC to run on a separate X-screen, isolated from my desktop environment. Made with reference to Ubuntu 9.10 (Karmic) for AMD64. ==== 1. X11: Separate X screens ==== - Install nVidia restricted drivers (login and let Karmic offer to do this for you, or install ''nvidia-glx'') - Fix the missing symlink: ''ln -s /usr/lib/nvidia/libnvidia-cfg.so.185.18.36 /usr/lib/libnvidia-cfg.so.1'' * [[https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers-180/+bug/414225|There's a bug on this]] - ''sudo service gdm stop'' to drop to a proper shell - Run ''sudo nvidia-xconfig -a --separate-x-screens --force-generate'' to create a new ''/etc/xorg.conf'' with both X screens - ''sudo X -wr'' to start X with a white region as the background (so you know it's worked!) - ''DISPLAY=:0.0 xeyes'' should display on one screen ... - ''DISPLAY=:0.1 xeyes'' should display on the other ... ==== 2. Sound: Pulseaudio at System Start ==== See also http://pulseaudio.org/wiki/SystemWideInstance - ''sudoedit /etc/default/pulseaudio'' * ''PULSEAUDIO_SYSTEM_START=1'' * ''DISALLOW_MODULE_LOADING=0'' - Start pulseaudio: ''sudo service pulseaudio start'' - Add yourself to the ''pulse-access'' group, or you'll get "Access denied": ''sudo adduser {your-username} pulse-access'' - Logout and back in to make the new group effective. No really. * Check effective groups with ''id'': * ''uid=1000(meermanr) gid=1000(meermanr) groups=4(adm),20(dialout),24(cdrom),46(plugdev),104(lpadmin),115(admin),118(pulse-access),120(sambashare),1000(meermanr)'' - Try playing something: ''DISPLAY=: paplay /usr/share/sounds/ubuntu/stereo/system-ready.ogg'' * You need the ''DISPLAY'' variable only if you are SSH'd in from another machine; pulseaudio tries to forward the sound over X. Isn't SSH awesome? ==== 3. XBMC: Installing and running ==== - Follow install instructions at [[https://launchpad.net/~team-xbmc/+archive/karmic-ppa]] - Run ''DISPLAY=0.1 xbmc --standalone'' ==== 4. TODO ==== * Prevent XBMC capturing the X keyboard and mouse - how will GDM use them? * Get GDM to run on *just* DISPLAY=:0.0 ===== Tips ===== - You can press "''\''" to toggle fullscreen mode ===== Library filename schemes ===== [[http://www.xbmc.org/wiki/?title=TV_Shows#TV_show_file_naming_conventions|TV Show file naming conventions]] TODO: Configure ''tvrenamer.pl'' (or complete ''tvrenamer.py'') for XBMC ===== Getting AC3 / DTS passthrough to work, despite pulseaudio ===== [[http://ubuntuforums.org/showpost.php?p=7560563&postcount=3]] While less than ideal, I managed to get AC3 / DTS to pass through pulseaudio, output on SPDIF and decoded by my amp by: * Putting pulseaudio's volume controls all on MAX * Change pulseaudio's default sampling rate to 48kHz * Telling my media player (XBMC) to use the "default" audio output when doing passthough (NOT "iec958" or "spdif"!) Because the volume is on 100%, the sound data is unchanged by pulseaudio and just "passes through" (so long as no other apps are producing sound, in which case the data gets garbled and my external decoder spits out static until the interfering app shuts-up). AC3 (Dolby Digital) is signed 16-bit little-endian 2-channel 48kHz PCM data (as far as we want pulseaudio and ALSA to be concerned), but the default pulseaudio sample-rate is 44.1kHz, so when an application attempts to connect to pulseaudio for passthrough pulse rejects the connection because of the sample-rate. You can change pulseaudio's default sample rate by creating ''~/.pulse/daemon.conf'' like so: default-sample-rate = 48000 You have to restart pulseaudio for this to take affect (restarting the PC will work as well). I happen to have the pulseaudio-utils package installed which provides the `pacmd` command, and I used it to verify my sample-rates before and after: # Before reboot $ pacmd list-sinks | grep sample sample spec: s16le 2ch 44100Hz sample spec: s16le 2ch 44100Hz # After reboot $ pacmd list-sinks | grep sample sample spec: s16le 2ch 48000Hz sample spec: s16le 2ch 48000Hz I've read that changing the default sample rate may cause audible crackling and pops when playing sound at the non-default rate, but I've not noticed any after making this change. ===== Wiimote controls ===== The default package was unusable when I tried it - tap an arrow key as fast as you like it would register as hit 4+ times: navigating menus was impossible. So I wrote my own. ==== Prerequisites ==== - XBMC, which comes with Python bindings by default - Python, which comes with Ubuntu by default - The "python-cwiid" package - A wiimote, obviously ==== Scripts ==== ''robmote.py'' #!/usr/bin/env python def translator(shutdown_event, q_wiimote, q_xbmcclient, log): log.info("Translator started") lookup = { "EVENT_CONNECTED": ["Notification(Wiimote Connected, Press + and - to disconnect, 3000)"], "EVENT_DISCONNECTED": ["Notification(Wiimote Disconnected, Press 1 and 2 to reconnect, 3000)"], "BTN_UP": ["Up"], "BTN_DOWN": ["Down"], "BTN_LEFT": ["Left"], "BTN_RIGHT": ["Right"], "BTN_A": ["Select"], "BTN_B": ["PreviousMenu"], "BTN_MINUS": ["OSD", "Fullscreen"], "BTN_HOME": ["ActivateWindow(home)"], "BTN_PLUS": ["Pause"], "BTN_1": ["Info"], "BTN_2": ["ContextMenu"], } while not shutdown_event.is_set(): try: event = q_wiimote.get(block=True, timeout=2.0) except: continue log.info(event) try: for action in lookup[event]: q_xbmcclient.put(action) except KeyError: pass q_wiimote.task_done() def xbmcclient(shutdown_event, q, log): import xbmc.xbmcclient from xbmc.defs import ICON_PATH import time log.info("XBMC client started") robmote = xbmc.xbmcclient.XBMCClient("Rob's Remote", "icon_wiimote.png") robmote.connect() robmote.send_notification("Connect Wiimote Now", "Press 1 + 2 on a Wiimote at any time to connect", "icon_wiimote.png") #time.sleep(3) # c.f http://xbmc.org/wiki/?title=List_of_Built_In_Functions #robmote.send_action("ActivateWindow(home)") while not shutdown_event.is_set(): try: action = q.get(block=True, timeout=2.0) except: continue print action if "(" in action: robmote.send_action(action) else: robmote.send_action(action, actiontype=xbmc.xbmcclient.ACTION_BUTTON) #q.task_done() robmote.close() import logging import multiprocessing import sys import select import wiimote log = multiprocessing.log_to_stderr() log.setLevel(logging.INFO) q_wiimote = multiprocessing.JoinableQueue(1) #q_xbmcclient = multiprocessing.JoinableQueue(1) q_xbmcclient = multiprocessing.Queue(1) shutdown_event = multiprocessing.Event() wiimote = multiprocessing.Process(name="Wiimote", target=wiimote.wiimote_session, args=(shutdown_event, q_wiimote, log)) translator = multiprocessing.Process(name="Translator", target=translator, args=(shutdown_event, q_wiimote, q_xbmcclient, log)) xbmcclient = multiprocessing.Process(name="XBMC client", target=xbmcclient, args=(shutdown_event, q_xbmcclient, log)) wiimote.start() translator.start() xbmcclient.start() select.select([sys.stdin], [], []) # Block until some input is ready log.info("Signalling shut down") shutdown_event.set() wiimote.join() translator.join() xbmcclient.join() ''wiimote.py'' #!/usr/bin/env python # Wiimote experiments. Hopefully this'll grow into something I can use with # wminput (or emulate it to the same affect) import os import sys import time import cwiid import Queue # Just for Queue.Full and Queue.Empty exceptions def wiimote_session(shutdown_event, q, log): # shutdown_event is a multiprocessing.Event which is initially not set. When it # is set the wiimote will be disconnected. # # q is a multiprocessing.JoinableQueue where messages will be put # # log is a logger to which messages will be written def disconnect(): q.put("EVENT_DISCONNECTED") if wiimote: wiimote.disable(cwiid.FLAG_MESG_IFC) wiimote.rpt_mode = 0 wiimote.close() q.join() while not shutdown_event.is_set(): wiimote = None log.info( "Press 1+2 on the Wiimote now..." ) while not wiimote: if shutdown_event.is_set(): return try: wiimote = cwiid.Wiimote() except Exception, e: log.warning( e ) log.warning( "(Ignoring previous exception)" ) time.sleep(0.5) log.info( "Successfully connected to Wiimote" ) q.put("EVENT_CONNECTED") wiimote.led = cwiid.LED1_ON | cwiid.LED2_ON | cwiid.LED3_ON | cwiid.LED4_ON wiimote.rumble = True time.sleep(0.4) wiimote.rumble = False wiimote.led = cwiid.LED1_ON | cwiid.LED4_ON q.join() log.debug( wiimote.state ) wiimote.enable(cwiid.FLAG_MESG_IFC) # "if changes" wiimote.rpt_mode = cwiid.RPT_BTN while wiimote: messages = wiimote.get_mesg() if shutdown_event.is_set(): disconnect() wiimote = None continue for mtype, m in messages: if mtype == cwiid.MESG_BTN: for x in dir(cwiid): if not x.startswith("BTN_"): continue if eval("cwiid.%s" % x) != m: continue q.put(x) q.join() break if m == cwiid.BTN_PLUS | cwiid.BTN_MINUS: log.info( "+ & -, exiting" ) disconnect() wiimote = None elif mtype == cwiid.MESG_ERROR: for x in dir(cwiid): if not x.startswith("ERROR_"): continue if eval("cwiid.%s" % x) != m: continue print x return else: for x in dir(cwiid): if not x.startswith("MESG_"): continue if eval("cwiid.%s" % x) != mtype: continue print x break if shutdown_event.is_set(): return def consumer(q): print "Starting consumption" while True: print q.get() sys.stdout.flush() q.task_done() if __name__ == "__main__": import logging import multiprocessing log = multiprocessing.log_to_stderr() log.setLevel(logging.INFO) q = multiprocessing.JoinableQueue(1) shutdown_event = multiprocessing.Event() wiimote = multiprocessing.Process(name="Wiimote", target=wiimote_session, args=(shutdown_event, q, log)) consumer = multiprocessing.Process(name="Consumer", target=consumer, args=(q,)) wiimote.start() consumer.start() #time.sleep(10) #print "Signalling shutdown event" #shutdown_event.set() wiimote.join() consumer.terminate() ==== Usage ==== meermanr@ikari:/store/projects/wiimote $ ./robmote.py [INFO/Wiimote] child process calling self.run() [INFO/Wiimote] Press 1+2 on the Wiimote now... [INFO/Translator] child process calling self.run() [INFO/Translator] Translator started [INFO/XBMC client] child process calling self.run() [INFO/XBMC client] XBMC client started # Pressed 1 + 2 ... [INFO/Wiimote] Successfully connected to Wiimote [INFO/Translator] EVENT_CONNECTED Notification(Wiimote Connected, Press + and - to disconnect, 3000) # Tested some buttons ... [INFO/Translator] BTN_UP Up [INFO/Translator] BTN_DOWN Down # Pressed + and - at the same time to turn off Wiimote.. [INFO/Translator] BTN_PLUS Pause [INFO/Wiimote] + & -, exiting [INFO/Translator] EVENT_DISCONNECTED [INFO/Wiimote] Press 1+2 on the Wiimote now... Notification(Wiimote Disconnected, Press 1 and 2 to reconnect, 3000) # Pressed to tell script to exit gracefully [INFO/MainProcess] Signalling shut down [INFO/Translator] process shutting down [INFO/Translator] process exiting with exitcode 0 [INFO/XBMC client] process shutting down [INFO/XBMC client] process exiting with exitcode 0 No wiimotes found [WARNING/Wiimote] Error opening wiimote connection [WARNING/Wiimote] (Ignoring previous exception) [INFO/Wiimote] process shutting down [INFO/Wiimote] process exiting with exitcode 0 [INFO/MainProcess] process shutting down ===== BBC iPlayer + YouTube ===== ==== 1. Install SVN Repo Installer plugin ==== cd ~/.xbmc/plugins/programs svn co "http://xbmc-addons.googlecode.com/svn/trunk/plugins/programs/SVN Repo Installer" ==== 2. Use SVN Repo Installer plugin ==== - Start XBMC - (Assuming default Project Midnight skin) Home Window -> Settings -> Skin Settings (side-bar) -> Home Windows (side-bar) -> Show programs in main menu - Home Window -> Programs -> Program plugins -> SVN Repo Installer -> xbmc-addons -> plugins -> video -> IPlayer - Home Window -> Programs -> Program plugins -> SVN Repo Installer -> xbmc-addons -> plugins -> video -> YouTube ==== 3. Use iPlayer / YouTube ==== - Home Window -> Video -> Video plugins -> etc ===== Playing Blurays ===== * [[http://forum.xbmc.org/showpost.php?p=484790&postcount=1|BluRay Player with MakeMKV]] * www.MakeMKV.com