下面是我正在尝试的代码。启动器活动正在使用CamPreview类。在实现PreviewCallback之前,我能够获得预览。当我通过实现onPreviewFrame尝试PreviewCallback时,我完全感到困惑它是如何在内部工作的。以下是让我困惑的事情。请澄清一下。
1)虽然我设置了像ImageFormat和Previewsize这样的相机参数,但它们似乎一直持续到方法onPreviewFrame的调用。例如,surfaceChanged方法中的Log.i语句(根据我的理解,在surfaceCreated之后立即调用一次)将预览大小打印为1056x864。但是,onPreviewFrame报告预览大小为1920x1080。
即使图片格式也从NV21(在surfaceChanged中为17)变为JPEG(在onPreviewFrame中为256)。
我已经验证并确认传递给onPreviewFrame的Camera实例与CamPreview类中声明的成员变量mCamera
相同。
如果我能够在onPreviewFrame中成功获得NV21的预览格式,我该如何将其转换为ARGB格式?我已经尝试了stackoverflow中发布的方法,但传递给onPreviewFrame的数据由于索引越界而失败,这导致我首先检查图像格式。如果有人尝试过类似的东西,请告诉我在创作过程中我错过了什么导致了这个混乱:(。
我试图通过最初从传递给onPreviewFrame的byte []创建YuvImage来创建位图,这给了我绿色的latern图像(全部为绿色或垃圾)!
2)你可以看到我在第(1)点中提到的其他Log.i stmts。它们在方法surfaceChanged和onPreviewFrame中打印出每个像素的位数和每个像素的字节数信息。结果分别为12和1。这怎么可能呢 ?同样,这可能是(1)
中发生的事情的副作用public class CamPreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {
private static final String TAG = "CamPreview";
private SurfaceHolder mHolder;
private Camera mCamera;
private byte[] mVideoSource;
private Bitmap mBackBuffer;
private Paint mPaint;
private Context mContext;
public CamPreview(Context context) {
super(context);
mContext = context;
mCamera = getCameraInstance();
mHolder = getHolder();
mHolder.addCallback(this);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(null);
mCamera.setPreviewCallbackWithBuffer(this);
this.setWillNotDraw(false);
Log.i(TAG, "@SurfaceCreated: initilization finished");
} catch (IOException eIOException) {
Log.i(TAG, "Error setting camera preview: " + eIOException.getMessage());
throw new IllegalStateException();
}
}
private Size findBestResolution(int pWidth, int pHeight) {
List<Size> lSizes = mCamera.getParameters().getSupportedPreviewSizes();
Size lSelectedSize = mCamera.new Size(0, 0);
for (Size lSize : lSizes) {
if ((lSize.width <= pWidth)
&& (lSize.height <= pHeight)
&& (lSize.width >= lSelectedSize.width)
&& (lSize.height >= lSelectedSize.height)) {
lSelectedSize = lSize;
}
}
if ((lSelectedSize.width == 0)
|| (lSelectedSize.height == 0)) {
lSelectedSize = lSizes.get(0);
}
return lSelectedSize;
}
private void createBuffers(String caller, Size prefSize) {
Camera.Parameters camParams = mCamera.getParameters();
int previewWidth = prefSize.width;
int previewHeight = prefSize.height;
mBackBuffer = Bitmap.createBitmap(previewWidth,
previewHeight,
Bitmap.Config.ARGB_8888);
Log.i(TAG,"@"+caller+": Piture Width " + Integer.toString(previewWidth));
Log.i(TAG,"@"+caller+": Piture Height " + Integer.toString(previewHeight));
Log.i(TAG,"@"+caller+": Piture format " + Integer.toString(ImageFormat.NV21));
camParams.setPreviewSize(previewWidth,previewHeight);
camParams.setPreviewFormat(ImageFormat.NV21);
mCamera.setParameters(camParams);
PixelFormat pxlFrmt = new PixelFormat();
PixelFormat.getPixelFormatInfo(camParams.getPreviewFormat(), pxlFrmt);
Log.i(TAG,"@"+caller+": Bits per pixel " + Integer.toString(pxlFrmt.bitsPerPixel));
Log.i(TAG,"@"+caller+": Bytes per pixel " + Integer.toString(pxlFrmt.bytesPerPixel));
int sz = previewWidth * previewHeight * pxlFrmt.bitsPerPixel/8;
mVideoSource = new byte[sz];
mCamera.addCallbackBuffer(mVideoSource);
Log.i(TAG, "@"+caller+": backbuffer initilization finished");
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
Log.i(TAG, "@SurfaceCreated: preview started");
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
Log.i(TAG,"No proper holder");
return;
}
try {
mCamera.stopPreview();
} catch (Exception e){
Log.i(TAG,"tried to stop a non-existent preview");
return;
}
createBuffers("surfaceChanged",findBestResolution(w, h));
}
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i(TAG,"@onPreviewFrame: Invoked");
Camera.Parameters params = camera.getParameters();
Camera.Size camSize = params.getPictureSize();
int w = camSize.width;
int h = camSize.height;
Log.i(TAG,"@onPreviewFrame: Piture Width " + Integer.toString(w));
Log.i(TAG,"@onPreviewFrame: Piture Height " + Integer.toString(h));
Log.i(TAG,"@onPreviewFrame: Piture format " + Integer.toString(params.getPictureFormat()));
PixelFormat pxlFrmt = new PixelFormat();
PixelFormat.getPixelFormatInfo(params.getPreviewFormat(), pxlFrmt);
Log.i(TAG,"@onPreviewFrame: Bits per pixel " + Integer.toString(pxlFrmt.bitsPerPixel));
Log.i(TAG,"@onPreviewFrame: Bytes per pixel " + Integer.toString(pxlFrmt.bytesPerPixel));
mBackBuffer = BitmapFactory.decodeByteArray(data, 0, data.length);
Log.i(TAG,"@onPreviewFrame: Back buffer set.");
invalidate();
}
@Override
protected void onDraw(Canvas pCanvas) {
super.onDraw(pCanvas);
Log.i(TAG,"@onDraw: Invoked");
if (mCamera != null) {
Log.i(TAG,"@onDraw: Bbefore draw call to canvas");
pCanvas.drawBitmap(mBackBuffer, 0, 0, mPaint);
mCamera.addCallbackBuffer(mVideoSource);
Log.i(TAG,"@onDraw: Draw finished");
}
}
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
/** A safe way to get an instance of the Camera object. */
private Camera getCameraInstance(){
Camera c = null;
if(checkCameraHardware(mContext)) {
try {
Log.i(TAG, "Trying to open the camera");
c = Camera.open(0);
Log.i(TAG, "Camera opened successfully.");
}
catch (Exception e){
Log.i(TAG, e.getMessage());
}
}
return c;
}
private void releaseCamera(){
if (mCamera != null){
mCamera.release(); // release the camera for other applications
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
releaseCamera();
mVideoSource = null;
mBackBuffer = null;
}
}
}
答案 0 :(得分:0)
好的,在通过android文档仔细重复阅读之后想出了几件事。显然预览[尺寸|格式]与图片[尺寸/格式]完全不同,我认为它们之前是相同的。因此,这修复了由于不正确的数据格式导致的渲染问题和崩溃。它还自动澄清了我对相机参数变化的困惑。
整个例子现在正在运作。但是,我看到两层预览,一个是由相机直接渲染的,另一个是我渲染的onDraw。我不确定我是否应该看到他们两个。以下是固定代码。
如果有人可能花时间在此,请感谢您。现在,我将努力将整个onPreviewFrame逻辑移动到本机代码以加快速度! :)
public class CamPreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {
private static final String TAG = "CamPreview";
private static final int mPreviewWidth = 1280;
private static final int mPreviewHeight = 720;
private static final int mFPSXOff = 72;
private static final int mFPSYOff = 72;
private static final int mFPSSize = 64;
private float mTotalTime;
private float mFrameCount;
private String mFPS;
private long mStart;
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private byte[] mVideoSource;
private Bitmap mBackBuffer;
private Paint mPaint;
public CamPreview(Context context) {
super(context);
mContext = context;
mHolder = getHolder();
mCamera = getCameraInstance();
mHolder.addCallback(this);
mFrameCount = 0;
mTotalTime = 0;
mFPS = "0 FPS";
mStart = 0;
mPaint = new Paint();
mPaint.setColor(0xFFFF0000);
mPaint.setTextSize(mFPSSize);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(null);
mCamera.setPreviewCallbackWithBuffer(this);
this.setWillNotDraw(false);
} catch (IOException eIOException) {
Log.i(TAG, "Error setting camera preview: " + eIOException.getMessage());
throw new IllegalStateException();
}
}
private Size findBestResolution(int pWidth, int pHeight) {
List<Size> lSizes = mCamera.getParameters().getSupportedPreviewSizes();
Size lSelectedSize = mCamera.new Size(0, 0);
for (Size lSize : lSizes) {
if ((lSize.width <= pWidth)
&& (lSize.height <= pHeight)
&& (lSize.width >= lSelectedSize.width)
&& (lSize.height >= lSelectedSize.height)) {
lSelectedSize = lSize;
}
}
if ((lSelectedSize.width == 0)
|| (lSelectedSize.height == 0)) {
lSelectedSize = lSizes.get(0);
}
return lSelectedSize;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null) {
Log.i(TAG,"No proper holder");
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
Log.i(TAG,"tried to stop a non-existent preview");
return;
}
PixelFormat pxlFrmt = new PixelFormat();
Camera.Parameters camParams = mCamera.getParameters();
Size previewSize = findBestResolution(w, h);
int previewWidth = previewSize.width;
int previewHeight = previewSize.height;
camParams.setPreviewSize(previewWidth,previewHeight);
camParams.setPreviewFormat(ImageFormat.NV21);
mCamera.setParameters(camParams);
mCamera.setDisplayOrientation(90);
mBackBuffer = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
PixelFormat.getPixelFormatInfo(camParams.getPreviewFormat(), pxlFrmt);
int sz = previewWidth * previewHeight * pxlFrmt.bitsPerPixel/8;
mVideoSource = new byte[sz];
mCamera.addCallbackBuffer(mVideoSource);
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
Log.i(TAG, "@SurfaceChanged: preview started");
} catch (Exception e){
Log.d(TAG, "@SurfaceChanged:Error starting camera preview: " + e.getMessage());
}
mFrameCount = 0;
mTotalTime = 0;
mStart = SystemClock.elapsedRealtime();
}
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i(TAG,"@onPreviewFrame: Invoked");
Camera.Parameters params = camera.getParameters();
Camera.Size camSize = params.getPreviewSize();
int w = camSize.width;
int h = camSize.height;
PixelFormat pxlFrmt = new PixelFormat();
PixelFormat.getPixelFormatInfo(params.getPreviewFormat(), pxlFrmt);
try {
YuvImage yuv = new YuvImage(data,ImageFormat.NV21,w,h,null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0,0,w,h), 100, baos);
byte[] jpgData = baos.toByteArray();
mBackBuffer = BitmapFactory.decodeByteArray(jpgData, 0, jpgData.length);
} catch (Exception e) {
;
}
Log.i(TAG,"@onPreviewFrame: Backbuffer set.");
postInvalidate();
mFrameCount++;
long end = SystemClock.elapsedRealtime();
mTotalTime += (end-mStart);
mStart = end;
mFPS = Float.toString((1000*mFrameCount/mTotalTime))+" fps";
}
@Override
protected void onDraw(Canvas pCanvas) {
Log.i(TAG,"@onDraw: Invoked");
if (mCamera != null) {
if(mBackBuffer==null) {
Log.i(TAG, "Back buffer is null :((((((( ");
} else {
pCanvas.drawBitmap(mBackBuffer, 0, 0, null);
pCanvas.drawText(mFPS, mFPSXOff, mFPSYOff, mPaint);
mCamera.addCallbackBuffer(mVideoSource);
}
}
}
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
return true;
} else {
return false;
}
}
private Camera getCameraInstance(){
Camera c = null;
if(checkCameraHardware(mContext)) {
try {
c = Camera.open(0);
Log.i(TAG, "Camera opened successfully");
Camera.Parameters params = c.getParameters();
params.setPreviewFormat(ImageFormat.NV21);
params.setPreviewSize(mPreviewWidth, mPreviewHeight);
c.setParameters(params);
Log.i(TAG, "NV21 format set to camera with resolution 1280x720");
}
catch (Exception e){
Log.i(TAG, e.getMessage());
}
}
return c;
}
private void releaseCamera(){
if (mCamera != null){
mCamera.release();
Log.i(TAG,"@releaseCamera:");
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
releaseCamera();
mVideoSource = null;
mBackBuffer = null;
}
}
}