Raspberry PiでUSBカメラ動画をキャプチャしようとしたらselect timeout

Raspberry Piに接続したUSBカメラの映像をストリーミングする場合、OpenCVや、MJPG-streamer、ffmpeg等を使えばできると思いますが、場合によっては、`select timeout`というエラーが発生して、カメラ映像をキャプチャできないことがあります。主な原因は、キャプチャ時に高解像度設定をしていて、処理が追い付かないということのようですが、ここでは、解像度を落としたくても落とせない時の対処法を記載します。

念のためラズパイのリリースは下記の通りです。

```
$ lsb_release -a
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:        10
Codename:       buster
```

# なぜか低解像度がない
V4L2の`--list-formats-ext`で、接続しているカメラ（この場合は、/dev/device0）の対応フォーマットを確認できます。
私の環境下では、下記の通り、1920x1080と2048x1536しかなく、OpenCV等キャプチャ時の設定で、低解像度を設定しても、「そんなものないから、1920x1080にするよ」と出てきて、その後select timeoutという結果でした。。。

```
$ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
  Type: Video Capture

  [0]: 'YUYV' (YUYV 4:2:2)
    Size: Discrete 1920x1080
      Interval: Discrete 0.200s (5.000 fps)
    Size: Discrete 2048x1536
      Interval: Discrete 0.333s (3.000 fps)
  [1]: 'MJPG' (Motion-JPEG, compressed)
    Size: Discrete 1920x1080
      Interval: Discrete 0.067s (15.000 fps)
      Interval: Discrete 0.200s (5.000 fps)
    Size: Discrete 2048x1536
      Interval: Discrete 0.100s (10.000 fps)
      Interval: Discrete 0.333s (3.000 fps)
```

# 解決方法
V4L2のuserspace([こちら](https://linuxtv.org/downloads/v4l-dvb-apis/))のVideo Capture Exampleのサンプルソースをコンパイルし、強制的にフォーマットを640x480 YUYVにすることで解決しました。（もしかしたら、直接的解法ではなく、トリガになっただけかもしれませんが）

1. Video Capture Exampleのmedia/v4l/capture.cをダウンロード
2. `gcc -o capture capture.c`でコンパイル
3. `./capture -f`

これで、不思議と下記のように、接続したUSBカメラが低解像度も対応しました。

```
$ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
  Type: Video Capture

  [0]: 'MJPG' (Motion-JPEG, compressed)
    Size: Discrete 640x480
      Interval: Discrete 0.067s (15.000 fps)
    Size: Discrete 160x120
      Interval: Discrete 0.040s (25.000 fps)
    ...(省略)...
  [1]: 'YUYV' (YUYV 4:2:2)
    Size: Discrete 640x480
      Interval: Discrete 0.067s (15.000 fps)
    Size: Discrete 160x120
      Interval: Discrete 0.040s (25.000 fps)
    ...(省略)...
    Size: Discrete 1920x1080
      Interval: Discrete 0.200s (5.000 fps)
      Interval: Discrete 0.333s (3.000 fps)
```

# mjpg-streamerでストリーミング
mjgp-streamerを使えば、簡単にカメラ映像をWebストリーミングできます。

## 1. mjpg-streamerのインストール

```shell
$ sudo apt update
$ sudo apt install -y cmake libv4l-dev libjpeg-dev imagemagick
$ git clone https://github.com/jacksonliam/mjpg-streamer.git
$ cd mjpg-streamer/mjpg-streamer-experimental
$ sudo make
$ sudo make install
```

## 2. ストリーミング起動用スクリプト
mjpg-streamer/mjpg-streamer-experimentalフォルダ下にシェルスクリプトを作成します。

```start_stream.sh
#!/bin/sh
PORT="8080" #ポート番号
SIZE="640x480" #画面サイズ
FRAMERATE="15" #フレームレート
export LD_LIBRARY_PATH=/usr/local/lib
mjpg_streamer \
-i "input_uvc.so -f $FRAMERATE -r $SIZE -d /dev/video0 -y -n" \
-o "output_http.so -w ./www -p $PORT"
```
作成シェルスクリプトを起動し、成功すると、下記のようになります。

```shell
$ sh start_stream.sh
MJPG Streamer Version: git rev: 310b29f4a94c46652b20c4b7b6e5cf24e532af39
 i: Using V4L2 device.: /dev/video0
 i: Desired Resolution: 640 x 480
 i: Frames Per Second.: 15
 i: Format............: YUYV
 i: JPEG Quality......: 80
 i: TV-Norm...........: DEFAULT
 o: www-folder-path......: ./www/
 o: HTTP TCP port........: 8080
 o: HTTP Listen Address..: (null)
 o: commands.............: enabled
```
## 3. ブラウザからストリーミング配信を確認する
下記URLにアクセスするだけです。
`http://[RaspberryPiのIPアドレス]:8080`
左メニューのStreamを選択すると、配信映像を確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/c76e5a82-b80f-19d1-9d21-5fc55247314c.png)

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/d6ad10bc-75ba-8124-14d4-f144657c6473.png)](https://twitter.com/ghibi)