• スポンサードリンク

AndroidでByteBufferからbyte配列に値を読み込む方法

Android

なんちゅう記事書くねんとか言わないでね(^_^;)

GitHubで公開中のUVCCameraプロジェクトでフレームデータをbyte配列とかで取得したいって問い合わせがよく来るので、先日更新して専用メソッドを追加しました。詳しくはこっちの記事を見てもらうことにして、早速こんな問い合わせが来ました。

ByteBufferからbyte配列に値を取り出そうとしてこんなコードを試したみたいです。

でも動かないよぉ〜(;_;)ってメールです(英語だっだけど)。

突っ込みどころがいくつか有るのは別にして、一見ちゃんと動きそうに見えませんか?と言うか#onFrameでの処理でなければ動く場合もあります。確かに間違っては無いんですよね。でも完全に正しいかって言われると抜けてることが有るのです。まぁだからこそ動かないコードだった訳だけど。

ちなみに、
#duplicateがおかしい訳ではありませんので、

と直接#getを呼ぶようにしてもダメですからね。#onFrameの場合は#duplicateを呼ぶことは悪影響の可能性しかありませんね。
 
 
 
 
余白を開けてみよう、直ぐに正解見ちゃ勉強にならんからね(笑)
 
 
 
 
どこがおかしいか判った人〜?ハーイ(^o^)/
でもこれは実際に試して悩んだ人にしかわからんかも。大体からして原因はアンドキュメンテッドなとこにあるんだから。
 
 
 
 
余白いっぺん隙間を開けてみようv(^_^)v
2時間程目をつむって横になっただけなので少しハイなのですm(__)m
 
 
 
 
UVCCameraライブラリのIFrameCallback#onFrameで実際に動くコードはこちら

どこが違うか判る?あえてコードにハイライトを付けてません(^_^;)
ちなみに今のAndroidの実装だとこれでもおっけぇー

でもこうする方がいいかも。

なにが原因かというと、大きくこの3つ。

  1. 最初2つのコードはJava側のVM内で確保されたbyte配列をbacking arrayとして持つByteBufferインスタンスの事しか考えてない
  2. 実際に#onFrameに来るByteBufferインスタンスはnative側で確保していて、しかもnativeのbyte配列をbacking arrayとして持つダイレクトバッファのインスタンスである
  3. 知っている限り全てのAndroidのバージョンではダイレクトバッファに対して#remaingを呼んでも正しい値を返さない(たぶんいつもゼロが返る)

3つ目が一番大事。ドキュメントに書かれてないけど。
ByteBufferのソースコードを追っかけてみると、JavaのVM内での操作でしか#remaingに関わる内部変数(フィールド)は更新されません。まぁそりゃそうかもしれんなぁとは思うけど。native側で生成したダイレクトバッファはJava側で余計なことをしなければ、position=0,limit=capacity=アクセス可能な配列の長さとなっています。

よくある間違いは、

のように、#flipを呼んじゃうことです。#flipは自分でByteBuffer値をputしていく時には定番の処理ですが、limitを現在位置(position)にセットしてからpositionをゼロにします。今のAndroidの実装ではnative側で生成したダイレクトバッファはpositionが最初からゼロなので#flipを呼んじゃうとposition=limit=remaing=0となってデータが無い事になってしまいます。

ちなみに、より一般化するのであれば、#hasArrayや#isDirectを使ってダイレクトバッファかどうかを判別して処理をすべきでしょう。でも#onFrameにはダイレクトバッファしか来ませんし将来においても変更する予定はありませんので、この場合にはダイレクトバッファ決め打ちで大丈夫です。

ちなみに、最初のコードでは受け取り用のbyte配列を#onFrameメソッド内で生成していました。#onFrameメソッドはカメラからの映像が届く度に呼び出されるので最大で30回/秒呼び出されます。まぁプリミティブ型の配列なので最近の機種なら殆ど問題無いとは思いますが、「ループ内や繰り替えし処理中にオブジェクトの生成は避ける」ってのがGCの影響を少なくしてJavaのパフォーマンスを少しでもましにする手段の1つなので気を付けましょう。

最後のコードではバッファサイズを確認して超えてれば生成し直すようにしてますが、#onFrameの場合は最初から引数に渡されるByteBufferのサイズはわかっているので決め打ちでも大丈夫です。
確保してあるバッファよりも大きいサイズが来た場合には、コードの性質に応じて次の5つのどれかで処理することになるでしょう。

  1. サイズを変更して受け取り用のbyte配列を再生成
  2. 例外を生成する
  3. byte配列に収まるだけを読み込む
  4. byte配列に収まるだけを読み込んで全部読み込めるまで処理を繰り返す
  5. あらかじめ決めた最大サイズ以下ならbyte配列を再生成、それも超えるなら1)〜4)のどれかで処理

しょうもない話を長々と引っ張ってしまいました。
お疲れ様でした。

« »

  • スポンサードリンク