Haskellでv4l2を用いてwebカメラから得られた画像を保存する(自分用メモ)

# 0. インストール環境

・Ubuntu 15.04
・ghc 7.8.3

# 1. v4l2のパッケージをインストールする・・・が
v4l2のapiをラップしてくれている[v4l2](https://hackage.haskell.org/package/v4l2)パッケージが既に存在します。(作成してくださった方に感謝！)
このパッケージをインストールするだけなのですが、

```
cabal install v4l2
```

でインストール出来ませんでした。

原因は
v4l2パッケージが依存している[bindings-linux-videodev2](https://hackage.haskell.org/package/bindings-linux-videodev2)が[Foreign](https://hackage.haskell.org/package/base-4.6.0.1/docs/Foreign.html)のunsafePerformIOを使用しており、
ghc 7.8.3ではunsafePerformIOが削除されているからです。
なので、2015/5/31現在では、手動でパッケージをビルドしなければ、使用できません。
ここでは、v4l2のパッケージをインストールしwebカメラの映像を表示させるところまでの手順を記載します。
(手順が合っているかどうかは分からないけど。)

## 1.1 bindings-linux-videodev2の手動インストール

bindings-linux-videodev2をインストールしないとv4l2パッケージをインストールすることが出来ないので、
まずこちらのパッケージからインストールします。

### 1.1.1 ソースのダウンロード
ソースは[こちら](http://code.mathr.co.uk/bindings-linux-videodev2)にあります。
git cloneするか、snapshotをクリックすればダウンロードできます。

### 1.1.2 generate.shの実行

```
./generate/generate.sh
```

を実行します。
すると、Bindingsフォルダやdistフォルダなどが作成されます。

### 1.1.3 Bindingsフォルダ以下のソースを書き換え

generate.shを実行したあと、Bindings/Linuxの中に
・VideoDev2.hsc
・videodev2.h
・videodev2.h.rej
・videodev2_cinline.c
が存在します。

生成されたファイルはどうも上手く生成されていないため、
[ここ](https://hackage.haskell.org/package/bindings-linux-videodev2-0.1/src/Bindings/)の内容に置き換えてしまいます。
(これが良いのかわからないけど・・・。)

### 1.1.4 VideoDev2.hscの内容を修正。

```.hs
import Foreign(unsafePerformIO)
```

を

```.hs
import System.IO.Unsafe(unsafePerformIO)
```

に書き換えます。

### 1.1.5 インストール

bindings-linux-videodev2のルートフォルダで

```
cabal configure
cabal build
cabal install
```

とすれば、bindings-linux-videodev2がインストールされます。

## 1.2 v4l2の手動インストール

### 1.2.1 ソースのダウンロード
ソースは[こちら](http://code.mathr.co.uk/v4l2)にあります。
git cloneするか、snapshotをクリックすればダウンロードできます。

## 1.2.2 v4l2.cabalの内容を修正

v4l2.cabalファイルの内容を

```
bindings-linux-videodev2 >= 0.1 && < 0.2,
```

のところを

```
bindings-linux-videodev2 >= 0.2,
```

に修正します。
(∵インストールしたのはbindings-linux-videodev2のv0.2.0.1だから)

### 1.2.3 インストール

v4l2のルートフォルダで

```
cabal configure
cabal build
cabal install
```

とすれば、v4l2がインストールされます。

# 2. v4l2パッケージを用いたプログラム例

v4l2パッケージを用いて、webカメラから取り込んだ画像を保存するプログラムの例を作成しました。
丁寧に書かれた例は、v4l2パッケージを作成した方が作成されていますので、[そちら](http://code.mathr.co.uk/v4l2-examples/blob/1c6a501ec038bf203b0561d89e56ec0ca7ec0fb9:/v4l2-capture/v4l2-capture.hs)を参考にされたほうが良いです。

以下のプログラムは例外処理などを書かず、
「デバイスを開いて、映像を取り込んで、画像を保存する」という
できるだけ最小限のコードにしました。

```v4l2_ex.hs
import System.IO
import Control.Monad
import Foreign (Ptr)
import Graphics.V4L2

width :: Int
width = 640

height :: Int
height = 480

imf :: ImageFormat
imf = ImageFormat width height PixelRGB24 FieldNone (width * 3) (width * 3 * height) ColorSRGB

main :: IO()
main = do
  device <- openDevice "/dev/video0"
  format <- setFormat device Capture imf
  forM_ [0..50] $ \i ->
    withFrame device format $ \p _ -> writePPM i (imageWidth format) (imageHeight format) p
  closeDevice device

writePPM :: Int -> Int -> Int -> Ptr a -> IO ()
writePPM idx w h p = do
  handle <- openFile ("writePPM_" ++ show idx ++ ".ppm") WriteMode
  hPutStr handle $ "P6\n" ++ show w ++ " " ++ show h ++ "\n255\n"
  hPutBuf handle p (w * h * 3)
  hClose handle
```

次はwebカメラで取り込んだ映像をwindow画面に表示したいと思います。

## (補足) repa-v4l2のインストール

v4l2のパッケージを更に扱いやすくラップしてくれているパッケージが[repa-v4l2](https://hackage.haskell.org/package/repa-v4l2)のようです。

```
cabal install repa-v4l2
```

とすると、llvm関連のエラーが出ました。

ですので、自分は

```
sudo aptitude install llvm-3.4
```

としました。(llvm-3.5とllvm-3.6をインストールしても上手くインストール出来ませんでした。)
更にghc-7.8.3/settingsのファイルを開いて

```
, ("LLVM llc command","llc-3.4")
, ("LLVM opt command","opt-3.4")
```

と修正します。

その後、

```
cabal install repa-v4l2
```

すれば、インストール出来ました。