====== 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