第１０回　Raspberry Pi で監視カメラを作ろう！　～動画編～

Raspberry Piと専用のカメラモジュールを使用し、ONVIF対応の監視カメラを作成する[シリーズ記事](https://qiita.com/lis-hanzomon/items/cc1f7f316fbeb9579b28)です。
本記事は[リンク情報システム株式会社](https://www.lis.co.jp/)の有志が作成しています。

---

今回は動画撮影のポイントとなる場所を解説していきます。

#動画撮影（カメラ制御）
###MMAL (Multi-Media Abstraction Layer) を使って動画撮影
静止画と同様に MMALを使用して、カメラモジュールから動画データを取得します。

第４回で動画取得に使ったraspividのソースファイル、RaspiVid.c を元にポイントとなる処理を抜粋して解説します。
参考元：[userland/RaspiVid.c at master · raspberrypi/userland · GitHub](https://github.com/raspberrypi/userland/blob/master/host_applications/linux/apps/raspicam/RaspiVid.c)
 

動画取得処理の流れは以下のようになります。
　1.カメラ設定
　2.エンコーダー設定
　3.カメラとエンコーダーの接続
　4.キャプチャ開始
　5.データ受信

#####1.カメラ設定 (create_camera_component関数)
静止画と同じようにカメラ設定をします。

#####2.エンコーダー設定 (create_encoder_component関数)
処理の流れは静止画と同じですが、動画時はH.264の設定になります。

```c
encoder_output->format->encoding = MMAL_ENCODING_H264;
encoder_output->format->bitrate = MAX_BITRATE_LEVEL42;

encoder_output->buffer_size = encoder_output->buffer_size_recommended * 4; // 1920x1080の場合に、buffer_size_recommendedだとハングアップするようでした。

if (encoder_output->buffer_size < encoder_output->buffer_size_min)
    encoder_output->buffer_size = encoder_output->buffer_size_min;
encoder_output->buffer_num = encoder_output->buffer_num_recommended;
if (encoder_output->buffer_num < encoder_output->buffer_num_min)
   encoder_output->buffer_num = encoder_output->buffer_num_min;
encoder_output->format->es->video.frame_rate.num = 0;
encoder_output->format->es->video.frame_rate.den = 1;

// Commit the port changes to the output port
status = mmal_port_format_commit(encoder_output);
```

#####3.カメラとエンコーダーの接続 (main関数)
静止画と違い、camera_video_portと接続します。

```c
connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection); 
```

#####4.キャプチャ開始 (main関数)
画像のキャプチャを開始します。作業用バッファを通知することで、そのバッファに生成された画像データが格納されます。

```c
// Enable the encoder output port and tell it its callback function
mmal_port_enable(encoder_output_port, encoder_buffer_callback);

// エンコーダーにバッファを通知する
int num = mmal_queue_length(state.encoder_pool->queue);
int q;
for (q=0;q<num;q++)
{
    MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.encoder_pool->queue);
    mmal_port_send_buffer(encoder_output_port, buffer);
}
```

#####5.データ受信 (main関数)
バッファに画像が格納されると、mmal_port_enableで設定しておいた、encoder_buffer_callback関数が呼び出されます。encoder_buffer_callback関数が呼び出されます。

以下のようにbuffer->flagsの値で受信したデータが判断できるので、それぞれの処理を実装します。

- 「MMAL_BUFFER_HEADER_FLAG_CONFIG」
 　　　ヘッダデータとしてsps、ppsの受信時に通知されます。
　　　尚、sps、ppsのデータの先頭には "00 00 01" が設定されます。
　　　データの先頭を見つける事で、sps、ppsのサイズを求める事ができます。

- 「MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO」
　　　センサからのメタデータを受信時に通知されます。
　　　今回はセンサを接続していないので、通知されません。

- 「上記以外」
　　　動画データを受信時に通知されます。
　　　尚、MMAL_BUFFER_HEADER_FLAG_KEYFRAMEは、
　　　キーフレーム（H.264の場合は IDR フレーム）を受信した時に通知されます。
　　　また、MMAL_PARAMETER_INTRAPERIODでキーフレームが出現する間隔を設定します。
　　　h.264 データはキーフレームが存在しないと再生を開始できないため、
　　　キーフレームの間隔は再生を開始できるタイミングと考えてください。
　　　ただし、キーフレームはサイズが大きいため、圧縮したデータのサイズに
　　　影響します。
　　　そのため、今回は以下のように１秒に設定しています。

```c
MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, 1};
mmal_port_parameter_set(encoder_output, &param.hdr);
```

　　　受信したデータには以下のようにアクセスします。
　　　また、処理が終わったら、動画データのバッファを解放＆通知します。

```c
static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
    mmal_buffer_header_mem_lock(buffer);

    unsigned char* data = buffer->data;
    int data_len = buffer->length;

    /* データの内容にアクセスする */
    fwrite(data, 1, data_len, fp);

    mmal_buffer_header_mem_unlock(buffer);

    // 受信したバッファをバッファプールに返す
    mmal_buffer_header_release(buffer);

    // 動画受信を続ける場合
    if (port->is_enabled)
    {
        // 新しいバッファをバッファプールから取得し、受信処理に通知する
        MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue);
        mmal_port_send_buffer(port, new_buffer);
    }
}
```

　　　上記のように受信した動画データをファイルに保存できたら、
　　　拡張子をh264 に変更しWindows上にコピーします。
　　　VLCアプリで再生して動画が表示されればOKです。



---
[インデックス記事へ](https://qiita.com/lis-hanzomon/items/cc1f7f316fbeb9579b28)
[第９回記事へ](https://qiita.com/lis-hanzomon/items/9e975477bf9f255ed374)
[第１１回記事へ](https://qiita.com/lis-hanzomon/items/91c283c3310b2e0f7858)

---
[リンク情報システム株式会社](https://www.lis.co.jp)では一緒に働く仲間を[随時募集](https://www.lis.co.jp/employ_top)しています！
また、お仕事のご依頼、ビジネスパートナー様も募集しております。[お気軽にご連絡](https://www.lis.co.jp/inquiry)ください。