8.16.2012

Build A Live Audio Streaming System

這是一個手工打造串流音樂台的記錄,source 是以 ffmpeg 擷取 foobar2000 的音源,轉檔並傳送到 server 那邊的 ffserver,
  • source: Intel Core 2 Duo E6850, 8GB ram, Windows 7 64bit.
  • server: Intel Atom D510, 4GB ram, FreeBSD 9.0 amd64.
Server 端只負責對外的服務,不需要特別強悍的效能。
這個串流系統總共用到這些東西,
  • ffmpeg:@source,擷取音源
  • foobar2000:@source,播音樂,並且將現在播放的資訊丟到遠方的節目表 service
  • ffserver :@server,接收 ffmpeg 來源,並提供對外的 streaming
  • socket.io:@server,接收 foobar2000 現在播放的資訊,推播到網頁上
  • nginx :@web,網頁介面
  • jPlayer:@web,HTML5 audio library

ffmpeg & ffserver


  • ogg in webm, 192kb for Firefox, Chrome.
  • mp3, 256kb for Chrome, Internet Explorer, Safari.

<stream mu.webm="mu.webm">
Feed mu.ffm
Format webm
AudioCodec libvorbis
AVOptionAudio flags +global_header
AudioBitRate 192
AudioChannels 2
AudioSampleRate 44100
NoVideo
</stream>

<stream mu.mp3="mu.mp3">
Feed mu.ffm
Format mp3
AudioCodec libmp3lame
AVOptionAudio flags +global_header
AudioBitRate 256
AudioChannels 2
AudioSampleRate 44100
NoVideo
</stream>

只要這邊設定好了,就已經能用常用的 player 打開 URL 聽串流。

foobar2000


因為是採用 ffmpeg 直接擷取整台電腦的音源,沒辦法直接將 foobar2000 的 meta informations 串流在一起,meta informations 必須另外自己處理,所以 foobar2000 這邊除了播音樂,還要負責將現在播放的資訊丟到遠方的節目表服務,這邊是以 foobar SDK 弄了一個 
它能將現在播放的資訊以 HTTP POST 傳到設定頁中指定的 URL,傳送的動作會在「歌曲變更」時觸發。

socket.io


socket.io 是 node 上非常方便的 Web Socket implementation,對於支援的的 browser,會直接以 WebSocket 溝通,否則會降回到傳統的幾個 long-pulling 機制。這邊以 node 與 socket.io 弄出一個節目表的服務,它的功用在

nginx


一者是想讓對外的服務介面一致,一者是 node 不花心思寫的話,serving static files 效能不彰。這裡實際上我讓 foobar2000 的 HTTP POST 與 browser client 的推播導向跑在 localhost 的節目表 service。

    server {
        server_name mu.adios.tw;

        location / {
            root        /home/www/adios.tw/mu;
            index       mu.html;
            try_files   $uri $uri/ @home;
        }
        location ~ /\.git {
            deny        all;
        }
        location ~ ^/foo {
            proxy_pass  http://127.0.0.1:2000;
        }
        location ~ ^/muuun {
            proxy_http_version  1.1;
            proxy_pass          http://127.0.0.1:2000;
        }
        location @home {
            proxy_pass  http://127.0.0.1;
        }
    }

網頁介面


網頁介面包括使用了現成的 jPlayer & CirclePlayer,與修改後的版本
這邊使用到的技術有 HTML5 Audio, Socket.io, CSS 3 WebFont

整個系統之後可以朝向多人 hosters 的目標發展,也就是由聽眾決定,要不要繼續聽目前的曲子,還是要直接跳至 foobar2000 清單中的下一首。這個過程能經過投票決定。這部份會是 foobar2000 與 socket.io 的相關設計。



8.15.2012

在 Windows 用 ffmpeg 錄桌面

比較新版本的 ffmpeg 支援 dshow input,在 Windows 下能直接使用 DirectShow 相容的裝置 (webcam, mic) 當輸入源,

ffmpeg -f dshow -list_devices true -i dummy

會列出所有它能認出的 dshow devices。接著安裝虛擬的 dshow 裝置,視需求安裝,
如果要擷取整台電腦的音源(想錄一般麥克風、或是列出的名稱是亂碼的音源裝置),第二項是必須要裝的。注意要用這兩個 virtual device 需要 32bit 的 ffmpeg

ffmpeg -rtbufsize 512000k -f dshow -i video=screen-capture-recorder:audio=virtual-audio-capturer
       -vcodec libx264 -preset fast -tune stillimage -x264opts crf=26
       -acodec libvorbis -aq 3
        screencast.mkv

這樣會將現在看到的桌面與任何動作以 x264 壓縮到 screencast.mkv 這個影片中,就算是在被遠端桌面的狀況下,也能正常錄製;當下桌面的解析度就會是影片壓製的解析度。

最近版本這樣壓出來的影片會是 4:4:4 的,若要丟到 Youtube 上,必須是 yuv420p,所以要指定,

ffmpeg -rtbufsize 512000k -f dshow -i video=screen-capture-recorder:audio=virtual-audio-capturer
       -vcodec libx264 -preset fast -tune stillimage -x264opts crf=26
       -acodec libvorbis -aq 3
       -pix_fmt yuv420p
       -sws_flags lanczos
       -vf crop=1440:810:0:0,scale=1280:720
        screencast.mkv

假設你的螢幕解析度是 1440x900,為了上傳 youtube 方便,想弄成 720p 的 16:9,可以先 crop 成 16:9 的 1440x810,然後再 scale 成 1280x720,如上的綠字部份。像是這個測試就是從 1024x640 crop and scale to 1280x720。

如果要流暢一點,還能指定 input 的 framerate (defaults to 25fps),詳細請看 ffmpeg 的 man pages。

記得,如果是在 mac 下…



可以直接用 QuickTime Player 錄桌面!

8.14.2012

試試 CSS3 Web Font

如果只是想要好看的英文字型,Google Web Font 提供非常方便的服務:
  • 能選擇多組字型
  • 能預覽這些字型在不同樣式、位置的搭配效果
  • 字型是公開授權過的
  • 不用擔心 @font-face 的多平台多字型問題
可惜沒有提供中文、日文字型,畢竟這些字型的檔案都太大(2MB+)。

TO HOST FONT


把字型檔案擺在正確的位置,並多加一行 @font-face 在 CSS 前面,就能讓 font-family 使用自已放在 server 上的字體:

<style type="text/css">
        @font-face {
                font-family: 'mikachan';
                src: url('/fonts/mikachan.otf');
        }
        /* some elsewhere ... */
        body {
                font-family: 'mikachan', sans-serif;
        }
</style>

在最簡單的情況下,用 Chrome 與 Firefox 能正常下載和顯示這字型。若字型要讓
  • 老版本的 Internet Explorer 使用 (eot)
  • 其它平台的瀏覽器使用 (svg, ttf)
  • 讓它遵循規範
那麼 @font-face 這邊就必須加上多種格式:

<style type="text/css">
        @font-face {
                font-family: 'mikachan';
                src:    url('/fonts/mikachan.eot');
                src:    url('/fonts/mikachan.eot?#iefix') format('embedded-opentype'),
                        url('/fonts/mikachan.woff') format('woff'),
                        url('/fonts/mikachan.ttf') format('truetype'),
                        url('/fonts/mikachan.svg#mikachan') format('svg');
                font-weight: normal;
                font-style: normal;
        }
</style>

當中的數個 src properties 是必要的,Nice Web Type 這邊記錄和說明每一行必須存在的原因,也詳列數年來各種不同的 bullet-proof 技巧。

不同字型格式的轉換可以用 www/ttf2eot 和 www/sfnt2woff,或是直接用線上的轉換服務:
它們會同時幫你產生對應的 @font-face CSS 和 demo page。

如果要 host 的字型變多了,可以寫一個產生 CSS @font-face 的 handler:

<?php
header('Content-Type: text/css');

function fill($buffer) {
        global $font_family;
        global $file_name;
        return str_replace('{FILE_NAME}', $file_name, str_replace('{FONT_FAMILY}', $font_family, $buffer));
}
?>

<?php ob_start('fill'); ?>
@font-face {
    font-family: '{FONT_FAMILY}';
    src: url('/fonts/{FILE_NAME}.eot');
    src: url('/fonts/{FILE_NAME}.eot?#iefix') format('embedded-opentype'),
         local('☺'),
         url('/fonts/{FILE_NAME}.woff') format('woff'),
         url('/fonts/{FILE_NAME}.ttf') format('truetype'),
         url('/fonts/{FILE_NAME}.svg#{FILE_NAME}') format('svg');
    font-weight: normal;
    font-style: normal;
}
<?php ob_end_flush(); ?>

甚至判斷瀏覽器 User Agent,僅回應指定的 @font-face rule,這樣就不需特別作 IE bullet proof。

NGINX CONFIGURATION


新增或修改這幾種附檔名的 MIME Type:

types {
    application/x-font-ttf                ttf;
    application/x-font-woff               woff;
    application/vnd.ms-fontobject         eot;
}

以及 nginx.conf,

server {
    ...
    location /fonts {
        add_header  Access-Control-Allow-Origin http://mu.adios.tw;
        gzip_types  image/svg+xml application/x-font-ttf application/vnd.ms-fontobject;
    }
    ...
}

傳輸用 gzip 是讓我們的字體能小一點點(woff 是已經壓過的格式,所以不再 gzip),要注意的是那個 header,W3C 規範瀏覽器必須禮貌性遵守字型的同源政策,也就是:

  • 你在瀏覽 Host A 的網頁
  • Host A 的 @font-face src properties 指到 Host B
  • 瀏覽器向 Host B 問字型時,Host B 會先回應含有這條 header 的訊息
  • 瀏覽器將這條 header 比對 Host A,如果一樣,才會下載並使用字型

IE9: CS3111: Unknown Error


如果 Internet Explorer 9 無法顯示字型,打開開發者工具,檢查看看是不是吐這條訊息,這表示字型檔案錯誤。詳細原因我也不明暸,應該是字體內有些屬性 Windows 不懂,這發生在從 otf 轉換的 eot 字型,後來我是拿為原本設計給 windows 的 ttf 格式來轉換 eot,就正常無虞了。

顯示字型的一致性


在字型正在被下載的過程中,採用該字體的文字:
  • Chrome 不顯示任何東西,直到字體下載完,才會顯示
  • Firefox 顯示預設字型,直到字體下載完,才會更新
WebFont Loader 能彌平這種不一致性。要嘛就讓所有瀏覽器都不顯示,要嘛就是先顯示預設字型。