Saturday, September 24, 2011

AirFlick in ruby

My AppleTV is still one of my favorite devices at home. As I usually watch movies once or twice, I usually don't bother putting them in iTunes. Therefore, I mostly use Airflick.
AirFlick is just a beautiful software: solves my problem, easy to use, reliable...but not open source and command line-based.
Last time I was visiting rubyflow, I noticed the airplay gem, gave it a try and found it working pretty well.
I then decided to build my own 100% ruby Airflick like with airplay and goliath gems. I tested only on OS X Lion, but I guess it should work on whatever platform the airflick and goliath gems are available.

Before starting, you need to install ruby 1.9 (required for goliath) and the following gems:
  • gem install goliath
  • gem install airplay

Then you can save the following script in a file called goliath.rb

require 'goliath'

require 'eventmachine'

require 'airplay'
require 'singleton'

# Singleton that deals with the file
class FilmLoader
include Singleton

attr_reader :mapping
attr_reader :size

def ensure_mapping_extension_is_present
@@fastfilereader ||= (require 'fastfilereaderext')
end
private :ensure_mapping_extension_is_present

# Limit ourselves to MP4 and M4V files
def contentType
case File.extname(@filename).upcase
when ".MP4"
return "video/mp4"
when ".M4V"
return "video/x-m4v"
end
""
end

def extname
File.extname(@filename)
end

def isValid?
File.exist?(@filename) and contentType.size > 0
end

def init(filename)
ensure_mapping_extension_is_present

@filename = filename

if isValid? then
@size = File.size(@filename)
@mapping = EventMachine::FastFileReader::Mapper.new(@filename)
else
puts "#{@filename} does not exist or is not a proper type"
exit
end
end
end

# The last parameter is the film filename
FilmLoader.instance.init(ARGV[-1])

# Goliath Plugin that will send the request to appletv
class AirPlayLauncher
def initialize(port, config, status, logger)
@port = port
end

def ip
# TODO: should be improved, is it cross platform?
adds = Socket.ip_address_list
adds.select { |x| x.ipv4? and x.ip_address!="127.0.0.1"}[0].ip_address
end

def run
# Wait for 2 seconds before sending the request to appletv
EM.add_timer(2) do
airplay = Airplay::Client.new
url = "http://#{ip}:#{@port}/film#{FilmLoader.instance.extname}"
airplay.send_video(url)
end
end
end

class AppleTVServer < Goliath::API

plugin AirPlayLauncher

def sendChunk(env, first, len)
# 32000 is the chunksize in bytes
chunklen = [len, 32000].min

chunk = FilmLoader.instance.mapping.get_chunk( first, chunklen )
env.stream_send( chunk )

if(chunklen < len) then
EM.next_tick { sendChunk(env, first + chunklen, len - chunklen) }
else
env.stream_close
end
end

def response(env)
if FilmLoader.instance.mapping then

if env["HTTP_RANGE"] == nil then
hash = {"Content-Type" => FilmLoader.instance.contentType,
"Accept-Ranges" => "bytes",
"Content-Length" => FilmLoader.instance.size.to_s }
[200, hash, "OK"]
else
# TODO: deal with other types of range:
# => -(\d+)
# => (\d+)-
m = /bytes=(\d+)-(\d+)/.match env["HTTP_RANGE"]

if m then
first = m[1].to_i
second = m[2].to_i
len = second - first + 1

EM.next_tick { sendChunk(env, first, len) }

range = "bytes #{first}-#{second}"
range <<"/#{FilmLoader.instance.size}"
env.logger.info range

hash ={"Content-Type" => FilmLoader.instance.contentType,
"Content-Range" => range,
"Accept-Ranges" => "bytes",
"Content-Length" => len.to_s }

env.logger.info hash
[206, hash, Goliath::Response::STREAMING]
else
[416, {}, "Range error:"+env["HTTP_RANGE"]]
end
end

else
[504, {}, "Could not map file"]
end
end
end


Let's say you want to play /Users/me/Downloads/movie.mp4, you just run the following in the Terminal:

ruby goliath.rb /Users/me/Downloads/movie.mp4

After a couple of seconds, the movie will start playing on your TV.

How does it work? First FilmLoader singleton, deals with the file mapping. Mapping is important to be able to quickly provide parts of the file when the server requires it.AirplayLauncher is using airplay to tell the appleTV where to request the file. Then the last part is AppleTVServer , the goliath API that sends chunks of the file according to the appleTV HTTP range requests.

I'm not really happy with AirplayLauncher.ip because I'm not convinced it is reliable or cross-platform. Your suggestions are more than appreciated.

The code only deals with mp4 and m4v movies right now because they are natively read by the appleTV. Why is command line-based so great? Because I can ssh from my iPad to my macbook and launch a movie in a couple of seconds.

Hope you'll enjoy this piece of code. I believe it is again a proof of ruby elegance (the script is less than 150 lines) and the great quality of ruby environment. Goliath web server is a really easy to use and powerful server.

Sunday, April 17, 2011

sopcast on AppleTv 2 with my macbook pro

Ever wanted to watch a soccer game with sopcast on your TV with your AppleTv 2 and your mac?
I've just managed to do it yesterday and thought it would be a good idea to explain the whole process.

Before we dive in the whole process, I'd like to thank Erica Sadun for her article on playing DVD on appletv and her excellent tool Airflick.
Secondly, I'd like to thank rfuilrez for his article about playing sopcast on OS X.
The following recipe is based on these 2 articles, you can have a look at them if you find my explanations not clear enough.

Like any good recipe, let's start with the ingredients. You'll need to download the following software before starting:

Start by installing VLC, Virtualbox, Keka and Airflick. I won't cover the installation of these 3 software in this article, they are straightforward.
If you have never used Airflick before, I suggest you play a little with it before going on, as you need to check if it works well with your AppleTV before starting.

I suggest you create a folder named sopcast and put sp-auth.tgz, libstdcpp5.tgz and Ubuntu_server_10-04-2.7z inside this folder.

Installing sopcast on Ubuntu server

  1. Unzip Ubuntu_server_10-04-2.7z with Keka, you should end up with a folder named Ubuntu server 10.04.2 that contains a few files.
  2. In folder Ubuntu server 10.04.2, double-click on Ubuntu server 10.04.2.vbox to launch Virtualbox with this image
  3. Start the image and log in (login: ubuntu, password: reverse)
  4. Check that linux is connected properly to your LAN (gets an IP with ifconfig address and can ping your OS X machine). If you encounter some problems, check the network settings of the image in Virtualbox (network adapter enabled, in bridge mode).
  5. Open a terminal on your OS X machine, and go to your sopcast folder
  6. Copy sopcast files to ubuntu server using scp: scp *.tgz ubuntu@UBUNTU_IP:/home/ubuntu where UBUNTU_IP is the IP address of your Ubuntu server
  7. Login to your Ubuntu server with ssh : ssh ubuntu@UBUNTU_IP and provide the password
  8. Execute ls and you should see files sp-auth.tgz and libstdcpp5.tgz
  9. Untar sp-auth.tgz: tar -zxvf sp-auth.tgz
  10. Untar libstdcpp5.tgz: tar -zxvf libstdcpp5.tgz
  11. Copy libstdcpp5 files to /usr/lib : sudo cp -v ./usr/lib/* /usr/lib
  12. Check if sopcast can be run: sp-auth/sp-sc-auth , it should show the usage message

Find a sopcast stream, basically it is an URL with the following form: sop://broker1.sopcast.com:3912/XXXX where XXX is the stream identifier (a large number).
Once you have find your stream, you can launch sopcast in your ssh shell with the following command:

sp-auth/sp-sc-auth sop://broker1.sopcast.com:3912/XXXX 3908 8908


You should see some text displayed, that means that the stream is being downloaded from your Ubuntu server and forward HTTP to port 8908

Run VLC and the apache server on OS X

  1. Run VLC, in the menu select File > Network Stream and enter http://UBUNTU_IP:8908
  2. After some time, you should see the sopcast video
If everything is fine, stop VLC and let's move on to showing the video on your AppleTv.
  1. On OS X, create a directory named stream in /Library/WebServer/Documents , it will contain the data that will be streamed to AppleTv
  2. Add two lines to file /etc/apache2/mime.types:
application/x-mpegURL m3u8
video/MP2T ts
  1. Enable OS X apache server by going to System Preferences>Sharing and checking web sharing
  2. On the right side on the System Preferences, you can see the http URL to access your server, remember it
  3. Enter the URL in Safari, you should see "It works" displayed in the browser
  4. Open a new Terminal window and launch the following: /Applications/VLC.app/Contents/MacOS/VLC -vv http://UNBUNTU_IP:8908 --intf=rc '--sout=#transcode{vcodec=h264,vb=2048,acodec=mp4a,ab=192}:standard{mux=ts,dst=-,access=file}' | mediastreamsegmenter -f /Library/WebServer/Documents/stream -D
  5. Open a new terminal window, go to /Library/WebServer/Documents/stream and type ls
  6. You should have file prog_index.m3u8 and several files with ts extension...so far so good

Send data to AppleTv

  1. Launch Safari and enter the http URL followed by /stream/prog_index.m3u8 , it should be downloaded properly (it is just a test)
  2. Launch Airflick and wait for it to detect your AppleTv
  3. Enter the URL of step 1 in the text box and press the Play button

If everything is fine, you can now watch your program on your TV. Enjoy!!

Sunday, June 13, 2010

Tactix posted on Apple Store

Today I posted my first Apple Store App.
It is a white board for sports coach to prepare their tactics and draw explain the players what they want during the game. It's pretty straightforward to use. It is an iPad only app, iPhone screens are too small for its purpose.
Feel free to post feedback, questions... in the comments.











Monday, June 9, 2008

Lua

Lua  is one of my favorites languages (Ruby being #1). For those who do not know Lua, I would describe it as a small language. Small meaning easy to learn, easy to embed and easy to extend; but still a very powerful language.

Some time ago, I used it at work. I work for a mobile phone company and the idea was to introduce widgets to the mobile world. My idea was to use Lua as the language to implement the widgets on the 2 low-end platforms we were using (Infineon  ULC2 and TI Locosto).
Integration of the language itself on the 2 platforms went extremely smoothly. We encountered a few issues on Infineon's because it does not support floating-point. Otherwise, it was integrated in a week or so.
Then, we started extending the language to call the features of the platform. We defined some API to access phonebook, send SMS, call a number as well provide display for text, images...
In the end (to our own surprise :-) ), we quickly managed to design widgets that could run perfectly on both platforms.
The project did not make it in a final product, but our proof of concept showed cross-platforms widgets were possible on low-end platforms by using Lua.

I believe the future of MMI development in the mobile phone industry is scripting, even if C and C++ are currently the leading languages. I noticed that Openmoko is pushing Python and I think they are completely right. There is no reason why a mobile phone MMI can not be written with languages such as Python, Lua, Perl or Ruby.

The only limitation I can think of is the final size of your software. Our widgets source code quickly ended up taking
a few kBytes as soon as we defined a proper MMI. It is clearly an issue to take into account if you are low on space.
We tried to compile the Lua scripts but the final size of the compiled script was often larger than the original script. One obvious solution is to compress the original script. It should work quite well as the scripts are plain text. However it is quite a burden to have to decompress the scripts on the fly.
If you have some suggestions on this issue, I'd be pleased to read them.

If you look for a small language that you want to embed in an application or a low-end chip to run scripts, Lua is a great solution. You can get results in a matter of weeks and given that it is a very powerful language, you will be able to go almost as far as you want.

Saturday, May 31, 2008

First post

Decided to start my blog today...everybody's having one, why not me, after all?
I guess I won't have much things to publish at first, but it should come with time.

What will I blog about? There are a couple things I'm interested in like Ruby language, computer vision,  mobile phones and new stuff in general.
I follow a few RSS feeds as well, so I will probably write about interesting posts in these as well.

That's all for this first post, we'll see where we head for the next one