回転が難しいというか、3つくらいこけてるとどこでこけてるかわからんね 2
- 純正のカメラアプリでもPORTRAITだけ対応だったりするので、回転してすぐカメラのプレビューが取れないとかはまあある話なのでは
- 単一方向で対応してその先はその後考えようか
- バッファとかの回転のマトリックスは固定値返したりとかあるみたいですね
- rotateとかtranslateの中心座標指定めっちゃ便利
- pauseからの復帰で戻ってくると
CameraDevice.StateCallback
のonErrorでエラーコード 1 = (ERROR_CAMERA_IN_USE
) を受け取る- このエラーは優先的にCamera APIを利用してるクライアントがありますよ、と言う意味だが、もちろんいまテスト用に書いてるものしか利用してないので、そのクライアントは自分のこととなる
- ロック、ロックお前なのか…
- 明日はあのサンプルの仰々しいロックの導入を試して見るかなー
- このエラーは優先的にCamera APIを利用してるクライアントがありますよ、と言う意味だが、もちろんいまテスト用に書いてるものしか利用してないので、そのクライアントは自分のこととなる
Matrix 中心座標を入れられるの便利
private Matrix getDirectionAdjustedMatrix() { // Surface.ROTATION_90 == landscape Matrix matrix = new Matrix(); int viewCenterX = (int) (mTextureView.getX() + mTextureView.getWidth() / 2); int viewCenterY = (int) (mTextureView.getY() + mTextureView.getHeight() / 2); matrix.postRotate(270 /* sensorOrientation などとの兼ね合いから決まる */, viewCenterX, viewCenterY); matrix.postScale((1.0f * IMAGE_WIDTH / IMAGE_HEIGHT) * (mTextureView.getWidth() / mTextureView.getHeight()), (1.0f * IMAGE_HEIGHT / IMAGE_WIDTH) * (mTextureView.getWidth() / mTextureView.getHeight()), viewCenterX, viewCenterY); return matrix; }
onPause -> onResumeの復帰がうまくいかない
@Override public void onPause() { if (mCaptureSession != null) { mCaptureSession.close(); mCaptureSession = null; } if (mImageReader != null) { mImageReader.close(); mImageReader = null; } super.onPause(); } @Override public void onResume() { super.onResume(); // When the screen is turned off and turned back on, the SurfaceTexture is already // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open // a camera and start preview from here (otherwise, we wait until the surface is ready in // the SurfaceTextureListener). if (mTextureView.isAvailable()) { openCamera(); } else { mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // 先ほどのカメラを開く部分をメソッド化した openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); } }
public static abstract class StateCallback { /** * An error code that can be reported by {@link #onError} * indicating that the camera device is in use already. * * <p> * This error can be produced when opening the camera fails due to the camera * being used by a higher-priority camera API client. * </p> * * @see #onError */ public static final int ERROR_CAMERA_IN_USE = 1;
- [既存課題]縦向きの場合と横向きの場合でちょうどいいrotationのMatrixを見つける
- 関連: http://mslgt.hatenablog.com/entry/2015/05/12/013013#rotatescreen
- 横向き or 縦向きだけの対応で十分では、となり、横向き固定の場合の行列見つけて安堵中
- [休止][既存課題]縦向きの場合と横向きの場合でそれぞれちょうどよくCameraのPreviewが表示されるようにする
- [新しい課題]Pauseからうまく復帰できない
回転が難しいというか、3つくらいこけてるとどこでこけてるかわからんね
- Previewを表示するSurfaceの大きさは枠はViewのサイズ、画像の大きさはTextureView.setTransformで渡すMatrixで設定(っぽい)
- CameraSessionから飛んでくる元画像の大きさはSurfaceTexture.setDefaultBufferSize でここでは向きの調整は入れられないっぽい
- TextureViewの縦横比は
TextureView.setAspectRatio()
で設定できる?
- LANDSCAPE用の回転 + 縮小をかけている場合、POTRAITではカメラのプレビューが表示されない
- ImageReaderの書き出す画像の向きがLANDSCAPE時に上下正しくなる向き(PORTRAITだと表示に利用する前に回転をかける必要がある)
- カメラのデバイスがCameraSessionで飛ばしてくる画像が、端末の向きに対してどれくらい回転しているかは
CameraCharacteristics.SENSOR_ORIENTATION
で取れるが、カメラによって固定っぽい - Activityの向いている方向は
getWindowManager().getDefaultDisplay().getRotation()
気になってること
- [新しい課題]縦向きの場合と横向きの場合でちょうどいいrotationのMatrixを見つける
- [新しい課題]縦向きの場合と横向きの場合でそれぞれちょうどよくCameraのPreviewが表示されるようにする
CameraDevice closeのタイミングとCameraCaptureSession closeのタイミングについて
https://github.com/googlesamples/android-Camera2Basic のサンプルから確認してメモする。
まとめ
CameraDevice
はCameraDevice.StateCallback
のコールバックの中でcloseするCameraCaptureSession
はonPause
でclose
していた
コード
/** * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. */ private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { // This method is called when the camera is opened. We start camera preview here. mCameraOpenCloseLock.release(); mCameraDevice = cameraDevice; createCameraPreviewSession(); } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } @Override public void onError(@NonNull CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; Activity activity = getActivity(); if (null != activity) { activity.finish(); } } };
@Override public void onPause() { closeCamera(); stopBackgroundThread(); super.onPause(); }
/** * Closes the current {@link CameraDevice}. */ private void closeCamera() { try { mCameraOpenCloseLock.acquire(); if (null != mCaptureSession) { mCaptureSession.close(); mCaptureSession = null; } if (null != mCameraDevice) { mCameraDevice.close(); mCameraDevice = null; } if (null != mImageReader) { mImageReader.close(); mImageReader = null; } } catch (InterruptedException e) { throw new RuntimeException("Interrupted while trying to lock camera closing.", e); } finally { mCameraOpenCloseLock.release(); } }
気になってることについては
とりあえずウェブオペレーションが2周目読んでからじゃないと次行っちゃダメっぽい感じで並行処理は来月かなー。
リリースについてのetc.
なんかあったら追加する。
- Android
- ベータ版リリースはクローズドベータで指定したメアドのGoogleアカウントでのみインストール、アップデート可能に
- ベータ版リリースは2回目以降は
- 更新に~1h程度かかる
- 同じVersionCodeのものは出せない
- その辺はアルファ使う?
- ベータ版から本番へ適用する時のステップはGoogle Play Consoleのアップデートによって変わることがあるが、こないだ試したらベータ版リリース -> 「ROLLOUT TO PRODUCTION」-> 本番へのドラフト追加 -> 本番からリリースだった
アプリで決めたActivityの向きがLANDSCAPEの時、Camera2のCameraDeviceから受け取るカメラのプレビューの向きがずれる
昨日のImageReaderのメモで プレビューの向きがおかしいのが気になる、という話を書きました。
その件について調べると
などどうもアプリの画面の向きがLANDSCAPEの場合、そういうことがあるらしい。
一つには、TextureView
に画像変換用の Matrix
をセットすることで、TextureViewにセットされる画像をいじることができて、例えば下記のように記述した場合、
// https://stackoverflow.com/questions/34536798/android-camera2-preview-is-rotated-90deg-while-in-landscape Matrix rotate = new Matrix(); rotate.postScale(0.5f, 0.5f); rotate.postTranslate(IMAGE_HEIGHT / 2, 0); rotate.postRotate(60); mTextureView.setTransform(rotate);
みたいな感じとなる。
これをもうちょっとうまくやる。
// Landscapeのアプリの場合、カメラの向きとプレビューの向きがずれるっぽい // https://stackoverflow.com/questions/34536798/android-camera2-preview-is-rotated-90deg-while-in-landscape Matrix rotate = new Matrix(); rotate.postRotate(270); // デバイスの向きとアプリの向きの差分から決めると良い // 関連: https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L520-L541 rotate.postTranslate(0, mTextureView.getWidth()); rotate.postScale(1.0f * 1.15f, 0.56f * 1.15f); // とりあえず頑張ってうめた mTextureView.setTransform(rotate);
これでだいたいプレビューの向きとサイズがあってくる。
このMatrixでの操作手順は、前操作した結果にさらに次の操作が反映されるので実際紙などを使って考えた方が早い。
さて、さっきのコードについてScaleが手打ちだとちょっと困るのでもう少しだけ頑張って
private void createCameraPreviewSession() { // Landscapeのアプリの場合、カメラの向きとプレビューの向きがずれるっぽい // https://stackoverflow.com/questions/34536798/android-camera2-preview-is-rotated-90deg-while-in-landscape Matrix rotate = new Matrix(); rotate.postRotate(270); // デバイスの向きとアプリの向きの差分から決めると良い // 関連: https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L520-L541 rotate.postTranslate(0, mTextureView.getWidth()); rotate.postScale((1.0f * IMAGE_WIDTH / IMAGE_HEIGHT) * (mTextureView.getWidth() / mTextureView.getHeight()), (1.0f * IMAGE_HEIGHT / IMAGE_WIDTH) * (mTextureView.getWidth() / mTextureView.getHeight())); mTextureView.setTransform(rotate); SurfaceTexture texture = mTextureView.getSurfaceTexture(); texture.setDefaultBufferSize(IMAGE_WIDTH, IMAGE_HEIGHT); // 自分の手元のデバイスで決めうちしてます
こう。
今日時点での全体のコードは https://gist.github.com/woshidan/5443e4d0d779ffff036862d7010e14ef
気になってることについては
- [新しい気になること]デバイスの向きとアプリの向きの差分
- [既存気になること]CameraDevice解放のタイミングについて
- [既存気になること]CameraCaptureSession closeのタイミングについて
View AnimationとProperty Animationの違いについて
- View Animation
- Animationクラスのサブクラス
- Viewの見た目の特徴のうち一つをいじるアニメーションのクラス
- 具体例:
- TranslateAnimation
- ScaleAnimation
- RotateAnimation
- AlphaAnimation
- 変化するのはrenderの結果だけで本当の値は変化していない(出典: https://developer.android.com/guide/topics/graphics/prop-animation.html#property-vs-view )
- テスト用アプリでView Animationのボタンを複数押してもそれらの結果は同時に適用されない
- それぞれのアニメーション前後でViewの状態が変化していないから、それぞれアニメーション実行前の状態から変化させた結果になる
- ボタンを押す前後で見た目は変わってもViewのプロパティを調べるログの結果は変化しない
- Property Animation
- Viewに生えているメソッド
- Viewのプロパティを変更するアニメーションのクラス
- 具体例:
- View.setTranslationX/Y
- View.setScaleX/Y
- View.setRotation, setPivotX/Y
- View.setAlpha
- Viewのプロパティを変更している
- テスト用アプリでProperty Animationのボタンを複数押すとそれらの結果は同時に適用される
- ボタンを押す前後で見た目は変わってもViewのプロパティを調べるログの結果は変化する
テスト用アプリコードはこちら View Animation vs Property Animation · GitHub
アニメーション: テスト用アプリでView Animationのボタンを複数押してもそれらの結果は同時に適用されない
アニメーション: テスト用アプリでProperty Animationのボタンを複数押すとそれらの結果は同時に適用される
参考
ImageReaderクラスを触ってみた
今日はまとめる余裕がないのでこっちにおくんじゃ。
- ImageReaderはAPI19で追加された、他のSurface(入力元=カメラなど)から画像を読み取る & 読み取ったコールバックで加工して他の部分へ流す(たとえばBitmapにしてImageViewに渡す)という使い方ができるSurface
- ImageはImageReaderがonImageAvailableコールバックが呼び出された時に持ってる、画像のバイト列などが扱えるオブジェクト
- MaxImagesを見ていると、何枚分かデータが貯められるそうだが、よくわからず
- 生のbyte列を扱えるが、Bitmapと違いUIへ渡せない
- 今回は http://woshidan.hatenablog.com/entry/2017/09/06/083000 をもとにして https://developer.android.com/things/training/doorbell/camera-input.html を参考にプレビューを見ながらシャッターを押したらさっき撮った画像が小窓に表示される、みたいなのを書いた
- ImageはImageReaderがonImageAvailableコールバックが呼び出された時に持ってる、画像のバイト列などが扱えるオブジェクト
- 追加でCamera2 APIについて気づいたこと
- mCameraDevice.createCaptureSession にはそのセッションで出力を送りうるすべてのSurfaceを配列で渡す
- このタイプのリクエストの結果はこのデバイスに… というのは、
CaptureRequest.Builder.addTarget
で設定 - すべてのSurfaceで画面サイズが適切に設定されていないとセッションが開始されないっぽい
- 新しく追加した
ImageReader
のSurface
の設定がおかしかった時のエラーが以下Surface with size (w=180, h=180) and format 0x21 is not valid, size not in valid set: [5248x3936, 5248x2952, 3840x2160, 3264x2448, 2048x1536, 1920x1080, 1280x720, 640x480, 480x320, 320x240]
CameraDevice-JV-0: Stream configuration failed
- 新しく追加した
- 追加でもんにゃりしていること
- Preview画面の向きについて(一番下を見ればわかるが、ずれた)
- CameraDevice解放のタイミングについて
- CameraCaptureSession closeのタイミングについて
こちらはコード全体のgist ImageReader初見メモ · GitHub
メモがてらのコメント付きコード断片。
// ImageReaderインスタンスの初期化 // MAX_IMAGES個だけ同時にImageオブジェクトが取得できる mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT, ImageFormat.JPEG, MAX_IMAGES); // imageAvailableListener -> 画像準備できた時反応するくん // backgroundHandler -> Listenerが実行された時に呼び出されるHandler // (に対応したLooperって形で処理を実行するバックグラウンドスレッドの指定) mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { // Surfaceから画像が利用できるようになった時に呼び出される @Override public void onImageAvailable(ImageReader reader) { // Imageは各種コーデック(圧縮方法みたいなもの)で圧縮したりする、画像のByteBufferを扱うためのオブジェクト Image image = reader.acquireLatestImage(); // 何枚か画像を扱うことができて(?)、それぞれはPlanesに入っている // この辺のコードは https://developer.android.com/things/training/doorbell/camera-input.html のサンプルより ByteBuffer imageBuf = image.getPlanes()[0].getBuffer(); final byte[] imageBytes = new byte[imageBuf.remaining()]; imageBuf.get(imageBytes); image.close(); final Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { ImageView imageView = (ImageView) findViewById(R.id.picture); imageView.setImageBitmap(bitmap); } }); } };
private void copyPreview() { // ImageViewへ静止画を送るためのCaptureRequestを作る // 静止画を送ってもらうためのリクエストのビルダーですよ CaptureRequest.Builder copyPreviewRequestBuilder = null; try { copyPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); } catch (CameraAccessException e) { e.printStackTrace(); } // 送り先はImageReaderにしてね copyPreviewRequestBuilder.addTarget(mImageReader.getSurface()); CaptureRequest copyPreviewRequest = copyPreviewRequestBuilder.build(); // (プレビュー時にセッションは開いたままで、)追加で静止画送ってくれリクエストを送る try { mCaptureSession.capture(copyPreviewRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } }
画面は以下のような感じ。
参考
- Add Camera Support | Android Things
- ImageReader | Android Developers
- Image | Android Developers
- https://developer.android.com/about/versions/android-4.4.html
- ImageReaderの概要としてはここの
Surface Image Reader
の項目がわかりやすいです
- ImageReaderの概要としてはここの