gkaindl / ambi-tv

a flexible ambilight clone for embedded linux
296 stars 85 forks source link

ambi-tv

A software framework to build an Ambilight clone based on an embedded Linux board (like Raspberry Pi), a video capture device and an RGB LED strip.

A demo video is available. It includes pictures of my hardware setup.

ambi-tv is based on the idea of using an HDMI splitter and an HDMI-to-Composite converter to tap into the image data displayed on your screen and processes it to drive the LEDs. This way, it works with any video source that you can connect to your screen, lifting the limitation of other systems that require the content to be displayed by a dedicated computer.

The ambi-tv software is designed in a way to be easily extensible and customizable.

A standalone ambi-tv device is extremely easy to build using a readily-available Raspberry Pi board.

Hardware Setup

ambi-tv only accesses hardware features through abstract kernel interfaces, so it should (in theory) work on any embedded Linux board that supports the necessary hardware features. However, since my own installation uses a Raspberry Pi, this will be the particular setup to be documented.

You will need the following hardware:

Assembling the hardware is easy, here's what I did:

Now connect the mini-USB power plug to the Raspberry Pi, then connect the extra headers as follows:

Raspberry Pi          Header
---------------------------------
P1/19 (MOSI)          LPD8806 DAT pin
P1/23 (SCLK)          LPD8806 CLK pin
P1/5                  Push switch side A
P1/6                  Push switch side B

Here is an illustration:

Raspberry Pi Wiring

Next, disconnect your main HDMI cable from your TV (the main cable should be the one coming from your A/V receiver. If you have multiple HDMI devices connected to your screen, I'd recommend buying an HDMI switch, so that you won't need to switch around any cables to use ambi-tv with all your devices).

Plug the main HDMI cable into the input of your HDMI splitter. Connect the screen to one output of the HDMI splitter, then connect the HDMI-to-Composite adapter to the other end. Connect the USB video grabber to the HDMI-to-Composite adapter and plug the grabber into the Raspberry Pi.

I used a strong double-sided adhesive tape to affix the RGB strip along the edges of my TV screen.

Finally, connect the power plug to the 5V adapter to power up the Raspberry Pi and RGB strip.

Software Installation

Clone the git repository on your Raspberry Pi and type make to build it. After the build process is finished, you will find the executable in bin/ambi-tv.

ambi-tv takes the following command line switches:

While ambi-tv is running, you can use the Space key to cycle between different programs defined in the configuration file, or use the t key to toggle between running and paused states. In paused state, all components are deactivated.

If you have a push button connected and enabled via command line argument, you can use single-click to toggle between running and paused states, or use a double-click to cycle between programs (e.g. the push button behaves quite similarly to the earplugs remote on iOS devices).

The repository contains a sample configuration file sample.conf that you can use or customize.

Be sure that the spidev driver is loaded when you start ambi-tv: modprobe spi-bcm2708.

Configuration File

ambi-tv uses a configuration file to customise available features and their settings. In the configuration file, you can create instances of ambi-tv components under an arbitrary name with an arbitrary set of options, as well as programs, which tie multiple components together.

A component is a part of ambi-tv's processing pipeline. It can either be a source (provides pixel data and timing), a processor (defines the behavior of an effect) or a sink (handles the interaction with the output LEDs). You can extend ambi-tv easily be making your own components. A component instantiation looks like this, and you can have multiple instantiations of the same component under different names with different settings:

component_name {
    name            instance_name
    settingA        valueA
    settingB        valueB
}

A program defines a set of component instances that should be active at the same time (typically one source, one processor and one sink instance). You can add as many programs as you like. A program definition looks like this (note the ampersand prefixes):

&program_name {
    activate        instance_nameA
    activate        instance_nameB
    ...
}

Have a look at the sample.conf to see how this works.

Components

Currently, ambi-tv includes the following components with their respective settings:

v4l2-grab-source: A video grabber source that uses video4linux 2 mmap()-based capture.

timer-source: A source that provides only timing information, but no video stream.

avg-color-processor: Calculates the average color of the entire video frame – This works nicely if the edge processor is too wild for you. It doesn't have any settings.

edge-color-processor: Calculates the colors around the edges of the video frame – This is the typical "ambilight" effect.

mood-light-processor: Doesn't require a video frame input and creates a nice "mood light" effect by mapping the HSL colorspace.

lpd8806-spidev-sink: An output sink that uses spidev to drive an LPD8806 RGB LED strip.

Extending ambi-tv

Due to the component-based architecture, ambi-tv can be extended quite easily by writing your own sources, processors and sinks. If you want to add your own color effects, have a look at component.h, as well as the existing components in the src/components directory.

The overall idea is that each component only needs to implement a certain set of functions, which are then exported via function pointers in a component structure. The configuration via the configuration file uses the int argc, char** argv mechanism you are probably very used to, so you can just use getopt_long with for it.

When you are done writing a component, you need to register it in registrations.c by adding it to the list there.

Once you re-compile ambi-tv, you can now instantiate your new component via the configuration file.

Video Formats

Supporting different video formats is also something you can do to the extend ambi-tv: To do so, have a look at video-fmt.c and add concrete implementations of the required functions for your new video format.

Right now, only YUYV (aka. YUV 4:2:2) is supported, because this is what my capture device uses.

Choosing a USB video grabber

Finding a USB video grabber that works on the Raspberry Pi isn't that easy, unfortunately, but here's what I use, so you can just replicate this.

I bought a cheap video grabber branded "EasyCAP", because I read on the internet that it is based on STK1160 chipset, which is well-supported under Linux. However, it turned out that the one I bought contains a different chipset branded fushicai usbtv, which doesn't not have a driver in the mainline kernel yet :-/

However, I found that somebody is currently working on a driver in the linux-next tree. At the time of writing, the driver only supports the NTSC input format. When I tested the driver, I found that there were a lot of artefacts with NTSC input, but it looked much better with PAL input. I have no idea why that is the case (probably because PAL uses only 25fps, whereas NTSC uses 30fps, and USB bandwidth is very limited on the Raspberry Pi).

Thus, I fired up a Windows VM and sniffed the USB protocol to find out how to enable PAL input the usbtv linux driver (which, as I've said, is under development and very rudimentary right now). I was happy to see that with PAL input, the usbtv driver works very well for me now!

I also found that I had to use raspi-config to overclock my Raspberry Pi to the highest settings in order to get a USB video stream without any artefacts at all. Thus, I added some passive heatsinks to the USB/Ethernet chipset and the Broadcom SoC on the Pi. I've had it running for days now, and the heatsinks don't even get particularly warm.

I've included the usbtv driver with my PAL patch in the misc directory, and while it's still unofficial, I'd actually recommend the fushicai grabber for ambi-tv, since the setup works extremely well for me. However, be aware that I claim absolutely no credit for the usbtv driver. I only took this off linux-next and hacked the PAL support into it.