Raspberry Pi のカメラモジュールとNode.jsでストリーミング

Raspberry Piのカメラモジュールを使ったストリーミングの覚え書きです。

## 環境

- Raspberry Pi2 ModelB （raspbian-Jessie・Node.js v4.2.4）
- PLANEX 無線LAN子機 (USBアダプター型) GW-USNANO2A (FFP)
- Raspberry Pi カメラモジュール Raspberry Pi Camera Board
- Raspberry Pi カメラモジュール専用　Blackケース

## mjpg-streamer

Raspberry Piであまり手間を掛けずにストリーミングしたい場合はlinuxのフリーソフト「mjpg-streamer」を利用するのがオススメです。

[jacksonliam/mjpg-streamer - GitHub](https://github.com/jacksonliam/mjpg-streamer)

こちらのブログの手順がエラーもなく実行出来ました。

[Cloud Piをつかってみました。（カメラモジュール）- スイッチサイエンス マガジン](http://mag.switch-science.com/2015/01/22/cloudpi_5/)

```bash:ssh

$ sudo apt-get install libv4l-dev libjpeg8-dev imagemagick
$ sudo apt-get install cmake
$ git clone https://github.com/jacksoliam/mjpg-streamer.git
$ cd mjpg-streamer/mjpg-streamer-experimental
$ make

```

実行コマンドは8000番ポート、fps10を指定してこんな風に

```bash:ssh
$ ./mjpg_streamer -o "./output_http.so -w ./www -p 8000" -i "./input_raspicam.so -fps 10"
```

http://192.168.0.10:8000/stream.html にアクセス

![MJPG-streamer 2016-03-15 22-23-44.png](https://qiita-image-store.s3.amazonaws.com/0/47128/1b895bb6-81f6-c2eb-bf79-a8f90ea13347.png)

fps10と動画そのものとは行きませんがかなり滑らかにストリーミング出来ます。

## Node.jsと標準のタイムラプスを使う

Raspberry Piをサーバーとしても利用している場合、タイムラプス撮影を利用してストリーミングを行うことも可能です。

mjpeg規格の様に圧縮を行わないので、サイズは480x320px、フレームレートは3fps（秒間3コマ）ほどのストリーミングになります。

今回もNode.jsとexpress generatorを利用します。

```js:routes/index.js
//expressの使用許可
var express = require('express');
var router = express.Router();

//コマンドラインを利用する為child_processの使用許可
var spawn = require('child_process').spawn;

//ホーム画面index.ejsの表示
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Ras Pi Stream' });
});

//ストリームボタンクリック時の処理
router.get('/stream', function (req, res) {
  console.log(req.query);
  var id = req.query.id;

var raspistill = spawn('raspistill', [ '-o' , './public/images/raspi.jpg', '-h', '300', '-w', '480', '-tl', '300', '-t', '60000', '-v']);

raspistill.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

raspistill.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

raspistill.on('close', function (code) {
  console.log('child process exited with code ' + code);
});

});

//ストップボタンクリック時の処理
router.get('/stop', function (req, res) {
  console.log(req.query);
  var id = req.query.id;

var raspistill = spawn('killall', ['raspistill']);

raspistill.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

raspistill.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

raspistill.on('close', function (code) {
  console.log('child process exited with code ' + code);
});

});

//プレビュー画面preview.ejsの表示
router.get('/preview', function(req, res, next) {
  res.render('preview', { title: 'Ras Pi Stream' });
});

module.exports = router;

```

Raspberry Piの写真撮影の標準コマンド`raspistill`を利用します。オプションコマンド `-tl` でタイムラプス撮影の間隔、`-t` で撮影トータル時間を設定出来ます。

今回は0.3秒間隔で60秒撮影撮影する様に指示しています。

```html:views/index.ejs
<html>
  <head>
    <meta http-equiv="content-type"
        content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
    <title><%= title %></title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css" />
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <header>
        <h1 class="text-center h2"><%=title %></h1>
    </header>
    <article class="text-center">

      <button class="btn btn-default" id="stream">Stream</button>
      <a class="btn btn-default" role="button" href="/preview">Preview</a>

    </article>
    <script src="http://code.jquery.com/jquery-2.2.1.min.js"></script>
    <script>
     $("#stream").click(function() {
        $.get("http://192.168.0.10:3000/stream", { id: 'stream' } );
    });
    </script>

  </body>
</html>

```

```css:public/stylesheets/style.css
body {
  margin: 0 auto;
  max-width: 640px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

article{
  padding: 50px;
}

a {
  color: #00B7FF;
}

```

![Ras Pi Stream 2016-03-15 22-56-50.png](https://qiita-image-store.s3.amazonaws.com/0/47128/044af541-9534-0851-3beb-52424a8f6bd0.png)

ストリームボタンでタイムラプス撮影を開始します。

```html:views/preview.ejs

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type"
        content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
    <title><%= title %></title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css" />
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <header>
        <h1 class="text-center h2"><%=title %></h1>
    </header>
    <article class="text-center">
            <img src="/images/raspi.jpg"  name="webcam" />
            <br>
     <button class="btn btn-default" id="stop">Stop</button>
      <a class="btn btn-default" href="/" role="button">Back</a>

    </article>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script>
      cam = new Image();
      cam.src = document.webcam.src;
      webcamTimer();

      function webcamTimer() {
      	var now = new Date();
      	document.webcam.src = cam.src + "?" + now.getTime();
      	setTimeout("webcamTimer()",300);
      }

        $("#stop").click(function() {
           $.get("http://192.168.0.10:3000/stop", { id: 'stop' } );
       });
    </script>
  </body>
</html>

```

![Ras Pi Stream 2016-03-15 22-56-40.png](https://qiita-image-store.s3.amazonaws.com/0/47128/adf6e544-196f-d06b-cf7d-b0796e5c834a.png)

タイムラプスで上書きされる写真の更新に悩んだのですが、`img`要素の`src`属性に`?hoge`を付けて更新すれば良いみたいです。

`new Date()`と`+ now.getTime();`を使うことで1000分の１秒毎に数値を加算することが出来ます。（これは知らなかった）あとは`settimeout`で0.3秒毎に更新するという仕組みです。

プレビュー画面にストップボタンを付けたので（`killall raspistill`コマンドを実行するボタン）ストリーミングを停止させることが出来ます。（右クリックやスマートフォンの長タップで画像を保存することも可能です）

## 参考にさせていただきました

[一定時間で画像を切り換える - JSすぐに使えるサンプル集](http://www.shurey.com/js/samples/3_img8.html)