Google TV是啥玩意 ?
![Google-tv-logo3-l](http://static.javashuo.com/static/loading.gif)
Google TV是支持自選圖像、寬帶網絡、傳統電視信號的綜合平臺,更附帶電視節目搜索功能. 谷歌公佈了其新版電視的兩個版本, 第一個叫作Buddy Box, 由索尼代工的電視盒而且價格昂貴, 第二個是即將發佈的集成電視, 將其電視盒內置到電視機內部.前端
Google TV界面預覽:node
開發者: 能夠爲Google TV開發新的網頁應用或者把已有的android應用改成適配大尺寸屏幕, 在谷歌的開發者網站能夠看到詳細介紹android
搭建咱們本身的Google TV
極客們就是喜歡重複發明輪子, 而且自得其樂. 因此咱們使用下列開源技術來搭建咱們本身的Google TV:ios
硬件:git
軟件:github
最終效果
樹莓派TV及其特殊的遠程遙控器web
步驟
主要分爲4步:shell
- 安裝軟件
- shell命令及腳本
- 搭建後臺: NodeJS + Express + Socket.io
- 搭建前端
安裝軟件:
安裝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 |
注意-1: 如今還沒法在樹莓派上用Chromium看youtube的視頻流, 由於在那種狀況下視頻並未經過GPU渲染, 會巨卡無比. Youtube-dl是不錯的替代方案, 先將視頻下載下來而後用OMXPlayer播放, 因爲用GPU渲染了視頻, 因此播放高清視頻比較順暢.
注意-2: Raspbian上默認就裝了OMXPlayer.
shell命令及腳本
若是你在用SSH鏈接樹莓派, 你須要先添加個環境變量「DISPLAY=:0.0″, 執行如下命令
執行如下命令可列出所有環境變量
在全屏模式下測試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", |
4 |
"scripts": { "start": "node app.js" }, |
5 |
"dependencies": { "express": "3.1.1", |
在建立並修改文件以後, 在應用目錄執行下列命令來安裝依賴.
注意-3: 在安裝依賴前會自動建立一個名爲node_modules 的文件夾, 若是你使用git, 別忘了要建立一個.gitignore文件並把「 node_modules」寫入其中, 在添加git項目時忽略這個文件夾.
新建一個名爲app.js的文件來建立咱們的本地HTTP訪問服務
01 |
var express = require( 'express' ) |
03 |
, server = require( 'http' ).createServer(app) |
04 |
, path = require( 'path' ) |
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' ))); |
12 |
app.get( '/' , function (req, res) { |
13 |
res.sendfile(__dirname + '/public/index.html' ); |
16 |
app.get( '/remote' , function (req, res) { |
17 |
res.sendfile(__dirname + '/public/remote.html' ); |
20 |
server.listen(app.get( 'port' ), function (){ |
21 |
console.log( 'Express server listening on port ' + app.get( 'port' )); |
上面已經配置了本地訪問的路徑. 如今咱們來測試一下搭建是否成功, 在public/目錄中建立index.html和remote.html文件, 寫入「Hello, World」, 而後執行命令行
或
注意-4: 要在
package.json文件中添加:
當服務正常啓動時會輸出"Express server listening on port 8080"
執行下列命令來測試咱們的「Hello, World」頁面
這是在後臺啓動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. 下面是其支持的傳輸協議:
- WebSocket
- Adobe® Flash® Socket
- AJAX long polling
- AJAX multipart streaming
- Forever Iframe
- JSONP Polling
把下列內容添加到app.js文件來整合Socket.io:
1 |
var express = require( 'express' ) |
3 |
, server = require( 'http' ).createServer(app) |
4 |
, path = require( 'path' ) |
5 |
, io = require( 'socket.io' ).listen(server) |
6 |
, spawn = require( 'child_process' ).spawn |
並添加如下內容下降日誌級別:
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) { |
5 |
io.sockets.emit( 'message' , data); |
服務端如今會在有新客戶端鏈接後發送消息「message」, 而後等待接收名爲「send」的事件來處理數據再回復全部鏈接的客戶端
在這裏咱們只有兩種類型的客戶端: 樹莓派的顯示器 (屏幕) 和移動Web應用 (遠程控制)
02 |
io.sockets.on( 'connection' , function (socket) { |
03 |
socket.on( "screen" , function (data){ |
04 |
socket.type = "screen" ; |
07 |
console.log( "Screen ready..." ); |
10 |
socket.on( "remote" , function (data){ |
11 |
socket.type = "remote" ; |
12 |
console.log( "Remote ready..." ); |
14 |
console.log( "Synced..." ); |
客戶端處理Socket通訊
在remote.html和index.html中添加下列內容:
1 |
< script src = "/socket.io/socket.io.js" > </ 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){ |
在Node服務器上執行Shell命令
Node容許咱們新建子進程來運行系統命令, 並監聽其輸入輸出. 還能給命令傳遞參數, 甚至能把一個命令的執行結果重定向給另外一個命令.
在NodeJS中執行shell命令的基本方法:
1 |
spawn( 'echo' ,[ 'foobar' ]); |
若是須要重定向輸出, 咱們須要把下列函數加到app.js文件中:
2 |
function run_shell(cmd, args, cb, end) { |
3 |
var spawn = require( 'child_process' ).spawn, |
4 |
child = spawn(cmd, args), |
6 |
child.stdout.on( 'data' , function (buffer) { cb(me, buffer) }); |
7 |
child.stdout.on( 'end' , end); |
添加OMXControl – 能夠控制OMXPlayer的Node模塊
我是偶然間在npmjs.org上發現能夠控制OMXPlayer的模塊!
把下列內容添加app.js文件中來使用這個模塊.
1 |
var omx = require( 'omxcontrol' ); |
這個模塊會爲咱們建立如下訪問路徑來控制視頻的播放:
1 |
http://localhost:8080/omx/start/:filename |
3 |
http://localhost:8080/omx/pause |
5 |
http://localhost:8080/omx/quit |
太TM帥氣鳥!
彙總
最終的app.js文件
04 |
var express = require( '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' ); |
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' ))); |
20 |
app.get( '/' , function (req, res) { |
21 |
res.sendfile(__dirname + '/public/index.html' ); |
23 |
app.get( '/remote' , function (req, res) { |
24 |
res.sendfile(__dirname + '/public/remote.html' ); |
27 |
io.set( 'log level' , 1); |
29 |
server.listen(app.get( 'port' ), function (){ |
30 |
console.log( 'Express server listening on port ' + app.get( 'port' )); |
33 |
function run_shell(cmd, args, cb, end) { |
34 |
var spawn = require( 'child_process' ).spawn, |
35 |
child = spawn(cmd, args), |
37 |
child.stdout.on( 'data' , function (buffer) { cb(me, buffer) }); |
38 |
child.stdout.on( 'end' , end); |
43 |
io.sockets.on( 'connection' , function (socket) { |
44 |
socket.on( "screen" , function (data){ |
45 |
socket.type = "screen" ; |
47 |
console.log( "Screen ready..." ); |