私のカメラアプリはカメラのプレビューを画面に表示し、バックグラウンドでも処理します。関連するコードを以下に示します。可能な限り圧縮されています (たとえば、エラー処理やフィールド宣言は示されていません)。
public final class CameraView extends SurfaceView implements
SurfaceHolder.Callback, Runnable, PreviewCallback {
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
void openCamera() {
// Called from parent activity after setting content view to CameraView
mCamera = Camera.open();
mCamera.setPreviewCallbackWithBuffer(this);
}
public void surfaceCreated(SurfaceHolder holder) {
new Thread(this).start();
// Set CameraView to the optimal camera preview size
final Camera.Parameters params = mCamera.getParameters();
final List<Camera.Size> sizes = params.getSupportedPreviewSizes();
final int screenWidth = ((View) getParent()).getWidth();
int minDiff = Integer.MAX_VALUE;
Camera.Size bestSize = null;
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
// Find the camera preview width that best matches the
// width of the surface.
for (Camera.Size size : sizes) {
final int diff = Math.abs(size.width - screenWidth);
if (diff < minDiff) {
minDiff = diff;
bestSize = size;
}
}
} else {
// Find the camera preview HEIGHT that best matches the
// width of the surface, since the camera preview is rotated.
mCamera.setDisplayOrientation(90);
for (Camera.Size size : sizes) {
final int diff = Math.abs(size.height - screenWidth);
if (Math.abs(size.height - screenWidth) < minDiff) {
minDiff = diff;
bestSize = size;
}
}
}
final int previewWidth = bestSize.width;
final int previewHeight = bestSize.height;
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = previewHeight;
layoutParams.width = previewWidth;
setLayoutParams(layoutParams);
params.setPreviewFormat(ImageFormat.NV21);
mCamera.setParameters(params);
int size = previewWidth * previewHeight *
ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8;
mBuffer = new byte[size];
mCamera.addCallbackBuffer(mBuffer);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
}
public void onPreviewFrame(byte[] data, Camera camera) {
CameraView.this.notify();
}
public void run() {
mThreadRun = true;
while (mThreadRun) {
synchronized (this) {
this.wait();
processFrame(mBuffer); // convert to RGB and rotate - not shown
}
// Request a new frame from the camera by putting
// the buffer back into the queue
mCamera.addCallbackBuffer(mBuffer);
}
mHolder.removeCallback(this);
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
public void surfaceDestroyed(SurfaceHolder holder) {
mThreadRun = false;
}
}
すべてのデバイスでカメラのプレビューが正しく表示され、ほとんどのデバイス (エミュレーター、Samsung Galaxy S3 など) で保存されているデータmBuffer
も正しく表示されます (もちろん、NV21 から RGB への変換と回転の後)。ただし、多くのデバイスは onPreviewFrame で正しいデータを提供しません。データは受信後に正しく RGB に変換されているはずなので、問題は に供給された生データにあるようmBuffer
です。YV12 (エイリアス YUV420p) カメラ プレビュー フォーマットに関連するこのバグ レポートに気付きましたが、古いデフォルトの NV21 (エイリアス YUV420sp) を使用しています。これは、互換性標準に従ってサポートする必要があります(7.5.3.2 の下部を参照)。 29ページの)。
たとえば、このシーンの場合 (ここでは、Samsung Galaxy Tab 2 のカメラ プレビューに示されています):
mBuffer
タブ 2 に渡されるデータは次のようになります。
Motorola Droid 4 では次のようになります。
すべてのデバイスで Android カメラのプレビュー データを取得する正しい方法は何ですか?
編集:の場合processFrame()
、OpenCV を使用して RGB に変換し、回転させました。この回答とこの回答を参照してください。