Skip to content

Instantly share code, notes, and snippets.

@woshidan
Last active September 9, 2017 23:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save woshidan/5443e4d0d779ffff036862d7010e14ef to your computer and use it in GitHub Desktop.
Save woshidan/5443e4d0d779ffff036862d7010e14ef to your computer and use it in GitHub Desktop.
Camera2 Preview Rotation
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MainActivity extends Activity {
private CameraDevice mCameraDevice;
private TextureView mTextureView;
private Handler mBackgroundHandler = new Handler();
private CameraCaptureSession mCaptureSession = null;
private CaptureRequest.Builder mPreviewRequestBuilder;
private CaptureRequest mPreviewRequest;
private ImageReader mImageReader;
// E/CameraDevice-0-LE: 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]
// 09-10 01:32:02.632 6585-6585/com.example.woshidan.cameratest W/CameraDevice-JV-0: Stream configuration failed
private final int IMAGE_WIDTH = 320;
private final int IMAGE_HEIGHT = 240;
private final int MAX_IMAGES = 5;
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);
}
});
}
};
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ImageReaderインスタンスの初期化
// MAX_IMAGES個だけ同時にImageオブジェクトが取得できる
mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT, ImageFormat.JPEG, MAX_IMAGES);
// imageAvailableListener -> 画像準備できた時反応するくん
// backgroundHandler -> Listenerが実行された時に呼び出されるHandler
// (に対応したLooperって形で処理を実行するバックグラウンドスレッドの指定)
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
mTextureView = (TextureView) findViewById(R.id.texture);
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) {
}
});
Button capture = (Button) findViewById(R.id.button_capture);
capture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mTextureView.isAvailable()) {
copyPreview();
}
}
});
}
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();
}
}
private void openCamera() {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
String selectedCameraId = "";
try {
selectedCameraId = manager.getCameraIdList()[0];
// https://github.com/googlesamples/android-Camera2Basic/blob/5dad16c103715b5e7e3c001cc5f6067f8d23f29e/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java#L499
// あたりにあるのですが、顔用カメラを使いたくないなどがあれば、CameraCharacteristicsを経由して確認可能
// CameraCharacteristics characteristics
// = manager.getCameraCharacteristics(selectedCameraId);
} catch (CameraAccessException e) {
e.printStackTrace();
}
try {
manager.openCamera(selectedCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(CameraDevice cameraDevice) {
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(CameraDevice cameraDevice, int error) {
cameraDevice.close();
mCameraDevice = null;
}
};
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); // 自分の手元のデバイスで決めうちしてます
Surface surface = new Surface(texture);
try {
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mPreviewRequest = mPreviewRequestBuilder.build();
} catch (CameraAccessException e) {
e.printStackTrace();
}
try {
mCameraDevice.createCaptureSession(Arrays.asList(surface , mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
// カメラがcloseされている場合
if (null == mCameraDevice) {
return;
}
mCaptureSession = session;
try {
session.setRepeatingRequest(mPreviewRequest, null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment