Setup a MySQL database on Ubuntu

Install mysql-server

sudo apt-get -y update; sudo apt-get -y upgrade; sudo apt-get -y install mysql-server

After installation, mysql server daemon should already running automatically.

Modify /etc/mysql/my.cnf, make

bind-address = *

Grant Privilege

Login to mysql and grant privilege for remote connecters.

mysql -u root -p 
GRANT ALL ON *.* to user@'%' IDENTIFIED BY 'password'; 
sudo service mysql restart

Then you can connect to this server with the default port 3306 (make sure you firewall is open on this port).

This is not safe but quite convenient when testing, do at your own risk.

Build a Web Client with Nodejs: wireup AngularJs + Express + coffeescript + Jade + Less

廢話

有沒有這麼一個概念:「用同一套方式開發iOS、Android以及web?」隨著javascript的日益強大以及前端MVC的迅猛發展,這個概念變得越來越可行了。

一個完整的application通常包含web端以及mobile端,而web已經可以被視為iOS, Android以外的第三個「client」了。在技術的分類裡,iOS, Android, web backend的主流開發框架都已經是MVC了,惟獨剩下web frontend尚未完全「進化」成MVC。而近年來,前端MVC的優秀框架如雨後春筍般出現,像是AngularJs, Backbone, Ember …等,讓web frontend這塊紛亂之地也逐漸被MVC給攻克佔據…

一招MVC打天下,似乎不再遙不可及。

以前也是有開發過web,但在我的戰鬥經驗裡,web frontend的經驗值始終是初心者等級。現在既然有了MVC跟咖啡,是不是應該來練一下等級。用這麼酷的方式寫web我還是第一次,這些前端的MVC中,又以Google開發的angularjs最為火紅,(因為是Google大神開發的),有道是「站在巨人的肩膀上,才能看得更遠」不是嗎?所以這兩天便學了一下AngularJs,在此做個筆記~

先說說幾個開發時主要的component:

Express

Nodejs最火紅的套件之一,建立網站必備

Jade

類似rails的haml,擁有簡潔的語法,真的很簡潔,用過就會愛上,用來編譯成html

Less

類似rails的Sass,強化版的css語法,可以有變數,巢狀結構(這超重要),用來編譯成css

Coffeescript

Javascrip雖然強大,但其語法看就噁心,寫就想吐,debug更是會令人當場身亡! Coffeescript結合了ruby跟python的優點,是我看過語法最漂亮的程式語言,不只簡潔,而且有力,會幫你避掉許多Javascript語法設計不良的小毛病(例如global scope)。就像賽亞人+地球人=超強混血,沒什麼好講的,必備。

LiveReload

寫web最常做的事就是不停的reload reload re-re-re-re-reload頁面,re到手都快抽筋了,這個小工具可以讓生命更美好一點。它會在程式存檔的當下自動reload瀏覽器頁面(神奇吧),

AngularJs

前端主角,不解釋。

Notifier

Nodejs啟動時給一個桌面小提示,可含圖示跟聲音,不須切換視窗就能知道server啟動成功或失敗。 一樣是讓生命更美好的小工具

node-dev

一旦存檔就自動重啟nodejs server,搭配Notifier服用考試都100分。

bower

第三方js與css的套件管理程式,像是rails的gem,iOS的pod。意外嗎?一開始我也覺得意外,but… welcome frontend!

前面幾個component的setup在這篇文章有不錯的介紹,我就不打算贅述了,畢竟有好的輪子就不用再重新發明了是吧。(我決定繼承那篇文章然後結束這回合。)


檔案結構

一圖解千文

 /></p>
<h5>/api</h5>
<p>放跟api server相關的程式。為什麼有api server? 因為我想把web搞得像client,就像mobile一樣ㄎㄎ</p>
<h5>/bower_components</h5>
<p>存放用bower這個前端套件管理程式所安裝的第三方套件,brow會自動創建此資料夾</p>
<h5>/node_modules</h5>
<p>存放用npm這個後端套件管理程式所安裝的第三方套件,npm會自動創建此資料夾</p>
<h5>/routes</h5>
<p>routing的規則,由於是thin server,所以只做很簡單的routing,大部份的routing會在AnglarJs裡做,在AngularJs尚未建立之前,還是需要由server供給一些基本的啟動檔案</p>
<h5>/views</h5>
<p>jade檔的家</p>
<h5>/client</h5>
<p>frontend端相關的檔案,AngularJs及Less的東西會分別存放在coffee/與less/裡,最後會compile成js與css,分別放在/public/js與/public/css資料夾裡,程式每次修改都要重來一次。(不要覺得很恐怖,這些過程都會全自動)</p>
<h3>package.json</h3>
<p>新增一個package.json檔,npm預設會找到這個檔案並安裝其內容,<code>代表安裝最新版本

{
  "name": "theApp",
  "version": "0.0.1",
  "description": "...",
  "author": "tpy",
  "devDependencies": {
    "express": "3.2.6",
    "request": "~2.27.0",
    "jade": "*",
    "underscore": "*",
    "routes":"*",
    "coffee-script":"*",
    "node-notifier":"*",
    "less-middleware":"*",
    "connect-coffee-script":"*",
    "livereload":"*"
  }
}

然後

npm install

另外幾個比較特別的要安裝在系統比較方便,以便當指令用

sudo npm install node-dev bower -g 

node-dev

安裝完node-dev後新增一個檔案.node-dev.json,內容如下

{
  "coffee": "coffee-script/register",
  "ls": "LiveScript",
  "clear": true
}

這讓node-dev可以看懂coffee,不需要手動compile成js,寫咖啡必備

app.coffee

...

lessDir = __dirname + '/client/less'
coffeeDir = __dirname + '/client/coffee'
publicDir = __dirname + '/client/public'
bowerDir = __dirname + '/bower_components'

lessMiddleware = require 'less-middleware'
app.configure ()->
    app.set 'port', port
    app.set 'views', __dirname + '/views'
    app.set 'view engine', 'jade'
    app.use express.favicon()
    app.use express.logger('dev')
    app.use express.bodyParser()
    app.use lessMiddleware lessDir, 
        dest: publicDir,
        force: true, # development only, force re-compile in each request
        preprocess: 
            path: (pathname, req) ->
                return pathname.replace '/css/', '/'
        # without preprocess: get http://domain/css/app.css will look up lessDir/css/app.less
        # with preprocess: get http://domain/css/app.css will look up lessDir/app.less

    app.use require('connect-coffee-script')
        src: coffeeDir,
        dest: publicDir+'/js',
        prefix : '/js'
        force: true   # development only
        # without prefix: get http://domain/js/item.js will look up coffeeDir/js/item.coffee
        # with prefix: get http://domain/js/item.js will look up coffeeDir/item.coffee

    app.use express.static(publicDir)
    app.use('/bower_components',  express.static(__dirname + '/bower_components'));

...

新版的express已經抽離less的模組,所以需要額外require 'less-middleware'。less-middleware能讓你coding時寫less,並讓client端自動收到css。由於我們的程式存放目錄跟user request的url不同,所以要額外做一些設定。我們這邊用lessDir作為less的來源資料夾,並用publicDir作為他compile成css後目的資料夾。在預設的情況下,當request http://domain/css/app.css時,less-middleware會去找lessDir/css/app.less,但很顯然我們的目錄結構並非如此,所以我們用他的preprocess功能,把/css濾掉,這樣才會找到lessDir/app.less,然後他也很聰明的是,將app.less`` compile完之後會直接放到回目的資料夾裏的正確置,也就是publicDir/css“`。

同理connect-coffee-script也是。它能讓你coding時寫coffee並讓client自動收到js。他的檔案結構跟user request的路徑也不一樣,所以一樣要做額外設定。他們的原理大同小異,但設定機制有點不同,如有興趣可以去github看官方的說明。

最後宣布publicDir就是可以直接存取的靜態檔案,無需經過routing規則,也就是

app.use express.static(publicDir)

另外附帶一提,為了讓bower安裝的套件能更方便地被存取,再新增一個靜態檔案存取規則:

app.use '/bower_components', express.static(__dirname + '/bower_components')

當請求http://domain/bower_components/時,會對應到bowerDir/

為什麼要把事情搞得複雜?

這是一個好問題,如果把coffee/js,less/css都放在同一個地方事情會簡單許多,我通常也不喜歡事情變得複雜,但是在coding的時候維持一個好的檔案目錄結構是一件很重要的事,這會讓程式碼易於被理解,我認為目錄結構應該跟request url獨立開來。

全部通通放在public然後再用express去擋coffee跟less的請求也是個做法,但這樣"public"資料夾的存取規則就有了例外,而我討厭例外。

接著套入notifier, development only

server = app.listen port, ()->
    console.log 'Web Server Started at port: '+ port
    notifier.notify 
        'title': 'Web Server Start',
        'message': 'Port: ' + port, 
        icon: './ok.png',
        sound: true

再套入livereload, development only

livereload = require('livereload').createServer
    exts: ['jade', 'less', 'coffee']
livereload.watch(coffeeDir);
livereload.watch(lessDir);
livereload.watch(__dirname+'/views');

目前整個app.coffee看起來像這樣:

express = require 'express'
path = require 'path'
notifier = require 'node-notifier'
lessMiddleware = require 'less-middleware'

port = 8888
app = express()
lessDir = __dirname + '/client/less'
coffeeDir = __dirname + '/client/coffee'
publicDir = __dirname + '/client/public'
bowerDir = __dirname + '/bower_components'

app.configure ()->
    app.set 'port', port
    app.set 'views', __dirname + '/views'
    app.set 'view engine', 'jade'
    app.use express.favicon()
    app.use express.logger('dev')
    app.use express.bodyParser()
    app.use lessMiddleware lessDir, 
        dest: publicDir,
        force: true,
        preprocess: 
            path: (pathname, req) ->
                return pathname.replace '/css/', '/'

    app.use require('connect-coffee-script')
        src: coffeeDir,
        dest: publicDir+'/js',
        prefix : '/js'
        force: true


    app.use express.static(publicDir)
    app.use '/bower_components',  express.static(bowerDir)


app.configure 'development', () ->
    app.use express.errorHandler()


server = app.listen port, ()->
    console.log 'Web Server Started at port: '+ port
    notifier.notify 
        'title': 'Web Server Start',
        'message': 'Port: ' + port, 
        icon: './ok.png',
        sound: true

# LiveReload
livereload = require('livereload').createServer
    exts: ['jade', 'less', 'coffee']
livereload.watch(coffeeDir);
livereload.watch(lessDir);
livereload.watch(__dirname+'/views');

啟動server

node-dev app.coffee

ng-app

新增一個layout.jade至/views

doctype
html(ng-app='theApp')
    head
        meta(charset='utf8')
        base(href='/')
        title HellpApp
        link(rel='stylesheet' href='/css/app.css')
        script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.js')

    body
        block body

在html標籤宣告ng-app,讓AngularJs的作用範圍等同整個document頁面。至於必要的angular.js從google的cdn拿就可以了XD。

再新增一個item.jade至/views

extends layout
block body
    .item
    script(src='/js/config.js')

目前就只有一個空的div(class="item")。注意到jade的的layout跟view是可以分開的,這把layout.jade從view中抽出,讓相依性進一步降低,rails也有類似的機制。

最後在末端載入config.js,這裡面會定義整個ng-app module,新增一個/client/coffee/config.coffee

angular.module 'theApp',[]

這一行基本上宣告了一個名為theApp的module,後面搭配一個空陣列表示沒有用到其他額外的module。這邊的module可以理解成一個容器,將來許多angular的元件像是component, filter, service, …等都必須放置在這個容器裡,基本上就是一個top level的容器,之後一些必要的設定也會在此進行,所以我把檔名命名成config,也有很多人會把檔名命名成app,up to you。

ng-controller

新增一個/client/coffee/controllers.coffee

app = angular.module 'theApp' #注意後面沒有array參數
class ItemController
    constructor: ($scope)->
        item = {title:'cup', photos:['1.jpg','2.jpg']}
        item.photos = (@getS3ImageUrl(photo) for photo in item.photos)
        $scope.item = item
    getS3ImageUrl: (imageName) ->
        url = 'http://images.theApp.com/'+ imageName

app.controller 'ItemController', ItemController

首先

app = angular.module 'theApp'

把剛剛在config.coffee定義好的theApp module拿出來,而

app.controller 'ItemController', ItemController

則把這個ItemController放進容器裡。

constructor裡有一個$scope參數,這是angular內建的service,angular會在instantiate物件時自動把所需的內建service餵進去。所有$開頭的變數都是angular的內建service。這個$scope代表的是一個DOM標籤的scope,例如

&lt;div ng-controller='ItemController'>
    ...
&lt;/div>

這個$scope就代表整個div,然後$scope.item = ...就表示我們在這個div的範圍裡定義了一個名為item的angular變數。這邊有完整的service列表。

然後

(@getS3ImageUrl(photo) for photo in item.photos)

用@getS3ImageUrl對item.photos做一點加工,並回傳一個新的array賦值回item.photos。這是coffee從python借來的list comprehension,酷吧! 其中@getS3ImageUrl的意思是this.getS3ImageUrl。

最後還有一個地方要修改,angular用關鍵字比對去認出那些內建的service像是$scope,但是在minify js檔案的時候變數名稱很可能都會被更動,如此便導致angular找不到關鍵字,解決這個問題的方式,就是我們同時把這個關鍵字也宣告成字串,這樣minify時就不會去動到它了。

app.controller 'ItemController', ItemController

改成

app.controller 'ItemController', ['$scope',ItemController]

現在我們有了item,可以在頁面中呈現資訊了,修改/views/item.jade

extends layout
block body
    .error(ng-hide="item")
    .item(ng-show="item" ng-controller='ItemController')
        .title {{item.title}}
        img(ng-repeat="photoUrl in item.photos" ng-src="{{photoUrl}}" style="margin: auto;")
    script(src='/js/config.js')
    script(src='/js/controllers.js')

首先link到controller.js,ItemController才會有定義,我們已經在ItemController裡定義了$scope.item,所以這邊就可以透過兩個大括號去access它{{item}}

這邊我們看到幾個用法

ng-hide

顧名思義,item存在時就這個tag就hide

ng-show

item存在時就這個tag就不hide,很ng-hide組合搭配很好用,能夠輕易地控制頁面內容,無需手動jQuery操作DOM

ng-repeat

這個ng-repeat一樣是用了跟coffee極度類似的list comprehension表示式,咖啡懂了這個就懂了。順帶一提ng-repeat跟ng-src不一定要在同一個tag,例如也可以這樣

ul(ng-repeat="photoUrl in item.photos")
    li img(ng-src="photoUrl")

Apple Push Notification Service (APNS): How your server push notifications to your apps?

Say, you have your own server, you want to push notifications to your apps, how to do that? Well, basically it should be easy because Apple do that for you by Apple Push Notification Service (APNS). But it’s not quite easy enough because the authentication process is cumbersome.

It’s all about authentication

Your Server -> APNS -> Your iOS apps

Under your provisioning profile, you have an App ID, if you want to enable the Push Notification, you need to create a certificate for it.

This is the way you do:

  • Create public/private key.
  • Create certificate signing request (CSR)
  • Upload the CSR to Apple to create certificate
  • Download the certificate
  • Convert the certificate to cert.pem
  • Convert private key to key.pem
  • Feed key.pem and cert.pem to your server
  • Get device token and upload it to your server
  • Start push notification in your server

CSR vs Certificate:

CSR is a request that you tell Apple you wan to get a certificate. You signed on it (with private key) first, submit to Apple and after Apple agrees your request, he generates a certificate and signed on it and give it to you.

Basically, a certificate is just like a paper that signed with you and Apple. It is a registration of your private key and your private key somehow represent yourself. It’s all about an identity of YOU.

Convert the certificate to cert.pem

In keychain access, find the certificate named “Apple Development iOS Push Server: appID", export (cmd+shift+E) to dev_cert.p12.

openssl pkcs12 -clcerts -nokeys -in dev_cert.p12 -out dev_cert.pem

Convert private key to key.pem

In keychain access, export the private key associated with the certificate to dev_key.p12.

openssl pkcs12 -nocerts -out dev_key.pem -in dev_key.p12

And make it unencrypted:

openssl rsa -in dev_key.pem -out dev_key.unencrypted.pem

The Client Side

The client side is relatively easy: just tell iOS you want to register remote notification and implement some delegate functions.

[application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert| UIRemoteNotificationTypeBadge |  UIRemoteNotificationTypeSound)];

This method will show up an alertView asking the user to agree with your push notification request, if the user disagree, you’ll get no tokens.

# pragma mark - Remote Notification Delegate
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    NSString *key = [[[userInfo objectForKey:@"aps"] objectForKey:@"alert"] objectForKey:@"loc-key"];
    NSString *soundFile = [[userInfo objectForKey:@"aps"] objectForKey:@"sound"];
    DLog(@"didReceiveRemoteNotification: %@",key);
}

// Once successfully received the token, you should send the token to your sever and stores it. Here is just a demo.
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
    NSString* tokenString = [[[NSString stringWithFormat:@"%@",deviceToken] stringByReplacingOccurrencesOfString:@" " withString:@""]substringWithRange:NSMakeRange(1, 64)];
    DLog(@"%@",tokenString);
}

-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{

}

A very nice ref: iOS用戶端的APNS服務簡介與實現

And some nice refs that are helpful to understand the concept:

Fastest: Nginx + WordPress

Fastest Approach to Build a Nginx + WordPress

Recently, I found this nice repo, which is a linux script helping you build a website based on nginx/php/mysql/wordpress (or non-wordpress). It’s awesome, quick and easy. To see how awesome it is:

wget -qO ee rt.cx/ee && sudo bash ee     # install easyengine
ee site create example.com --wp          # Install required packages & setup WordPress on example.com

Live 1080p video streaming from the Raspberry Pi to browsers using nginx and rtmp

Gstremer + RTMP + Browser

This approach is mainly referenced from here except that I use gstraemer as streaming/encode provider instead of ffmpeg. Gstreamer is an awesome media framework for streaming service. It is not only powerful but also flexible. The plugin-based architecture makes it very flexible in any situations, from codecs to communication protocols.

Install Gstreamer

Gstreamer are composed by a gstreamer core and some plugins libraries. Installation please reference this post. Notice that we’re going to use gstreamer’s rtmp module, which is contained in gst-plugins-bad, you must make sure that you have librtmp-dev installed before you compile gst-plugins-bad.

Install Nginx & Nginx-rtmp module

While gstreamer are good at video encoding and streaming, it’s not probably the best server solution facing the public. We need nginx to re-transmit the rtmp packets to allow multiple client connections.

sudo apt-get -y install nginx
sudo apt-get -y remove nginx
sudo apt-get clean

Make sure /etc/nginx is empty because make install will not overwrite files which already exists.

sudo rm -rf /etc/nginx/* 

Make a temporary folder to build codes

mkdir -p ~/nginx_src
cd ~/nginx_src
git clone https://github.com/arut/nginx-rtmp-module.git
git clone https://github.com/nginx/nginx.git
apt-get update
apt-get install -y curl build-essential libpcre3-dev libpcre++-dev zlib1g-dev libcurl4-openssl-dev libssl-dev
sudo mkdir -p /var/www
cd nginx
./configure --prefix=/var/www --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_ssl_module --without-http_proxy_module --add-module=~/nginx_src/nginx-rtmp-module 
make -j2  # if you ever faced "memory exhausted" error, try use "make" instead
sudo make install

Check nginx version and start server.

nginx -v
sudo service nginx start

Setting nginx

Edit nginx.conf user root; #Root is only OK if the server is not public. Otherwise you need to increase security on your own. # user www-data; #use up to 4 processes if you expect a lot of traffic. But this causes issues with rtmp /stat page and possibly pushing/pulling

 # worker_processes 4;
 worker_processes 1;

 events {  
   worker_connections 1024;  
 }

 http {  
      include /etc/nginx/mime.types;  
      default_type application/octet-stream;  
      sendfile on;  
      keepalive_timeout 65;  
      #if you want gzip enabled  
      #gzip on;  
      #gzip_disable "msie6";

 server {  
     listen    80;  
     server_name localhost;

     # sample handlers  
     #location /on_play {  
     #  if ($arg_pageUrl ~* localhost) {  
     #    return 201;  
     #  }  
     #  return 202;  
     #}  
     #location /on_publish {  
     #  return 201;  
     #}  
     #location /vod {  
     #  alias /var/myvideos;  
     #}  
     # rtmp stat  
     location /stat {  
       rtmp_stat all;  
       rtmp_stat_stylesheet stat.xsl;  
     }

     location /stat.xsl {  
       # you can move stat.xsl to a different location  
       root /home/pi/nginx_way/nginx-rtmp-module;  
     }

     # rtmp control  
     location /control {  
       rtmp_control all;  
     }  
     error_page  500 502 503 504 /50x.html;  
     location = /50x.html {  
       root  html;  
     }  
   }  
 }  
 rtmp {  
   server {  
     listen 1935; 
     #chunk_size 8192;
     ping 30s;  
     notify_method get;  
     application rtmp{  
       live on;

       # You can push this stream to an external rtmp server while accessible locally.
       # If you experience artefacts and delays on external server lower the bitrate.
       # There seems to be a bug. When watching local stream and pushing to remote, the remote
       # stream become really weird with random blocks and strange shadows.(consider 1 one for now)
       # push rtmp://ip-address-external-rtmp/app/stream;  

       # sample play/publish handlers  
       #on_play http://localhost:80/on_play;  
       #on_publish http://localhost:80/on_publish;  
       # sample recorder  
       #recorder rec1 {  
       #  record all;  
       #  record_interval 30s;  
       #  record_path /tmp;  
       #  record_unique on;  
       #}  
       # sample HLS  
       #hls on;  
       #hls_path /tmp/hls;  
       #hls_sync 100ms;

      #This will start/stop our ffmpeg script and camera stream (thanks #towolf)
      #exec gst-launch-1.0 rpicamsrc bitrate=2000000 rotation=90 ! video/x-h264,width=640,height=480,framerate=20/1 ! h264parse ! flvmux ! rtmpsink location='rtmp://localhost:1935/rtmp/live live=1';

      #exec_static ~/rtmp-nginx.sh;
      #exec_kill_signal term;

     }  
     # Video on demand  
     #application vod {  
     #  play /var/Videos;  
     #}  
     # Video on demand over HTTP  
     #application vod_http {  
     #  play http://localhost:80/vod/;  
     #}  
   }  
 }  

Restart server

sudo service nginx restart 

Install flash rtmp player: StorbeMediaPlayback

For client side playback, I use strobe media playback which is an open source media framework from adobe. It seems that it’s not maintained since 2012, but it works great!

cd /var/www/html
wget http://sourceforge.net/projects/smp.adobe/files/Strobe%20Media%20Playback%201.6%20Release%20%28source%20and%20binaries%29/
sudo unzip StrobeMediaPlayback_1.6.328-full.zip
sudo cp -R StrobeMediaPlayback_1.6.328-full/for Flash Player 10.1/ ../strobe

Some helpful video tutorial here

setup index.html

Edit /var/www/html/index.html and modify {pi_address} to valid address

<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<!-- strobe -->
<script type="text/javascript" src="strobe/lib/swfobject.js"></script>
<script type="text/javascript">
    var parameters = {  
         src: "rtmp://{pi_address}/rtmp/live",  
         autoPlay: false,  
         controlBarAutoHide: false,  
         playButtonOverlay: true,  
         showVideoInfoOverlayOnStartUp: true,  
         optimizeBuffering : false,  
         initialBufferTime : 0.1,  
         expandedBufferTime : 0.1,  
         minContinuousPlayback : 0.1,  
         poster: "images/poster.png"  
    };  
    swfobject.embedSWF(
        "strobe/StrobeMediaPlayback.swf"
        , "StrobeMediaPlayback"
        , 1024
        , 768
        , "10.1.0"
        , "strobe/expressInstall.swf"
        , parameters
        , {
            allowFullScreen: "true"
        }
        , {
            name: "StrobeMediaPlayback"
        }
    );
</script>


</head>
<body>
<div id="StrobeMediaPlayback"></div>
</body>
</html>

Start Streaming Pipe video stream from gstreamer to nginx by rtmp

gst-launch-1.0 rpicamsrc bitrate=4000000 rotation=90 ! video/x-h264,width=1920,height=1080,framerate=25/1 ! h264parse ! flvmux ! rtmpsink location='rtmp://localhost/rtmp/live live=1'

Open browser and visit

http://{pi_address}

Status Monitor

http://{pi_address}/status

Demo (OSX): Open Chrome, Safari, Firefox at the same time.

Screen Shot 2014-09-16 at 下午5.06.02

Backup everythin Here’s a very

nice post for backing up SD card as an image.

讓brew更方便一點

OSX的套件管理工具brew可以說是相當方便的工具,brew有時候(or always?)會下載source code來build,放在他自己的資料夾/usr/local/Cellar/,然後再link到系統目錄(通常包括但不限於/usr/local/opt/)。

但是就在link這個階段,常常需要sudo權限去寫系統目錄,而brew本身為了安全考量,不能直接使用sudo brew,除非brew執行檔的擁有者是root。這樣安全歸安全,但每次link都要改目錄(而且很可能不只一個)的寫入權限,link完再改回來,實在是很不方便。

如果你跟我一樣也喜歡簡單一點的人生,就把brew擁有者改成root吧! (At your own RISK!!)

sudo chown root /usr/local/bin/brew