Table of Contents

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

  1. Install nVidia restricted drivers (login and let Karmic offer to do this for you, or install nvidia-glx)
  2. Fix the missing symlink: ln -s /usr/lib/nvidia/libnvidia-cfg.so.185.18.36 /usr/lib/libnvidia-cfg.so.1
  3. sudo service gdm stop to drop to a proper shell
  4. Run sudo nvidia-xconfig -a –separate-x-screens –force-generate to create a new /etc/xorg.conf with both X screens
  5. sudo X -wr to start X with a white region as the background (so you know it's worked!)
  6. DISPLAY=:0.0 xeyes should display on one screen …
  7. DISPLAY=:0.1 xeyes should display on the other …

2. Sound: Pulseaudio at System Start

See also http://pulseaudio.org/wiki/SystemWideInstance

  1. sudoedit /etc/default/pulseaudio
    • PULSEAUDIO_SYSTEM_START=1
    • DISALLOW_MODULE_LOADING=0
  2. Start pulseaudio: sudo service pulseaudio start
  3. Add yourself to the pulse-access group, or you'll get “Access denied”: sudo adduser {your-username} pulse-access
  4. 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)
  5. 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

  1. Run DISPLAY=0.1 xbmc –standalone

4. TODO

Tips

  1. You can press “\” to toggle fullscreen mode

Library filename schemes

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:

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

  1. XBMC, which comes with Python bindings by default
  2. Python, which comes with Ubuntu by default
  3. The “python-cwiid” package
  4. 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 <ENTER> 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

  1. Start XBMC
  2. (Assuming default Project Midnight skin) Home Window → Settings → Skin Settings (side-bar) → Home Windows (side-bar) → Show programs in main menu
  3. Home Window → Programs → Program plugins → SVN Repo Installer → xbmc-addons → plugins → video → IPlayer
  4. Home Window → Programs → Program plugins → SVN Repo Installer → xbmc-addons → plugins → video → YouTube

3. Use iPlayer / YouTube

  1. Home Window → Video → Video plugins → etc

Playing Blurays