V4l2でアプリケーション作りました

カテゴリー: 映像 | 投稿日: | 投稿者:

ひっさびさの いかお です。

前回の宣言通りつくってみました。

MJPEGで撮影できるUVCカメラで動画撮影し、とれた映像をそのまま書いたら
JPEGじゃね?

と思って作ってみますた

カメラはlogicoolのC920Tです。
多少不安定な感じだけど、あっさり認識した

[6734110.675010] usbhid 3-2:1.0: >couldn't find an input interrupt endpoint
[6734110.675925] usbcore: registered new interface driver usbhid
[6734110.675927] usbhid: USB HID core driver
[6734966.300322] usb 3-1: >new high-speed USB device number 11 using xhci_hcd
[6734966.319401] usb 3-1: >New USB device found, idVendor=046d, idProduct=082d
[6734966.319405] usb 3-1: >New USB device strings: Mfr=0, Product=2, SerialNumber=1
[6734966.319408] usb 3-1: >Product: HD Pro Webcam C920
[6734966.319410] usb 3-1: >SerialNumber: A0313D6F
[6734966.319925] uvcvideo: Found UVC 1.00 device HD Pro Webcam C920 (046d:082d)
[6734966.320342] input: HD Pro Webcam C920 as /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/input/input37
[6734968.751553] xhci_hcd 0000:00:14.0: >WARN Event TRB for slot 10 ep 0 with no TDs queued?

中核部分はこんな感じ

////////////////////////////////////
// O P E N   D e v i c e          //)
////////////////////////////////////
        camFd   =       open(devName, O_RDWR);
        if(camFd == -1){
                fprintf(stderr, "Device(%s) open failed\n", devName);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto threadExit;
        }
        // setup resolution etc
        memset((void *)&fmt, 0x00, sizeof(fmt));
        fmt.type                =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width       =       startParm->width;
        fmt.fmt.pix.height      =       startParm->height;
        fmt.fmt.pix.pixelformat =       V4L2_PIX_FMT_MJPEG;
        ret = xioctl(camFd, VIDIOC_S_FMT, &fmt);
        if(ret < 0){
                fprintf(stderr, "Settup failed\n");
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        if(fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG){
                fprintf(stderr, "WARNING!! Assigned incorrect fmt:%d resolition:%dx%d\n",
                        (int)fmt.fmt.pix.pixelformat,
                        (int)fmt.fmt.pix.width,
                        (int)fmt.fmt.pix.height);
        }
        ///////////////////////////////////
        // memory mapping
        ///////////////////////////////////
        // request mmap
        memset((void*)&req, 0x00, sizeof(req));
        req.count       =       MMAP_CNT;
        req.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory      =       V4L2_MEMORY_MMAP;
        ret = ioctl(camFd, VIDIOC_REQBUFS, &req);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_REQBUFS failed\n");
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        if(req.count != MMAP_CNT){
                fprintf(stderr, "mmap allocate failed(%d)\n", req.count);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        // map to userland)
        for(i=0;i < MMAP_CNT;i++){
                memset((void*)&buf, 0x00, sizeof(buf));
                buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      =       V4L2_MEMORY_MMAP;
                buf.index       =       i;
                ret = xioctl(camFd, VIDIOC_QUERYBUF, &buf);
                if(ret < 0){
                        fprintf(stderr, "mmap VIDIOC_QUERYBUF[%d]\n", i);
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        goto closeExit;
                }
                mmap_p[i] = mmap(NULL, buf.length, PROT_READ, MAP_SHARED, camFd, buf.m.offset);
                if(mmap_p[i] == MAP_FAILED){
                        fprintf(stderr, "mmap(%d)\n", errno);
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        gExecuted       =       0;
                        goto closeExit;
                }
                mmap_l[i]       =       buf.length;
#if 1
                fprintf(stderr, "mmbuff[%d] length is \"%u\"\n", i, buf.length);
#endif
        }
        // Queueing before
        for(i=0;i < MMAP_CNT;i++){
                memset((void*)&buf, 0x00, sizeof(buf));
                buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      =       V4L2_MEMORY_MMAP;
                buf.index       =       i;
                ret = xioctl(camFd, VIDIOC_QBUF, &buf);
                if(ret < 0){
                        fprintf(stderr, "VIDIOC_QBUF error\n");
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        goto closeExit;
                }
        }
        ////
        // start stream
        ////
        type    =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = xioctl(camFd, VIDIOC_STREAMON, &type);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_STREAMON error:%d\n", ret * -1);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        // Capture Stanby
        startParm->stanby       =       1;
        //
        while(gExecuted){
                fd_set  fds;
                FD_ZERO(&fds);
                FD_SET(camFd, &fds);
                struct  timeval timeOut;
                timeOut.tv_sec  =       0;
                timeOut.tv_usec =       USEC_1MSEC;
                ret = select(camFd + 1, &fds, NULL, NULL, &timeOut);
                if(ret < 0){
                        if(errno == EINTR)
                                continue;
                        fprintf(stderr, "select() error\n");
                        *exitStatus     =       (int)FWCAMCAPT_OTHER;
                        break;
                }
                if(FD_ISSET(camFd, &fds)){
                        memset(&buf, 0, sizeof(buf));
                        buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                        buf.memory      =       V4L2_MEMORY_MMAP;
                        ret = xioctl(camFd, VIDIOC_DQBUF, &buf);
                        if(ret < 0){
                                fprintf(stderr, "VIDIOC_DQBUFi error\n");
                                *exitStatus     =       (int)FWCAMCAPT_OTHER;
                                break;
                        }
                        if(pthread_mutex_lock(&(startParm->bufMutex)) == 0){
                                memcpy(startParm->frameBuff, mmap_p[buf.index], buf.bytes[6734110.675010] usbhid 3-2:1.0: >couldn't find an input interrupt endpoint
used);
                                startParm->length       =       buf.bytesused;
                                pthread_mutex_unlock(&(startParm->bufMutex));
                        }
                        ret = xioctl(camFd, VIDIOC_QBUF, &buf);
                        if(ret < 0){
                                fprintf(stderr, "VIDIOC_QBUF error\n");
                                *exitStatus     =       (int)FWCAMCAPT_OTHER;
                                break;
                        }
                }
#if 0
                else
                        fprintf(stderr, "TO\n");
#endif
        }
        ////
        // stop stream
        ////
        type    =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = xioctl(camFd, VIDIOC_STREAMOFF, &type);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_STREAMOFF error:%d\n", ret * -1);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
////////////////////////////////////
// C L O S E   D e v i c e        //
////////////////////////////////////
closeExit:
        close(camFd);

これを説明していくと、基本的にこの中核処理はサブスレッドで動いていて、
カメラからMJPEGのフレームが来たら
startParm->frameBuff
にコピーしてるってわけです。

ここを適当なタイミング(1秒毎とか)覗いてフレームが
あったら(ここには書いてないけど)xxx.jpegという名前で書いてます
※ここでの書き込みと、フレームデータの参照が重ならないように
mutex()してます

それと、コードの中で
camFd = open(devName, O_RDWR);
ってのがあります。
最初、カメラからビデオを読むだけだからO_RDONLYで良いと浅はかに考えていたけど、
よく考えてみたらカメラデバイスをコントロールするため、ioctl()で制御してるんだから
書き込みがいるんだよね。

ちなみに
xioctl()
アットマークテクノさんのページ
を参考にしました(全体的にも参考にしてます)

内容的には以下のコード

//////////////////////////////////////////////////////////
// xioctl copy from 
// http://manual.atmark-techno.com/armadillo-810/armadillo-810_product_manual_ja-1.3.0/ch14.html
/////////////////////////////////////////////////////////
static int xioctl(int fd, int request, void *arg){
int     ret;
        for(; ; ){
                ret = ioctl(fd, request, arg);
                if(ret < 0){
                        if(errno == EINTR)
                                continue;
                        return -errno;
                }
                break;
        }
        return 0;
}

メモリマップとユーザーランドのやりとりの低レベル
(っていうかioctl()とかしてる段階で感じるけど)な感じが
オタク心をくすぐるか?

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

『リーダブルコード』を読みました。(3)

コードのスタイル、見た目

こんにちは、ニタです。
リーダブルコード第3回目は、「コードのスタイル、見た目」について書きたいと思います。

可読性が高く、読みやすいコードを書くには、分かりやすい変数、関数の命名規則の他に、見た目も意外と大事だと思います。

どんなにバグがなく高速に処理できるプログラムでも、ファイルを開いてみると、コードがゴチャゴチャしていて、理解するのに時間が掛かるようでは、修正する際困るのはコードを書いた自分であり、修正することになる自分を含む誰かです。

コードがキレイだと、さっと流し読みができて理解しやすく、修正、リファクタリングのしやすさに繋がると思います。
では、キレイなコードを書くためには何が重要なのでしょうか。

キレイなコードを書くためのポイント

キレイなコードを書くために気をつけるべきポイント・原則は、この3点だと思います。

  • 読み手が慣れているパターン、一貫性のあるレイアウトを使う
  • 似ているコードは似ているように見せる
  • 関連するコードは1ブロックにまとめる

これらを実施するためには、実際にコードを書く段階でどのような事を行えばいいでしょうか。

一貫性のある簡潔な改行位置

例えば、関数の引数の指定など、コードの改行位置を統一することでコードを見やすくなる場合もあります。

以下は、投稿内容を取得する関数で、ニュースとブログの一覧を取得するコードです。
Post::getList()で必要な引数ごとに改行し、各引数の横にそれぞれの引数が何を意味するのかをコメントで書いてみました。

$news_list = Post::getList(
    'news', // 投稿タイプ
    20, // 1ページあたりの表示件数
    1, // 表示ページ
    true // 降順表示
);

$blog_list = Post::getList(
    'blog', // 投稿タイプ
    10, // 1ページあたりの表示件数
    1, // 表示ページ
    false // 降順表示
);

vimなどで、複数のファイルを同時に表示した際、1行あたりの文字数が限られてしまいます。
そのため、このように縦に長いコードの方が読みやすい場合も出てくると思います。
また、直接引数に値を入力せずに、分かりやすい変数名を決め、変数の上部や後ろにコメントで説明を追記しても良いですが、変数を作るまでもない場合はこのように書くのもアリかと思います。

ただ、上記のように同じメソッドを複数回呼び出す場合だと、縦に長くなるし、同じコメントを何回も書かなければなりません。
書き手も読み手も、ちょっと疲れますよね。

では、どう書けば良いか。
最初のメソッドだけコメントを書いて、以降は書かないというのも、コードのスタイルが統一されておらず、おかしいです。
コメントが書かれているメソッドが削除され、書かれていないメソッドだけが残ったら、途端に引数の内容が分からなくなるでしょう。
また、前述した「似ているコードは似ているように見せる」というポイントからも外れてきます。

このような場合は、下記のように仮の引数を使い、説明用のコメントを用意すると分かりやすくなると思います。

// Post::getList($post_type, $posts_per_page, $page, $is_desc_sort);
//               投稿形式, 1ページあたりの表示件数, 表示ページ, 降順表示するか

$news_list = Post::getList('news',20,1,true);
$blog_list = Post::getList('blog',10,1,false);

メソッドを使った整列、整頓

どんどんコードを書いていくうちに、コードが長く複雑になり、読みにくくなっていく場合もあるでしょう。
その場合、別のメソッドに分けることで、コードを簡潔になるケースもあります。
そうすることで、1つのコードの中で似たような処理を何度も書かなくてはいけない部分を、メソッドの呼び出しだけで済むようになります。

それにより、以下の様な副作用が生じる場合があります。

  • コードが簡潔になり、可読性が上がる
  • メソッドの呼び出しが簡潔になり、テスト用コードの追加が簡単になる

縦の線をまっすぐにする

縦の線、縦の並びを整えるということです。
ちょっと面倒ですが、こんな感じに並びをキレイにすると流し読みしやすくなります。

$detail = $request->post('detail');
$name   = $request->post('name');
$url    = $equest->post('url');
$phone  = $request->post('phone');
$email  = $request->post('email');

こういう感じで。

と、終わりたいところですが、上のコードの中でタイプミスがあります。
どこだか分かりますか?

縦の並びを意識して整列させると、こういったミスを見つけやすくなると思います。
($urlの右辺、$requestが誤字)
でも、手間っちゃ手間です。時間に余裕がある時にやる感じの作業に感じます。
だからやらなくても良いかなとも思います。とは言え、流し読みはしやすくなるなとも感じるので、一概にやらない訳にもいかない作業です。
なので、時間的余裕と、今後そのコードを読む人への親切心がある場合に行う程度の作業と捉えておいてください。

一貫性と意味のある並び

「似ているコードは似ているように見せる」というポイントを実施するための書き方になります。

Webサイトのフォームのコーディングで、inputタグなど同じコードが複数ある場合、タグ内の属性の順番を統一させるなど、統一感を持たせることで、可読性が上がります。

<input type="text" name="name" value=""/>
<input type="text" name="email" value=""/>
<input type="text" name="tel" value=""/>
<input type="text" name="address" value=""/>

タグごとに属性の順番がバラバラだと、コードを読み進めていくうちに一貫性のなさにイライラしたり、逆に意味があるのではないかと疑問に思ったりするかもしれません。

また、そのフォームでの入力内容を制御する側のコードでも、inputタグが書かれた順番に処理していくと、統一感が生まれ理解しやすいコードになると思います。

// 処理する順番を、inputタグの順番に合わせる。
$name = $_POST['name'];
$email = $_POST['email'];
$tel = $_POST['tel'];
$address = $_POST['address'];

inputタグ順など基準となるものがない場合は、重要な項目順、アルファベット順など、ある一定の順番を決めておくといいと思います。

宣言をブロックにまとめる

コードを段落ごとに分割して読みやすくする、ということです。
ハンドラ系ならそれらで一塊に、ヘルパー系ならそれだけの塊でまとめます。
更に、先頭部分にコメントで何のブロックなのか、どういった処理を行うのかなど説明を書いておくと、より分かりやすくなるでしょう。

WordPressのテーマファイルの土台となるファイル群を自動生成する「_s」(underscores)というサービスがあります。

_s

作成したいテーマ名を入力すると、ヘッダー、フッター、サイドバーや基本的なCSSファイルなどを生成してくれます。
その中のstyle.cssには、ファイル内でスタイル定義する内容の一覧表がコメントで書かれています。

更に項目ごと(ヘッダー部分、サイドバー部分)のスタイル定義を開始する前にコメントで、何のスタイル定義なのかを明記しており、手を加えやすい構成になっています。

個人的な好みと一貫性

最後に、個人的な好みに近いことです。
プロジェクト、チームごとの決まり事として、コーディング規約を決めておくと良いよという話です。
ifの「{」をifと一緒の行にするか、1行にするかとか、そういう話ですね。

if($hoge){
}
//もしくは
if($hoge)
{
}

コーディング規約は、個人的な好みで決める前に、使用するプログラム言語にも左右されるので、そこは注意しておく必要があります。

また、プロジェクトや社内ルールなどで決まったコーディング規約があるなら、それに合わせた方が良いでしょう。

規約が曖昧だったり、統一されていないと、コードが読みにくくなります。
プログラム言語、フレームワークによって、規約は様々あります。

まとめ

いつも書いていくと長くなるのですが、まとめてしまうとこんなに簡潔なことになります。

  • 複数のコードブロックで、同じようなコードがあれば、シルエット(見た目)も同じようにすべき。
  • コードの「列」を整列すれば、概要が把握しやすい。
  • 意味ある順番を選び、常に守る。(例:inputの要素の並び)
  • 空行を使い、大きなブロックを論理的な「段落」にする。
  • コーディング規約などプロジェクト、チームごとの規約に準拠した書き方をする。

まずは仕様通りに、期待した動きができるようコードを書き進めるのが第一ですが、書き進めていくなかで、これまで書いてきたコードを振り返ったりする場合もあると思います。
その際に、ゴチャゴチャしすぎて分からなくなるようでは、どんなに動くコードでも、理解できなければ、修正できなければ、いざという時困ったことになります。

そのような事を防ぐためにも、日頃から少しずつ、今回のコードの整理を行うことで、読みやすいコードに近づくことができると思います。
それでは、また。

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

『リーダブルコード』を読みました。(2)

タグ: | 投稿日: 投稿者:

こんにちは、ニタです。
『リーダブルコード』まとめ、第2回目は変数や関数などの名前について書いていきたいと思います。

良い名前とは?

コードを書いていて、変数名や関数名を決める時、どのように決めていますか?書いているコードの役割、処理の内容を考慮したり、その時の気分だったりでしょうか。

その時の気分で決めるのは、お薦めできませんが、ここでも「理解しやすいコード」を意識した名前付け、「良い名前」が大事になってくると思います。

良い名前とは、変数で言えば、変数の目的や値の内容(実際の値ではなく)がわかり、誤解されないものを指すと思います。

「名は体を表す」と言いますし。
では、良い名前付けには、どういった事を気をつけていけば良いのでしょうか。

明確な単語を選ぶ

曖昧な意味を持つ単語、色々な意味に捉えられる単語は避けたほうが良いでしょう。

ユーザー情報を登録する関数名で、setUserData()に比べてinsertUserData()の方が分かりやすいと思います。SQLのINSERT文で慣れ親しんでいるのもありますし、set…だと何にsetするのか明確ではないと思います。
セッションに登録するのなら、setを使ってsetSession()のような名前だと良いと思います。

また、処理内容の重たさも考慮した名前を選ぶのも重要になってくると思います。
他の人が書いたコードを読んでいて、関数の名前で、その関数が何をしているかおおよそ予想がつく場合もあるでしょう。 

ですが、たまに、よく見かけるgetXXX()のような関数名で、数100行にも渡り、DBの色んなテーブルや外部サービスからデータを取得し計算し…と、複雑かつ時間のかかる処理が書かれている場合もあります。 

get…から始まる関数名は、いろんなコードで見かけるし、これまで何度も書いたことのある関数名だと思いませんか?

なので、getXXX()は「処理が軽そうなイメージ」が持たれるため、前述のような重たい処理の関数の場合は、名前だけでは推測しにくい、普段慣れ親しんでいないような単語を選ぶのが良いと思います。
computeXXX()とかcombineXXX()のようなものに変えてみると、気を引きやすいのではないでしょうか。
もちろん、軽い処理であれば、getXXX()などの慣れ親しんだ単語を使うのは、分かりやすいし良いと思います。

 

他の意味と間違わられないように、状況に合わせた単語選びを

明確な単語を選ぶとしても、1つの動作に対し英単語も結構な数があります。 類語辞典や翻訳サイトや辞典サイトなどで調べて、見栄えの良い単語を使ってみると、他の関数と差別化が図れると思います。

ただし、やり過ぎには注意しましょう。
phpのexplode()とsplit()は、返り値は同じですが、処理方法、引数の内容は異なります。 この2つの関数の違いを名前だけでは判断が出来ません。

また、単語選びを横着して、他の意味に間違われるような単語を選びがちになるケースもあります。
面倒だからと、filter()なんて名前にした場合、何を取捨選択する関数なのかパッと見分かりません。
select(選択)するのかもしれないし、逆にexclude(除外)するのかもしれません。

 

名前に情報がないもの、汎用的な名前は避ける(※ただし状況を選ぶ)

名前だけである程度内容や変数の値が分かるように、いちいち考え、調べて命名している時間がない場合もあるでしょう。
そんな時やってしまいがちなのが、$tmp,$returnなどの汎用的な変数名だと思います。
気持ちはわかるのですが、やはり名前に情報を持たないものは避けた方が良いです。
後から困るのは自分だし、他の人が読んだ時理解するのに時間がかかるでしょう。

ただし、以下の場合は例外として使用してもいいと思います。

  • 使用期間が短く、他に渡されたり何度も書き換えられない。
  • この変数には他の役割がない、他の処理で使えないと分かる場合。

これらに該当しないが、どうしても使いたい場合は、それ相応の理由をコメントなどに明記しておくべきでしょう。

ただし、「時間がない」「適切な名前が思い浮かばない」といった理由は、改修時や他の人がそのコードを読む時に困るので、やめましょう。バグの温床です。

また、ループ処理のイテレータに、i,j,kなどを使用していると思いますが、ループが複数ネストする場合やループが長い場合、どれがどのループのイテレータなのかi,j,kだけでは分かりにくくなりませんか?
そのような場合は、category_i,users_i,orders_iというように、説明的な変数名を使用すると分かりやすいと思います。

 

具体的な名前を使う

たまに、他の関数や変数との差別化を測りすぎて、抽象的な名前になってしまう場合もあるかもしれません。僕も前科があります(笑)。
WordPressのプラグインで、プラグインを有効化した時実行される関数をhelloRucy()、アンインストール時に実行される関数をgoodbyRucy()としてました。
開発当時は、自分が分かれば良いやと名づけたのですが、オープンソースのプラグインでそれは無いなと反省しています。
他の関数、変数と差別化を図る場合は、具体的な名前で考えたほうがいいでしょう。

 

プレフィックス、サフィックスなどを使用し、情報、属性を追加する。

コードが長くなっていって、変数名が一つの単語だけでは分かりにくくなる場合もあるでしょう。

例えば、$startの単位は何なのか?$limitは最大値なのか最小値なのか、単位は何なのか。
$startの場合、単位がミリ秒なら、$start_msというようにサフィックスをつけると分かりやすくなるでしょう。
$limitにしても、$max_page, $min_pageのように限界値を示すプレフィックスを付けると良いでしょう。

範囲を指定するような変数の場合は、$first_…, $last_…または、 $begin_…, $end_…などのプレフィックスに情報を追加して、分かりやすくなると思います。

その他にも、プレフィックスに属性を追加するとコードを追っていかなくても、変数の内容が分かる場合もあります。

  • パスワードを代入する変数$passwordの値が、暗号化前の平文の場合、$plain_passwordとする。
  • ブール値なら、is_…, has_…, can_…, should_…といったプレフィックスを付ける。この場合、肯定形の名前にすると、if文の条件分岐などが分かりやすくなる。

 

名前の長さを決める

具体的で、プレフィックスを追加していくと長い変数名、関数名になっていくでしょう。 打つのが面倒で、短い名前にする場合もあると思います。

ですが、cssで、padding-top:10pxの指定するクラス名「pt10」は、やはり理解するのに時間がかかります。
名前が短すぎると、型とか意味が名前から分からなくなり、バグの温床になります。
また、処理の中で長く使う(スコープが大きい)変数には、長く説明的な名前を付けると、コードを読み進めるときも分かりやすくなるでしょう。

しかし、長すぎる名前で省略しても分かる場合は、短くしても良いでしょう。
ConvertToString()という関数名で、Convertがなくても、ToStringだけでも文字列に変換する関数だとわかると思います。

さて、逐一これまで書いてきたことを実施していくと、時間ばかり取られるのでは?と思うでしょう。
名前を決めるのにも、長くなった変数名を打つのも時間がかかります。
全てに対して実施しなさいということではなく、「変数の意味を取り違えたらバグになりそうな箇所だけ」でもいいと思います。
つまり「変数の意味を理解してもらわないと困る場合」は、変数名をしっかり考えるべきということです。

 

プロジェクト、チーム固有のルール、省略形、頭文字は避ける。

名前の長さやプレフィックス、サフィックスなど命名規約を決めていくのは良いことですが、度が行き過ぎて、プロジェクト固有のルールになり過ぎないよう注意が必要です。
フレームワークを使用している場合(symfony 1.x系のコアクラスにはsfが先頭に付くlowerCamelで書かれています)や、共通認識としてある汎用的な省略形は例外だと思いますが。
また、フレームワークや言語固有の命名規則もあるので、それに則るのも良いでしょう。

 

まとめ

今回も長々と書いてしまいましたが、まとめると、こんな感じになります。

  • 明確な単語を選びを。
  • 汎用的な単語は避け、具体的な名前を使い、内容を詳細に説明する。※明確な理由があれば別
  • 変数名、関数名に重要な情報を追加する。
  • スコープの大きな変数には長い名前を付ける。
  • 独自命名規則は避ける。バグの温床。
  • 最善の名前は、誤解されない名前。

コード内の処理がしっかり動いていても、変数名、関数名がおざなりになっていると、いざメンテナンス、改修時に工数が取られる原因につながります。
多少面倒かと思いますが、気をつけていただければと思います。
それでは、また。

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

『リーダブルコード』を読みました。(1)

こんにちは、ニタです。

最近、オライリーの『リーダブルコード』という本を読みました。
その名の通り、読みやすいコードを書くためのノウハウが書かれた、大変ためになる内容でした。

どれだけ短期間でプログラムが出来たとしても、後で他の人が修正を加える事になった時、どこで何の機能をどのように書いているのか、全く理解できなければ、メンテナンス性の低いコードになってしまいます。
最初にプログラムを書いた人が、ずっと保守していかなければならないのでしょうか。
その人が設計内容を忘れてしまったら、どう保守していけばいいのでしょう。

様々な開発言語や、日々新しく出てくるシステムに触れてみるのも大事ですが、プログラムを書くためには、この本に書かれてあるような、もっと基礎的なことが大事だと思います。
これから数回に分けて、この本について感じたこと、大事だと思ったことを書いていきたいと思います。
よろしくお願い致します。

理解しやすいコードとは

僕がプログラマという肩書を戴き、プログラミングの仕事をもらうようになってから、自分で書いたものはともかく、様々なフレームワーク、他のプログラマの方々のコード…いろんなコードを目にしてきました。
共通して分かっていることは、「読みやすいコードは分かりやすい」ということです。
まあ、当然なことなんですけどね…。 そう、コードは理解しやすい内容じゃないといけません。
じゃあ「理解しやすい」って何でしょうか。

コードの意味が分かるということではないでしょうか。
他の人(プロジェクトに参加していない同僚や新しく参加する人)や未来の自分が、そのコードを読んで、短時間で変更を加える事が出来たり、バグを見つけることができることが「理解できる」ということではないでしょうか。
自分が書いたコードの内容って、時間が経つと忘れたりしますしね…。

「簡潔」と「安心」どちらが大事なのでしょう

また、簡潔なコードは理解しやすいと感じる場合もあるでしょう。コード数、行数が少なければその分コードを読む時間は短縮されますし。
では、この場合はどうでしょうか。

return ($foo > 0) ? $hoge * (1 * $var) : $hoge / (1 * $var);

ワンライナーで書かれていて、スマートって感じです。
これをこう書くと、どう感じるでしょうか。

if($foo > 0){
return $hoge * (1 * $var);
} else {
return $hoge / (1 * $var);
}

前者のほうが簡潔なコードですが、読んだときパッと理解できたのは後者のほうではないでしょうか。
後者の方が読んでいて分かりやすく安心します。
簡潔なコードも大事ですが、読んだときの「安心さ」も、分かりやすいコードとして大事なのではないでしょうか。

そのコード、後で読んで理解できますか?

ところで、自分が書いたコードを「理解しやすいコード」と思って書いていますか?
例えそう思っていたとしても、それは自分が仕様を理解し、設計しているからだけではないでしょうか。

前述しましたが、そのプロジェクトに関係のない同僚、もしくはそのプロジェクトに新しく入った人が、そのコードを読んで理解できる内容かどうかが大事だと思うのです。

新しくコードを書くとき、修正するとき、すぐに手を動かそうとせず、
「これから書くコードは理解しやすいコードだろうか?」と一歩引いてみることも大事だと思います。

まとめ

以上のことから、理解しやすいコードを書くためのポイントは、このようなことではないでしょうか。

  • コードは(未来の自分も含む)他人が、最短時間で理解できるように書かなければならない。
  • コードは短くした方がいい。でも「理解するまでにかかる時間」を短くする方が優先。
  • コードを書くとき、すぐにコードの修正に入らず、「このコードは理解しやすいだろうか?」と一歩引いて自問自答し、考えることから始める。

次回は、変数名、関数名などの名前について触れてみたいと思います。
それでは、また。

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

cssで横幅計算

タグ: | 投稿日: 投稿者:

こんにちは、kikuです。

今日はcssで横幅を決める時に楽になる方法を記述していこうと思います。

例えばtwitterのタイムラインのようなUIで、左にサイズ固定の画像があり右に残りのサイズで何かがあるような場合。
画像表示領域の横幅を60pxにしていた場合、残りは 100% – 60px ですよね。

これを実現するのにpaddingやらmarginやらを設定したりするのはとても面倒で馬鹿らしいです。

そこでこれ!calc()!!計算ができます。こんな風に使えます! 

width: calc(100% – 60px);

最初これを見た時は目から鱗が落ちました。
というか最初からこういうものがあっても良いと、いや、寧ろあるべきだと思いました。
しかし残念ながらAndroidでは使えないようです。

IE IE9〜
Chrome Chrome26〜 (Chrome19から25までは -webkit- をつける)
Firefox Firefox16〜(Firefox4から15までは -moz- をつける)
Safari Safari6.1,Safari7.0〜(Safari6.0は -webkit- をつける)
Opera Opera15〜
iOS iOS7〜
Android 未対応(Chromeなら対応している模様)

使い方はこんな感じにするのが基本でしょうか。

.hoge {
  width:96%;
  width:-webkit-calc(100% – 60px);
  width:calc(100% – 60px);
}

ではストレスのないcssライフを過ごしましょう。

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

GitLab7.1.1でGitHub Flowを実践してみた!

タグ: , | 投稿日: 投稿者:

こんにちは。

最近はもっぱらチーム開発を効率よくできる仕組みを考える事に必死なベッチです。

そんな時に電車でいろいろ調べていたら以下のスライドをみつけました。

少人数チームにおけるプロジェクト管理のベストプラクティス

このスライドでは以下の2つの事を実践する事で品質が向上すると提唱しています。

  1. コードの強制自動テスト
  2. プログラマ同士のコードレビュー

最近開発チーム内でもコードレビューの必要性について議論されてきているのでこのタイミングで開発フローにのせれるように検証してみようと考えました。

ただし「1.コードの強制自動テスト」を強制するのは現場が混乱するのでとりあえずは保留にしておきます。

スライドではRedmine、GitLab、Jenkinsを使った開発フローが紹介されていますが、いきなり全てを連携してというのは無理なので、まずは「2.プログラマ同士のコードレビュー」を実現する為にGitLabを使った「GitHub Flow」を実践してみようというのがこの記事の本題です。

GitHub Flowについてはググればいくらでもでてきますが簡単に説明すると以下のフローになります。

  1. 作業をする時はmasterブランチからトピックブランチを作成する
  2. ローカルで作業、コミット、pushをする
  3. Pull Requestによって誰かにコードレビューを依頼する
  4. コードレビューで問題ない事を確認後、masterブランチにトピックブランチをマージする
  5. 即時にmasterブランチをプロダクション環境へデプロイする

なお、GitLabではPull Requestという名前ではなくMerge Requestになっていますが機能はほぼ同じだと思います。

それでは最新のGitLab7.1.1でGitHub Flowを実践したいと思います。

以下、長くなりますが結論から言うと問題なく実現できました。

ストーリー

地球に挨拶をする(「Hello, Earth!」という文字列を表示する)プログラムがあります。
今回、さらに宇宙にも挨拶をする機能を追加して下さい。

登場人物

  • 開発者A(開発担当者)
  • レビュアB(開発者Aのコードのレビュー担当者)

手順

1.作業をする時はmasterブランチからトピックブランチを作成する

この作業をするのは開発担当者なのかプロジェクト管理者なの かはそのチームで決めておく方が良いと思います。

GitLabにて対象のプロジェクトを選択後、「Commits」をクリックします。

1-1

現在masterブランチだけなのでブランチ数が「1」となっていますが、この「Branches」タブ部分をクリックします。

1-2

「New branch」ボタンをクリックします。

1-3

トピックブランチ名と、元のブランチを選択して「Create branch」ボタンをクリックします。

トピックブランチの名前は他の開発者がみてもあなたが何の機能を実装しようとしているか、どんなバグを修正しようとしているか一目でわかるようにします。
これは自分自信にとっても、何の機能を実装すべきかを明確にしてくれるので非常に有益です。

元のブランチはGitHub Flowでは必ず「master」ブランチとなります。

1-4

トピックブランチが作成されている事を確認します。

1-7

2.ローカルで作業、コミット、pushをする

開発者Aはトピックブランチをローカル環境にpullし、チェックアウトします。

トピックブランチ上で要件を実現する為の実装作業を行います。

ここで大事なのは以下の2点です。

  1. トピック以外の作業は絶対にコミットしない事
  2. レビュアや他の開発者の為に意図が伝わる粒度でコミットする事

 

3.Pull Requestによって誰かにコードレビューを依頼する

開発担当者はコードレビューを依頼する前に一度リモートリポジトリにpushしておきます。

push後、該当プロジェクト画面の「Commits」をクリックし、コミット一覧画面で「Compare」タブをクリックします。

4-1

差分を確認し、問題なければ「Make a merge request」ボタンをクリックします。

4-3

マージリクエスト画面が表示されるので以下の項目を入力して「Submit merge request」ボタンをクリックします。

  1. タイトル
  2. 概要
  3. 担当者(レビュア)
  4. マイルストン(今回は使用しない。GitHub Flowでは不要かも)

後から気づきましたが、ここで入力した内容はmasterブランチにマージする際のデフォルトのコミットメッセージとなります。

よって、このトピックブランチでどんな変更が加わるのかという内容を簡潔に記載するのが適切でしょう。

つまり以下のような「レビュー依頼」というタイトルは間違いですので注意して下さい!

4-4

マージリクエスト完了です。

※マージリクエストを行うまでのフローはこの他にもあります。

4-5

レビュアBはマージリクエスト申請のメールを受信したので確認をします。

「Changes」タブをクリックする事でソースコードの差分が確認できます。

4-6

4-7

サイドバイサイドでの差分確認も可能です。

4-7-a

レビュアBがソースコードを確認したところ、指摘事項があったので開発者Aに対してコメントを送信します。

4-8

4-9

開発者Aは該当のマージリクエストにコメントされた旨のメールを受信したので内容を確認します。

4-10

開発者Aはコメントの内容にもとづいてソースコードを修正し、コミット、pushします。

push後、対象のマージリクエストでコメントを入力します。

4-10

レビュアBはコメントを受けてソースコードを確認します。

4-11

レビュアBは意図したものと違うコードがあがってきたので指摘をしようと思いましたが、リリースまでに時間がなかった事と、その内容が軽微なものであったので自分自身で修正する事にしました。

「Edit」ボタンをクリックする事で画面上から直接修正し、コミットが可能となります。

4-12

コミット完了です。

なんか画面上でソースコード修正してコミットまでするのって変な感じです。

実際はあまり利用しないでしょうが、GitLabの機能紹介という事でやってみました。

4-13

その後、開発者Aはふと気づいた事があるのでソースコードに対してコメントしました。

※GitLabはソースコードの該当箇所にたいしてもコメントが可能です。

4-14

レビュアBはその内容を確認して再度自分自身で修正、コミットし、その旨コメントしました。

4-15

 

4.コードレビューで問題ない事を確認後、masterブランチにトピックブランチをマージする

リリースできる状態となりましたのでレビュアBはmasterブランチに今回のトピックブランチをマージします。

該当のマージリクエスト画面にある「Accept merge request」ボタンをクリックします。

 

4-16

問題なくマージができました。

4-18

 

5.即時にmasterブランチをプロダクション環境へデプロイする

プロダクション環境へのデプロイ方法についてはまだ未検討です。

製品にもよりますが、最終形はDockerを利用したブルーグリーンデプロイメントが理想なんじゃないかと思ったりしています。

弊社ではこの部分についてはまだ当分手動になりそうです。

 

今後はRedmineとの連携、その次はJenkinsとの連携をやっていきます。

 

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

任意の名前でGitコミット

タグ: , , | 投稿日: 投稿者:

こんにちはモリです。

今年の暑い夏もお盆まで!というニュースを見て一安心しました。。。

今回も備忘録として書いていきます。

今回はGitのコミットをする際に任意の名前にてコミットを行うやり方を紹介します。

commit の引数に[–author]を指定すると任意のユーザー名を指定することで可能になります。

git commit --author='mori <mori@fairway-corp.co.jp>' -m 'コメントを入力'

19

これで、いちいちconfigを編集しないで済みます。

お試しあれ。

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

WordPressプラグインの申請公開方法

タグ: , | 投稿日: 投稿者:

こんにちは、ニタです。

またしてもWordPressネタですが、プラグインを作ったのなら公開してみるのも良いと思います。WordPressをお使いの方々に多少貢献出来るかもしれません。
そこで、今回はWordPressプラグインの申請、公開方法をまとめした。

事前準備・用意するもの

wordpress.orgのアカウント

アカウント登録はこちらからできます。最低でもusernameとemailが入力していれば登録できます。
登録すると、入力したメールアドレスにwordpress.orgからusernameとpasswordが記載されたメールが送られてきます。
こちらでログインすると、プラグインとテーマの申請、WordPressのフォーラムへの参加ができるようになります。

プラグインファイル一式

当然ですが、動作テストし問題ない状態でリリースしましょう。
※今回は割愛しますが、管理画面などに項目を追加するようなプラグインの場合、表示項目の国際化処理をするのをおすすめします。

readme.txtを書く

プラグインには、readme.txtを追加する必要があります。
このtxtファイルを基に、WordPressプラグインページが作成されます。
表記内容は、WordPressのreadmeファイルの標準に沿ったものになり、MarkDown記法で記載していきます。
readme.txtは、プラグインディレクトリの直下に置きます。
表記例はこんな感じ。

=== Rucy ===
Contributors: gips-nita
Tags: post, update content, update post, update page, schedule update, reserve update, reservation update, rucy, Rucy
Requires at least: 3.5+
Tested up to: 3.9
Stable tag: 0.2.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Add reservation update published content.

== Description ==

You can automatically update the DateTime specified posts published by this plugin.

Post and Page, specify custom post type is also possible.

But in your WordPress, this plugin is a prerequisite is that the reservation can be posted (can use WP_CRON is required).

== Installation ==

You can install this plugin directly from your WordPress dashboard:

 1. Go to the *Plugins* menu and click *Add New*.
 2. Search for *rucy*.
 3. Click *Install Now* next to the *rucy* plugin.
 4. Activate the plugin.
 5. Configure reservation post type in *rucy* in *Setting* menu.

== Screenshots ==

1. Added Reservation Content Editor in Editor.
2. Reservation Content Type Setting Page.

== Frequently Asked Questions ==

== Changelog ==

= 0.2.0 =
* 2014-06-13
* fixed Bugs.
* change input type reservation datetime.
* add check validation reservation datetime is past.

= 0.1.2 =
* 2014-05-04
* add reservation datetime post updated messages.
* add "revision" and "attachment" in invalid custom post type Setting
* refactoring

= 0.1.1 =
* 2014-04-25
* if accept checkbox unchecked , clean schedule update content.
* modified revision post_type do not add schedule update.
* add donation link.

= 0.1.0 =
* 2014-04-18 First release

表記する項目は、以下のとおり。MarkDown記法で記載していきます。
=== [プラグイン名] ===

Contributors: [WordPress.orgで登録したusername]
Tags: [プラグインを検索した時にヒットされやすいタグをカンマ区切りで記載]
Requires at least: [このプラグインが動作保証出来るWordPressの最低バージョン]
Tested up to: [プラグインの動作テストで使用したWordPressのバージョン]
Stable tag: [動作保証出来るプラグインのバージョン]
License: GPLv2 or later (ライセンス)
License URI: http://www.gnu.org/licenses/gpl-2.0.html (ライセンスのURL)

== Description == 詳細説明
== Installation == インストール方法
== Screenshots == スクリーンショットの説明。
1.一番目のスクリーンショットの画像の説明。
スクリーンショット画像は、上記の番号に対応するスクリーンショット画像を用意します。
ファイル名は、screenshot-[番号].[拡張子]
拡張子は、png/jpg/jpeg/gifのいずれかです。
画像ファイルは、readme.txtと同じ階層に配置しておきます。
== Frequently Asked Questions == よくある質問。初回は項目があるだけでも構いませんが、想定される質問を記述するのもいいでしょう。
== Changelog == 更新履歴。更新する度に追記していきます。

wp-plugin_dir

説明用コメントアウトの更新

プラグインのphpファイルに、コメントアウトでプラグインの説明を記述していると思います。
以前、最低でもPlugin NameだけでOKと説明しましたが、こちらも更新しておきましょう。

/*
Plugin Name: (プラグインの名前)
Plugin URI: (プラグインの説明と更新を示すページの URI)
Description: (プラグインの短い説明)
Version: (プラグインのバージョン番号。例: 1.0)
Author: (プラグイン作者の名前)
Author URI: (プラグイン作者の URI)
License: (ライセンス名の「スラッグ」 例: GPL2)
*/

申請作業

wordpress.orgにアップロード

さて、色々準備をしてきましたが、ようやく申請です。
プラグインファイルとreadme.txt、スクリーンショット画像をまとめたzipファイルを作成し、外部からアクセス可能なサーバにアップロードしておきます。

そして、WordPressのプラグイン申請ページで、各入力内容を記入し申請します。

WordPress_›_Requests_«_WordPress_Plugins

これで申請完了です。
審査が通過し承認されると、WordPress.orgからその旨のメールが届きます。

申請ページにもありますが、現在申請している数と審査中の数が出ていますので、タイミングによっては結構待たされる場合もあるようです。
僕が以前申請した際は、2、3日程度でした。
もし、審査に引っかかた場合、その理由などが書かれたメールが届くそうなので、そちらを基に修正するなり、WordPress.orgとやり取りし、再申請となります。

審査が通ったら

審査を無事通過し公開可能となりますと、そのプラグイン用のsvnのリポジトリが与えられます。
前述の、承認されたという連絡のメールにリポジトリのURLが記載されているので、ローカル環境にリポジトリをチェックアウトし、プラグインファイルなどを追加し、コミットします。

// 作業ディレクトリを作成
mkdir ~/workspace/wordpress-plugin/
// チェックアウト
svn co [wordpress.orgから届いたリポジトリURL] ~/workspace/wordpress-plugin/

// プラグインなどファイルコピー
cp file/to/path/my-plugin.php ~/workspace/wordpress-plugin/trunk/my-plugin.php
cp file/to/path/readme.txt ~/workspace/wordpress-plugin/trunk/readme.txt

// svnに追加
svn add trunk/*

// コミット
svn ci -m 'コミットメッセージ'

参照:http://wordpress.org/plugins/about/svn/

コミットが完了すると、15分程度でWordPress.orgに反映され、めでたく公開となります。
以後、プラグインの更新は、このsvnからコミットし更新して反映していきます。

また、コミットする際、svnのtrunkやtagsと同階層にassetsディレクトリを作成し画像を追加すると、WordPress.orgのプラグインページのアイキャッチ的な部分に画像が反映されます。

画像ファイルの仕様は以下になります。
ファイル名:banner-772×250.png
幅:772px
高さ:250pxになります。
画像の種類は、jpg,pngのどちらかです。

また高解像度ディスプレイ向けに、以下の仕様の画像を用意すると、サイト側で動的に切り替えてくれます。
ファイル名:banner-1544×500.png
幅:1544px
高さ:500px

WordPress_›_Rucy_«_WordPress_Plugins

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

Video For Linux(v4l)について

タグ: , | 投稿日: 投稿者:

Webカメラなどから撮影した動画を取り込んで××するんならWindowsなら今時はMedia Foundationでしょうか?いまだにDirectShowが便利なんでしょうか?
以前にDirectShowでフィルターを書いて色々実装しましたが、フィルターグラフの接続とかが面倒だったりしました。最近の私の場合はLinux(Ubuntu)なので、DirectShowのお世話になっていません。

以前Linuxでカメラでキャプチャした画像を××するのに実際に私が行ったのは、BlackMagicDesign社のビデオボードDecklink/Intensityシリーズにカメラを接続する形で行いました。BlackMagic Decklink/IntensityにはBlackmagic DeckLink SDKを利用してアクセスし、画像の生データを得ました。

BlackMagic Decklink/Intensityにカメラをさしても/dev/videoXが生えてくるわけじゃないので使えませんでしたが、今度はV4lです

※実際にはOpenCvでキャプチャ処理とかも書きましたが、これはOpenCv内部からv4lを呼んでるんですね

1.オープン

 int open(const char *pathname, int flags);
 で行う。pathnameに"/dev/video0"とか、flagsにはO_RDONLY, O_RDWR
 とか設定

2.カメラ操作
※カメラで操作可能な設定をいじったり、バッファを関連付けたり、画素データを取り込んだりします

int ioctl(int fd, int request, ...);
で行う。リクエスト(request)については下に例示

リクエストの例

VIDIOC_QUERYCAP カメラデバイスに保有する機能を問い合わせます
VIDIOC_S_FMT カメラデバイスに画像データのフォーマットを設定
VIDIOC_G_FMT カメラデバイスに画像データのフォーマットを取得
VIDIOC_REQBUFS 画像データ用のバッファをカメラデバイスに要求
VIDIOC_QUERYBUF VIDIOC_REQBUFSで要求したバッファの情報を取得
VIDIOC_QBUF バッファをエンキューします
VIDIOC_DQBUF バッファをデキューします
VIDIOC_STREAMON キャプチャ開始
VIDIOC_STREAMOFF キャプチャ停止

3.select()で待つ(ってことはpoll()も使えんのか)ことで、キューにデータ入ったか確認する
4.(カメラの)バッファをユーザープログラムにマッピングする

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
で行う

※ユーザー定義のポインタにバッファアドレスをマッピングする

★で、手順的には

  1. カメラデバイスオープン
  2. 設定(解像度など)
  3. バッファ要求
  4. バッファにポインタ割り当て
  5. バッファをキュー(あらかじめ)
  6. キャプチャ開始
  7. デキュー/エンキュー繰り返し→フレームデータの処理
  8. キャプチャ終了
  9. 後始末

となります。

以上ですが、今度は、ちゃんと何かつくりますね。これとffmpegのlibで何か作るとか、OpenCvと組み合わせるとかで・・・・

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook

Android用 FFmpeg を利用した Androidアプリ

タグ: , | 投稿日: 投稿者:

お久しぶりです。
takeです。

前回の記事はこちら

前回 libffmpeg.so のビルドが完了したので、今回はこのライブラリを使ってJNI部分を作成します。

JNIを作成するため、Eclipse(luna)に以下の設定が必要です。
・C/C++開発環境(Help->Install New Software)
http://download.eclipse.org/releases/luna を入力し、以下を選択する
・Programming Languages -> C/C++ Development Tools
・Programming Languages -> C/C++ Development Tools SDK
・Programming Languages -> C/C++ Library API Documentation Hover Help

・NDKプラグインのインストール(Help->Install New Software)
https://dl-ssl.google.com/android/eclipse/ を入力し、以下を選択する
・Developer Tools -> Android Native Development Tools

・NDKの設定(Window->Preferences->Android->NDK)
前回の記事でNDKを展開したディレクトリを指定します。

以上で設定は終わりです。

次は、プロジェクトを作成します。
File -> New -> Android Application Project を選択します。
アプリケーション名を適当にいれてプロジェクトを作ります。

プロジェクト作成後、プロジェクトメニューから Android Tools -> Add Native Support を選択します。
JNIフォルダが作成されて準備ができました。

ここからが大変です。自分も試行錯誤した結果なので誤ってる個所があるかもしれませんがご容赦ください。

まず、FFMpegライブラリのコピーを実施します。
・jniフォルダにlibフォルダを作成します。
・build/ffmpeg/armv7/libffmpeg.so を jni/lib へ
・build/ffmpeg/armv7/include を jni/include へ

JNIフォルダにある Android.mk ファイルを編集します。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_EXPORT_C_INCLUDES := include
LOCAL_SRC_FILES := lib/libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := FFMpegTest
LOCAL_SRC_FILES := FFMpegTest.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid  -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg
include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/cpufeatures)

※今回のプロジェクトはFFMpegTestとしています。

編集内容を保存後、プロジェクトメニューから「Build Project」を実施すると
JNIライブラリが作成されているはずです。
(I/Fがないのでなにもできませんが。。。)

長くなってしまったので、続きはまた次回!!

Share on Google+Tweet about this on TwitterShare on StumbleUponShare on Facebook