Raspberry Piと専用のカメラモジュールを使用し、ONVIF対応の監視カメラを作成するシリーズ記事です。
本記事はリンク情報システム株式会社の有志が作成しています。
今回は動画撮影のポイントとなる場所を解説していきます。
#動画撮影(カメラ制御)
###MMAL (Multi-Media Abstraction Layer) を使って動画撮影
静止画と同様に MMALを使用して、カメラモジュールから動画データを取得します。
第4回で動画取得に使ったraspividのソースファイル、RaspiVid.c を元にポイントとなる処理を抜粋して解説します。
参考元:userland/RaspiVid.c at master · raspberrypi/userland · GitHub
動画取得処理の流れは以下のようになります。
1.カメラ設定
2.エンコーダー設定
3.カメラとエンコーダーの接続
4.キャプチャ開始
5.データ受信
#####1.カメラ設定 (create_camera_component関数)
静止画と同じようにカメラ設定をします。
#####2.エンコーダー設定 (create_encoder_component関数)
処理の流れは静止画と同じですが、動画時はH.264の設定になります。
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と接続します。
connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection);
#####4.キャプチャ開始 (main関数)
画像のキャプチャを開始します。作業用バッファを通知することで、そのバッファに生成された画像データが格納されます。
// 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 データはキーフレームが存在しないと再生を開始できないため、
キーフレームの間隔は再生を開始できるタイミングと考えてください。
ただし、キーフレームはサイズが大きいため、圧縮したデータのサイズに
影響します。
そのため、今回は以下のように1秒に設定しています。
MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, 1};
mmal_port_parameter_set(encoder_output, ¶m.hdr);
受信したデータには以下のようにアクセスします。
また、処理が終わったら、動画データのバッファを解放&通知します。
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です。
リンク情報システム株式会社では一緒に働く仲間を随時募集しています!
また、お仕事のご依頼、ビジネスパートナー様も募集しております。お気軽にご連絡ください。