樹莓派搭建 Google TV

出處:http://my.oschina.net/funnky/blog/142067css


樹莓派搭建 Google TV

Image By http://korben.info/

Google TV是啥玩意 ?

Google-tv-logo3-l

Google TV是支持自選圖像、寬帶網絡、傳統電視信號的綜合平臺,更附帶電視節目搜索功能. 谷歌公佈了其新版電視的兩個版本, 第一個叫作Buddy Box, 由索尼代工的電視盒而且價格昂貴, 第二個是即將發佈的集成電視, 將其電視盒內置到電視機內部.前端

Google TV界面預覽:node

google_tv_preview

開發者: 能夠爲Google TV開發新的網頁應用或者把已有的android應用改成適配大尺寸屏幕, 在谷歌的開發者網站能夠看到詳細介紹android

搭建咱們本身的Google TV

極客們就是喜歡重複發明輪子, 而且自得其樂. 因此咱們使用下列開源技術來搭建咱們本身的Google TV:ios

硬件:git

軟件:github

  • Raspbian系統 – 爲樹莓派特殊定製的Debian發行版
  • NodeJs
    • Socket.io – 經過websocket遠程鏈接TV
    • Express – 用來處理一些基本的http請求
    • Omxcontrol – 用來控制樹莓派上最棒的視頻播放器OMXPlayer
  • Chromium瀏覽器
  • OMXPlayer
  • Youtube-dl – 一個下載youtube視頻的腳本
  • QuoJS – 在手機網頁上處理滑動手勢
  • HTML5, CSS3, Javascript, 和Moustache模板引擎
  • Youtube API

最終效果

raspberrypi_tv_google_tv

樹莓派TV及其特殊的遠程遙控器web

 

步驟

主要分爲4步:shell

  1. 安裝軟件
  2. shell命令及腳本
  3. 搭建後臺: NodeJS + Express + Socket.io
  4. 搭建前端

安裝軟件:

安裝Raspbian和NodeJS

按照這篇教程在樹莓派上安裝Raspbian和Node Js

安裝Chromium和Youtube-dl

安裝Chromium瀏覽器

1 sudo apt-get install chromium-browser

爲了顯示效果更佳咱們能夠安裝使用MC字體

1 sudo apt-get install ttf-mscorefonts-installer

安裝並升級Youtube下載器

1 sudo apt-get install youtube-dl
2  
3 sudo youtube-dl -U

注意-1: 如今還沒法在樹莓派上用Chromium看youtube的視頻, 由於在那種狀況下視頻並未經過GPU渲染, 會巨卡無比. Youtube-dl是不錯的替代方案, 先將視頻下載下來而後用OMXPlayer播放, 因爲用GPU渲染了視頻, 因此播放高清視頻比較順暢.

注意-2: Raspbian上默認就裝了OMXPlayer.

shell命令及腳本

若是你在用SSH鏈接樹莓派, 你須要先添加個環境變量「DISPLAY=:0.0″, 執行如下命令

1 export DISPLAY=:0.0

執行如下命令可列出所有環境變量

1 env

在全屏模式下測試Chromium:

1 chromium --kiosk http://www.google.com

測試Youtube-dl

1 youtube-dl youtube_video_url

你能夠給youtube-dl加幾個參數, 好比添加「-o youtube ID [dot] the extension」會自動更改下載文件的名稱,  「-f /22/18 」能夠強制下載視頻的720p版本. 這裏有完整的參數格式列表.

1 youtube-dl  -o "%(id)s.%(ext)s" -f /22/18 youtube_video_url

下載視頻完成後, 用OMXPLayer來播放

1 omxplayer youtube_video_file

能夠用鍵盤快捷鍵來暫停/恢復視頻, 更多快捷鍵說明看這裏

太棒了! 下面就讓咱們用Node JS來自動化實現上面的整個過程

搭建後臺: NodeJS + Express + Socket.io

下面是源碼的目錄結構:

  • public
    • js
    • css
    • images
    • fonts
    • index.html
    • remote.html
  • app.js
  • package.json

Package.json – npm用來自動安裝依賴的JSON文件, 並存儲了一些基本信息

1 { "name": "GoogleTV-rPi",
2   "version": "0.0.1",
3   "private": false,
4   "scripts": { "start": "node app.js" },
5   "dependencies": { "express": "3.1.1",
6                     "socket.io":"0.9.14",
7                     "omxcontrol":"*" }
8 }

在建立並修改文件以後, 在應用目錄執行下列命令來安裝依賴.

1 npm install

注意-3: 在安裝依賴前會自動建立一個名爲node_modules 的文件夾, 若是你使用git, 別忘了要建立一個.gitignore文件並把「 node_modules」寫入其中, 在添加git項目時忽略這個文件夾.

新建一個名爲app.js的文件來建立咱們的本地HTTP訪問服務

01 var express = require('express')
02   , app = express() 
03   , server = require('http').createServer(app)
04   , path = require('path')
05 // all environments
06 app.set('port', process.env.TEST_PORT || 8080);
07 app.use(express.favicon());
08 app.use(express.logger('dev')); app.use(express.bodyParser());
09 app.use(express.methodOverride());
10 app.use(express.static(path.join(__dirname, 'public')));
11 //Routes
12 app.get('/', function (req, res) {
13   res.sendfile(__dirname + '/public/index.html');
14 });
15  
16 app.get('/remote', function (req, res) {
17   res.sendfile(__dirname + '/public/remote.html');
18 });
19  
20 server.listen(app.get('port'), function(){
21   console.log('Express server listening on port ' + app.get('port'));
22 });

上面已經配置了本地訪問的路徑. 如今咱們來測試一下搭建是否成功, 在public/目錄中建立index.html和remote.html文件, 寫入「Hello, World」, 而後執行命令行

1 node app.js

1 npm start
注意-4: 要在   package.json文件中添加:
1 ...
2 "scripts": {
3         "start": "node app.js"
4     },
5 ...

當服務正常啓動時會輸出"Express server listening on port 8080"
執行下列命令來測試咱們的「Hello, World」頁面

1 node app.js &

這是在後臺啓動Node應用的最原始方法, 若是你熟悉node, 你能夠用Forever.js這樣的模塊來自動執行這項簡單的任務

咱們的Node應用如今已經在後臺啓動了, 執行下列命令用chromium在全屏模式下打開咱們的Hello, World頁面.

1 chromium --kiosk http://localhost:8080

添加Socket.io

我一直都認爲WebSocket是現代web的基礎, 對於Socket.io我認爲其意義重大

當AJAX剛興起的時候, 雖然很神往, 可是開發者總被不一樣瀏覽器處理異步JavaScript和XML請求時不一樣的方式所困擾. jQuery提供了統一的一組函數從而解決了這個噩夢. Socket.io對於WebSocket有一樣做用, 甚至更多!

爲了在全部瀏覽器上提供實時鏈接, Socket.IO會根據運行時選擇傳輸能力最強的方式, 且不須要修改API. 下面是其支持的傳輸協議:

  1. WebSocket
  2. Adobe® Flash® Socket
  3. AJAX long polling
  4. AJAX multipart streaming
  5. Forever Iframe
  6. JSONP Polling

把下列內容添加到app.js文件來整合Socket.io:

1 var express = require('express')
2   , app = express() 
3   , server = require('http').createServer(app)
4   , path = require('path')
5   , io = require('socket.io').listen(server)
6   , spawn = require('child_process').spawn

並添加如下內容下降日誌級別:

1 //Socket.io Config
2 io.set('log level', 1);

如今咱們的Socket.io就配好了, 但其尚未任何功能, 如今咱們要實現如何處理從客戶端發到服務端的消息和事件.

下面是實現服務端功能的方法, 對應的咱們還要實如今客戶端實現如何處理消息, 這會在下一章介紹.

1 io.sockets.on('connection', function (socket) {
2     socket.emit('message', { message: 'welcome to the chat' });
3     socket.on('send', function (data) {
4         //Emit to all
5         io.sockets.emit('message', data);
6     });
7 });

服務端如今會在有新客戶端鏈接後發送消息「message」, 而後等待接收名爲「send」的事件來處理數據再回復全部鏈接的客戶端

在這裏咱們只有兩種類型的客戶端: 樹莓派的顯示器 (屏幕) 和移動Web應用 (遠程控制)

01 var ss; //Socket.io Server
02 io.sockets.on('connection', function (socket) {
03     socket.on("screen", function(data){
04         socket.type = "screen";
05         //Save the screen socket
06         ss = socket;
07         console.log("Screen ready...");
08     });
09  
10     socket.on("remote", function(data){
11         socket.type = "remote";
12         console.log("Remote ready...");
13         if(ss != undefined){
14             console.log("Synced...");
15         }
16     });
17 )};

客戶端處理Socket通訊

在remote.html和index.html中添加下列內容:

1 <script src="/socket.io/socket.io.js"> </script>
2 <script>
3 //use http://raspberryPi.local if your using Avahi Service 
4 //or use your RasperryPi IP instead
5 var socket = io.connect('http://raspberrypi.local:8080');
6 socket.on('connect', function(data){
7     socket.emit('screen');
8 });
9 </script>

在Node服務器上執行Shell命令

Node容許咱們新建子進程來運行系統命令, 並監聽其輸入輸出. 還能給命令傳遞參數, 甚至能把一個命令的執行結果重定向給另外一個命令. 

在NodeJS中執行shell命令的基本方法:

1 spawn('echo',['foobar']);

若是須要重定向輸出, 咱們須要把下列函數加到app.js文件中:

1 //Run and pipe shell script output 
2 function run_shell(cmd, args, cb, end) {
3     var spawn = require('child_process').spawn,
4         child = spawn(cmd, args),
5         me = this;
6     child.stdout.on('data', function (buffer) { cb(me, buffer) });
7     child.stdout.on('end', end);
8 }

添加OMXControl – 能夠控制OMXPlayer的Node模塊

我是偶然間在npmjs.org上發現能夠控制OMXPlayer的模塊!
把下列內容添加app.js文件中來使用這個模塊.

1 var omx = require('omxcontrol');
2 //use it with express
3 app.use(omx());

這個模塊會爲咱們建立如下訪問路徑來控制視頻的播放:

1 http://localhost:8080/omx/start/:filename
2  
3 http://localhost:8080/omx/pause
4  
5 http://localhost:8080/omx/quit

太TM帥氣鳥!

彙總

最終的app.js文件

01 /**
02  * Module dependencies.
03  */
04 var express = require('express')
05   , app = express() 
06   , server = require('http').createServer(app)
07   , path = require('path')
08   , io = require('socket.io').listen(server)
09   , spawn = require('child_process').spawn
10   , omx = require('omxcontrol');
11 // all environments
12 app.set('port', process.env.TEST_PORT || 8080);
13 app.use(express.favicon());
14 app.use(express.logger('dev'));
15 app.use(express.bodyParser());
16 app.use(express.methodOverride());
17 app.use(express.static(path.join(__dirname, 'public')));
18 app.use(omx());
19 //Routes
20 app.get('/', function (req, res) {
21     res.sendfile(__dirname + '/public/index.html');
22 });
23 app.get('/remote', function (req, res) {
24     res.sendfile(__dirname + '/public/remote.html');
25 });
26 //Socket.io Congfig
27 io.set('log level', 1);
28  
29 server.listen(app.get('port'), function(){
30     console.log('Express server listening on port ' + app.get('port'));
31 });
32 //Run and pipe shell script output 
33 function run_shell(cmd, args, cb, end) {
34     var spawn = require('child_process').spawn,
35         child = spawn(cmd, args),
36         me = this;
37     child.stdout.on('data', function (buffer) { cb(me, buffer) });
38     child.stdout.on('end', end);
39 }
40 //Save the Screen Socket in this variable
41 var ss;
42 //Socket.io Server
43 io.sockets.on('connection', function (socket) {
44     socket.on("screen", function(data){
45         socket.type = "screen";
46         ss = socket;
47         console.log("Screen ready...");
48     });
相關文章
相關標籤/搜索