実例で学ぶPHP拡張モジュールの作り方

第4回 WEBカメラから画像をキャプチャ(その2)

この記事を読むのに必要な時間:およそ 6 分

リソースを受け取る関数

次に,cv_save_capture()を実装します。リスト9はpecl-gen直後の状態です。

リスト9 cv_save_capture()関数(cvcapture.c)

/* {{{ proto bool cv_save_capture(resource cvcapture capture, string filename[, array &mixed])
  Capture a frame. */
PHP_FUNCTION(cv_save_capture)
{
  /* リソース関連変数の宣言 */
  zval * capture = NULL;
  int capture_id = -1;
  CvCapture * res_capture;

  /* その他の変数の宣言 */
  const char * filename = NULL;
  int filename_len = 0;
  zval * size = NULL;

  /* 引数をパース */
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|z", &capture, &filename, &filename_len, &size) == FAILURE) {
    return;
  }
  /* リソースの実体を取得 */
  ZEND_FETCH_RESOURCE(res_capture, CvCapture *, &capture, capture_id, "cvcapture", le_cvcapture);

  /* 実装されていない旨のエラーを出力 */
  php_error(E_WARNING, "cv_save_capture: not yet implemented"); RETURN_FALSE;

  RETURN_FALSE;
}
/* }}} cv_save_capture */

引数をリソースをして取得する場合,指示子は⁠r⁠で,対応する型は⁠zval *⁠です。このままではキャプチャ構造体にアクセスできないので,続いてマクロZEND_FETCH_RESOURCE()でリソースの実体を取得し,⁠CvCapture *⁠型にキャストしています。

もし引数がリソースでない場合はzend_parse_parameter()でエラー,リソースだけどタイプがle_cvcaptureでない場合はZEND_FETCH_RESOURCE()でエラーとなり,falseを返します。capture_idの値を-1以外にすると,引数の代わりにcapture_idと同じIDのリソースを取得できますが,ここでは-1のままにしておいてください。文字列の"cvcapture"は,エラーメッセージに表示されるリソースタイプ名です。

この関数の実装もcv_camera_capture()関数から流用できます。先頭に変数の宣言リスト10を追加し,引数のパースの後にリスト11の内容を加えれば完了です。

リスト10 追加の変数宣言

char *fullpath;     /* 保存先のフルパス */
IplImage *image;    /* イメージ構造体 */

リスト11 キャプチャした画像を保存

/* パスのチェック */
if (strlen(filename) != filename_len ||
  (fullpath = expand_filepath(filename, NULL TSRMLS_CC)) == NULL)
{
  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong filename given");
  RETURN_FALSE;
}

if (php_check_open_basedir(fullpath TSRMLS_CC) ||
  (PG(safe_mode) && !php_checkuid(fullpath, NULL, CHECKUID_CHECK_FILE_AND_DIR)))
{
  efree(fullpath);
  RETURN_FALSE;
}

/* フレームを取得・保存 */
image = cvRetrieveFrame(res_capture);
if (image == NULL) {
  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot retrieve image");
  efree(fullpath);
  RETURN_FALSE;
}

if (!cvSaveImage(fullpath, image)) {
  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot save image to '%s'", fullpath);
  efree(fullpath);
  RETURN_FALSE;
}

/* 画像サイズを代入 */
if (size != NULL) {
  CvSize imgsize = cvGetSize(image);
  zval_dtor(size);
  array_init(size);
  add_next_index_long(size, (long)imgsize.width);
  add_next_index_long(size, (long)imgsize.height);
}

efree(fullpath);
RETURN_TRUE;

インストール

実装が終わったら,お決まりの手順でインストールします。テストも忘れずにしておきましょう。

操作2 ビルド&テスト&インストール

$ phpize
$ ./configure --enable-cvcapture
$ make
$ make test
$ sudo make install

実際に使ってみる

リスト12は前回のサンプルをリソースを使うように書き直したものです。

リスト12 サンプルスクリプト(cvcapture-sample2.php)

<?php
extension_loaded('cvcapture') || dl('cvcapture.so');

$capture = cv_create_camera_capture();
if ($capture) {
    for ($i = 1; $i <= 10; $i++) {
        cv_save_capture($capture, sprintf('capture%02d.jpg', $i));
        sleep(1);
    }
}

今回の目的の一つは,リソースを使って毎回カメラに接続するオーバーヘッドを節約することでしたので,サンプルに少し手を加えてキャプチャにかかる時間を計ってみました。

表1 リソースを使わないときと使ったときの比較

リソースを使わないときリソースを使ったとき
初期化-0.312秒
1回目1.085秒0.548秒
2回目0.949秒0.040秒
3回目0.564秒0.039秒
4回目0.565秒0.038秒
5回目0.565秒0.039秒
6回目0.565秒0.039秒
7回目0.564秒0.038秒
8回目0.568秒0.038秒
9回目0.570秒0.037秒
10回目0.565秒0.041秒
合計6.566秒1.214秒

どうやら期待通りの結果が得られたようです。このようにリソースを使えばPHPの組み込み型でない構造体を使い回すことができます。

おわりに

今回はリソースとリソースを扱う関数を定義しました。しかし,今どきのPHPプログラミングでリソースをそのまま使うのは少々時代遅れと言えます。そこで,次回はこれをラップして,リソースをプロパティとして持つクラスを定義し,拡張モジュールでオブジェクト指向のAPIを実装する方法を紹介します。

サンプルファイルのダウンロード

著者プロフィール

関山隆介(せきやまりゅうすけ)

ジンガジャパン株式会社に所属。PHP拡張モジュールを作った数なら(たぶん)日本一。PHP 5.3に対応したものはPEARチャンネルGitHubで公開中。

URLhttp://d.hatena.ne.jp/rsky/