出处:http://my.oschina.net/funnky/blog/142067

树莓派搭建 Google TV

Google TV是啥玩意 ?

Google TV是支持自选图像、宽带网络、传统电视信号的综合平台,更附带电视节目搜索功能. 谷歌公布了其新版电视的两个版本, 第一个叫做Buddy Box, 由索尼代工的电视盒并且价格昂贵, 第二个是即将发布的集成电视, 将其电视盒内置到电视机内部.

Google TV界面预览:

开发者: 可以为Google TV开发新的网页应用或者把已有的android应用改为适配大尺寸屏幕, 在谷歌的开发者网站可以看到详细介绍

搭建我们自己的Google TV

极客们就是喜欢重复发明轮子, 并且自得其乐. 所以我们使用下列开源技术来搭建我们自己的Google TV:

硬件:

软件:

  • Raspbian系统
    – 为树莓派特殊定制的Debian发行版
  • NodeJs
    • Socket.io – 通过websocket远程连接TV
    • Express – 用来处理一些基本的http请求
    • Omxcontrol – 用来控制树莓派上最棒的视频播放器OMXPlayer
  • Chromium浏览器
  • OMXPlayer
  • Youtube-dl – 一个下载youtube视频的脚本
  • QuoJS – 在手机网页上处理滑动手势
  • HTML5, CSS3, Javascript, 和Moustache模板引擎
  • Youtube API

最终效果

树莓派TV及其特殊的远程遥控器

步骤

主要分为4步:

  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     });
49     socket.on("remote"function(data){
socket.type = 
"remote";
50         console.log("Remote
ready..."
);
51     });
52  
53     socket.on("controll"function(data){
54         console.log(data);
55         if(socket.type
=== 
"remote"){
56             if(data.action
=== 
"tap"){
57                 if(ss
!= undefined){
58                     ss.emit("controlling",
{action:
"enter"});
59                 }
60             else if(data.action
=== 
"swipeLeft"){
61                 if(ss
!= undefined){
62                     ss.emit("controlling",
{action:
"goLeft"});
63                 }
64             else if(data.action
=== 
"swipeRight"){
65                 if(ss
!= undefined){
66                     ss.emit("controlling",
{action:
"goRight"});
67                 }
68             }
69          }
70      });
71  
72     socket.on("video"function(data){
73         if(
data.action === 
"play"){
74             var id
= data.video_id,
75                 url
"http://www.youtube.com/watch?v="+id;
76             var runShell
new run_shell('youtube-dl',
77                 ['-o','%(id)s.%(ext)s','-f','/18/22',url],
78                 function (me,
buffer) {
79                     me.stdout
+= buffer.toString();
80                     socket.emit("loading",{output:
me.stdout});
81                     console.log(me.stdout)
82                 },
83                 function ()
{
84                     //child
= spawn('omxplayer',[id+'.mp4']);
85                     omx.start(id+'.mp4');
86                 }
87             );
88         }   
89  
90     });
91 });

搭建前端

树莓派TV前端屏幕显示样式:

关于如何编写这个前端的介绍超出了本教程的范围, 不过我想我会在不久之后发一些在开发中实用的小技巧.

在为大尺寸屏幕设计时, 你应当遵循一些设计上的考量, Google在其开发者网站上详述了一套他们的标准

树莓派TV远程控制端样式:

大部分远程控制端设计粗糙, 充满了样式丑陋的按钮, 所以我决定使用QuoJS, 现在变得又帅气又易用!

1 $$(".r-container").swipeLeft(function(){
2     socket.emit('control',{action:"swipeLeft"});
3 });

这是如何用”swipeLeft”方法把“Control”消息传回服务器的示例.

服务器会把这条消息传到屏幕上, 然后根据选择框的指向(Watch, Listen, Play)进行处理

这里还有几个小技巧能让你的web应用在iphone上看起来像原生应用一样带有好看的Icon和启动画面.

只需要把下列内容加到HTML的 <head></head>块中

1 <link rel="apple-touch-icon" href="images/custom_icon.png"/>
2 <link rel="apple-touch-startup-image" href="images/startup.png">
3 <meta name="viewport" content="width=device-width
initial-scale=1, maximum-scale=1, user-scalable=no"
 />
4 <meta name="apple-mobile-web-app-title" content="Remote">
5 <meta name="apple-mobile-web-app-capable" content="yes">

总结

这个项目仍在不断开发中, 不久之后便会有更新. 如果你喜欢本教程不妨上Github给项目加个星标. 视频也录好了!
请看here.

树莓派搭建 Google TV的更多相关文章

  1. 树莓派搭建WEB服务器

    树莓派搭建WEB的教程网上有许多,但感觉每一篇都有一些问题,这次我将网上的教程汇总,并亲身实践,将注意的问题都写进去,方便新手学习! 目录:1,安装nginx+sqlite+php5打造轻量级服务器, ...

  2. 搭建Google镜像网站

    很多人FQ或者买VPN账号仅仅只是为了使用Google搜索.相对于搭建VPN服务器来说,下面的方法搭建Google镜像网站将更加便捷. 条件:最好有自己的域名(可选),有可以正常访问Google的服务 ...

  3. 树莓派 Learning 002 装机后的必要操作 --- 05 给树莓派搭建“x86 + pi”环境 -- 安装**32位运行库** -- 解决`E:未发现软件包 xxx` 问题

    树莓派 装机后的必要操作 - 给树莓派搭建"x86 + pi"环境 – 安装32位运行库 – 解决E:未发现软件包 xxx 问题 我的树莓派型号:Raspberry Pi 2 Mo ...

  4. 树莓派搭建python环境服务器

    树莓派搭建python环境服务器 服务器结构大致为:django+uwsgi+nginx+python+sqlite 配置python环境 系统本身自带了python2.7和python3.5.在这里 ...

  5. 树莓派搭建钓鱼wifi热点

    我们连接的公共wifi其实是非常不安全的网络,骇客可以利用wifi路由设备进行中间人攻击,劫持DNS伪造钓鱼网站.接下来我会做个简单的实验,伪造中国电信的路由ChinaNet并发射出热点wifi等待别 ...

  6. 树莓派搭建Nexus2私服

    使用树莓派搭建Nexus2私服需要的材料有: 树莓派3B+(或者4B) 移动硬盘一个 1. 下载nexus2.x安装包 由于nexus2.x官方的启动环境并不支持arm架构的树莓派,所以这里采用tom ...

  7. 20-2 树莓派搭建服务器 Tornado Web服务器

    Drive.google.com/drive/folders/1ahbeoEHkjxoo4NV1wReOmpoRWbl448z- 1.Tornado简介 Tornado一款使用 Python 编写的, ...

  8. Django + Apache + 树莓派 搭建内网微信公众号服务器

    其实早在微信开放公众号开发平台时就想弄一个自己的公众号服务器,奈何对web服务器搭建和开发一窍不通,只是注册了一下开发者帐号,并没有采取行动,万恶的拖延症. 前一年,开始接触python,打开了神奇世 ...

  9. 【转】用树莓派搭建web服务器

    本文将详细介绍如何在树莓派上配置服务器,和<教你在Xubuntu上搭建LAMP服务器>有些类似,多了一些介绍在树莓派上的不同步骤的地方. 这种服务器的配置被称为LAMP,是最流行的服务器配 ...

随机推荐

  1. Python的基础知识01 _个人笔记

    1.快捷键:Alt+n 回到上一条语句>把上一条语句复制 Alt+p  去到下一条语句 2.Python 中不用“:”来表示一个语句 3.print("I Love you" ...

  2. redhat的systemd版本list

    acess https://access.redhat.com/errata/#/ https://github.com/lnykryn/systemd-rhel redhat 知识库 redhat ...

  3. Codeforces Round #471 (Div. 2)A. Feed the cat

    After waking up at hh:mm, Andrew realised that he had forgotten to feed his only cat for yet another ...

  4. 阿里云服务上面部署redis + 本地Redis客户端连接方法

    本文结合自己在阿里云服务器上面搭建redis服务器,在本地redis的客户端Redis Desktop Manager连接成功的操作,将操作中的一些方法做了一些归纳和总结,希望可以帮到有需要的同学. ...

  5. UVA227 - Puzzle(紫书习题3.5)

    #include <algorithm> #include <iostream> #include <cstdlib> #include <cstring&g ...

  6. [tyvj2054] 四叶草魔杖 (最小生成树 状压dp)

    传送门 Background 陶醉在彩虹光芒笼罩的美景之中,探险队员们不知不觉已经穿过了七色虹,到达了目的地,面前出现了一座城堡和小溪田园,城堡前的木牌上写着"Poetic Island&q ...

  7. Linux下常用函数-字符串函数

    inux下常用函数-字符串函数 atof(将字符串转换成浮点型数)  相关函数   atoi,atol,strtod,strtol,strtoul 表头文件   #include <stdlib ...

  8. 洛谷—— P1204 [USACO1.2]挤牛奶Milking Cows

    https://www.luogu.org/problem/show?pid=1204 题目描述 三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶.第一个农民在300秒(从5点开始计时)给他的牛挤奶, ...

  9. 面试宝典之基本的C#面试问答

    下文是100个基本的C#面试问答清单.这些面试问题简单.直接了当,涵盖了C#最基本的概念,大部分和面向对象的概念相关.所以如果你在准备C#面试,我建议你必须掌握这100个基本的C#面试问答来复习你的C ...

  10. [GraphQL] Apollo React Query Component

    In this lesson I refactor a React component that utilizes the graphql higher-order component to the ...