Android camera , onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未调用
Posted
技术标签:
【中文标题】Android camera , onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未调用【英文标题】:Android camera , onPictureTaken(byte[] imgData, Camera camera) method & PictureCallback never called 【发布时间】:2013-10-30 13:22:42 【问题描述】:我有一个自定义相机应用程序,它可以在 SurfaceView 上预览相机视频输出并尝试拍照,这些照片应该由“xzing”扫描仪 API 处理,以解码图像中的任何条形码。
我的应用可以正确预览并且不会抛出任何错误或期望,但是我的 onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未被调用,因此我无法获取图像并继续进一步扫描。
以下是负责相机逻辑的活动的实际代码。 onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 都在这个类中(ScanVinFromBarcodeActivity.java)请看:
package com.ty.tyownerspoc.barcode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.ty.tyownerspoc.R;
public class ScanVinFromBarcodeActivity extends Activity
private Camera globalCamera;
private int cameraId = 0;
private TextView VINtext = null;
private View scanButton = null;
// bitmap from camera
private Bitmap bmpOfTheImageFromCamera = null;
// global flag whether a camera has been detected
private boolean isThereACamera = false;
// surfaceView for preview object
private FrameLayout frameLayoutBarcodeScanner = null;
private CameraPreview newCameraPreview = null;
private SurfaceView surfaceViewBarcodeScanner = null;
private int counter = 0;
private volatile boolean finishedPictureTask = false;
/*
* This method , finds FEATURE_CAMERA, opens the camera, set parameters ,
* add CameraPreview to layout, set camera surface holder, start preview
*/
private void initializeGlobalCamera()
try
if (!getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA))
Toast.makeText(this, "No camera on this device",
Toast.LENGTH_LONG).show();
else // check for front camera ,and get the ID
cameraId = findFrontFacingCamera();
if (cameraId < 0)
Toast.makeText(this, "No front facing camera found.",
Toast.LENGTH_LONG).show();
else
Log.d("ClassScanViewBarcodeActivity",
"camera was found , ID: " + cameraId);
// camera was found , set global camera flag to true
isThereACamera = true;
// OPEN
globalCamera = Camera.open(cameraId);
// pass surfaceView to CameraPreview
newCameraPreview = new CameraPreview(this, globalCamera);
// pass CameraPreview to Layout
frameLayoutBarcodeScanner.addView(newCameraPreview);
try
globalCamera
.setPreviewDisplay(surfaceViewBarcodeScanner
.getHolder());
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
// PREVIEW
globalCamera.startPreview();
Log.d("ClassScanViewBarcodeActivity",
"camera opened & previewing");
// end else ,check for front camera
// end try
catch (Exception exc)
// in case of exception release resources & cleanup
if (globalCamera != null)
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
Log.d("ClassScanViewBarcodeActivity initializeGlobalCamera() exception:",
exc.getMessage());
// end catch
// onCreate, instantiates layouts & surfaceView used for video preview
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barcode_vin_scanner);
Log.d("ClassScanViewBarcodeActivity", "onCreate ");
// create surfaceView for previewing of camera image
frameLayoutBarcodeScanner = (FrameLayout) findViewById(R.id.FrameLayoutForPreview);
surfaceViewBarcodeScanner = (SurfaceView) findViewById(R.id.surfaceViewBarcodeScanner);
initializeGlobalCamera();
// create text area & scan button
VINtext = (TextView) findViewById(R.id.mytext);
scanButton = findViewById(R.id.webbutton);
// on click listener, onClick take a picture
scanButton.setOnClickListener(new View.OnClickListener()
public void onClick(View v)
try
// if true take a picture
if (isThereACamera)
Log.d("ClassScanViewBarcodeActivity",
"setOnClickListener() isThereACamera: "+ isThereACamera);
//set picture format to JPEG, everytime makesure JPEg callback is called
Parameters parameters = globalCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
globalCamera.setParameters(parameters);
//take pic , should call Callback
globalCamera.takePicture(null, null, jpegCallback);
// wait 1 sec , than start preview again
Thread.sleep(1000);
//STOP
globalCamera.stopPreview();
//start previewing again onthe SurfaceView in case use wants to take another pic/scan
globalCamera.startPreview();
// end try
catch (Exception exc)
// in case of exception release resources & cleanup
if (globalCamera != null)
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
Log.d("ClassScanViewBarcodeActivity setOnClickListener() exceprtion:",
exc.getMessage());
// end catch
// end on Click
);// end OnClickListener() implementation
// end onCreate
@Override
protected void onResume()
Log.d("ClassScanViewBarcodeActivity, onResume() globalCamera:",
String.valueOf(globalCamera));
if (globalCamera != null)
// START PREVIEW
globalCamera.startPreview();
else
initializeGlobalCamera();
super.onResume();
@Override
protected void onStop()
if (globalCamera != null)
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
super.onStop();
//callback used by takePicture()
PictureCallback jpegCallback = new PictureCallback()
public void onPictureTaken(byte[] imgData, Camera camera)
BinaryBitmap bitmap = null;
try
Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");
//save image to sd card
savePicture(imgData);
// get the bitmap from camera imageData
bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
imgData, 0, imgData.length);
if (bmpOfTheImageFromCamera != null)
//Galaxy S3 , incorrect rotation issue rotate to correct rotation
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);
// convert bitmap to binary bitmap
bitmap = cameraBytesToBinaryBitmap(rotatedBitmap);
if (bitmap != null)
// decode the VIN
String VIN = decodeBitmapToString(bitmap);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
VIN);
VINtext.setText(VIN);
else
Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));
else
Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
String.valueOf(bmpOfTheImageFromCamera));
// end try
catch (Exception exc)
exc.getMessage();
Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
exc.getMessage());
// end onPictureTaken()
;// jpegCallback implementation
/*
* created savePicture(byte [] data) for testing
*/
public void savePicture(byte [] data)
Log.d( "ScanVinFromBarcodeActivity " , "savePicture(byte [] data)");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "Picture_"+counter+"_"+ date + ".jpg";
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String filename =sdDir + File.separator + photoFile;
File pictureFile = new File(filename);
try
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
Toast.makeText(this, "New Image saved:" + photoFile,
Toast.LENGTH_LONG).show();
catch (Exception error)
Log.d( "File not saved: " , error.getMessage());
Toast.makeText(this, "Image could not be saved.",
Toast.LENGTH_LONG).show();
counter++;
private int findFrontFacingCamera()
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++)
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_BACK)
Log.d("ClassScanViewBarcodeActivity , findFrontFacingCamera(): ",
"Camera found");
cameraId = i;
break;
return cameraId;
// end findFrontFacingCamera()
@Override
protected void onPause()
if (globalCamera != null)
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
super.onPause();
// end onPause()
public String decodeBitmapToString(BinaryBitmap bitmap)
Reader reader = null;
Result result = null;
String textResult = null;
try
reader = new MultiFormatReader();
if (bitmap != null)
result = reader.decode(bitmap);
if (result != null)
textResult = result.getText();
else
Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result));
else
Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): bitmap = ",
String.valueOf(bitmap));
/*
* byte[] rawBytes = result.getRawBytes(); BarcodeFormat format =
* result.getBarcodeFormat(); ResultPoint[] points =
* result.getResultPoints();
*/
catch (NotFoundException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (ChecksumException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (FormatException e)
// TODO Auto-generated catch block
e.printStackTrace();
return textResult;
// end decodeBitmapToString (BinaryBitmap bitmap)
public BinaryBitmap cameraBytesToBinaryBitmap(Bitmap bitmap)
BinaryBitmap binaryBitmap = null;
if (bitmap != null)
int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
bitmap.getPixels(pixels, 0, 0, bitmap.getWidth() - 1,
bitmap.getHeight() - 1, bitmap.getWidth(),
bitmap.getHeight());
RGBLuminanceSource source = new RGBLuminanceSource(
bitmap.getWidth(), bitmap.getHeight(), pixels);
HybridBinarizer bh = new HybridBinarizer(source);
binaryBitmap = new BinaryBitmap(bh);
else
Log.d("ClassScanViewBarcodeActivity , cameraBytesToBinaryBitmap (Bitmap bitmap): bitmap = ",
String.valueOf(bitmap));
return binaryBitmap;
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
/*
* The method getScreenOrientation() return screen orientation either
* landscape or portrait. IF width < height , than orientation = portrait,
* ELSE landscape For backwards compatibility we use to methods to detect
* the orientation. The first method is for API versions prior to 13 or
* HONEYCOMB.
*/
public int getScreenOrientation()
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
// if API version less than 13
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = Configuration.ORIENTATION_UNDEFINED;
if (currentapiVersion < android.os.Build.VERSION_CODES.HONEYCOMB)
// Do something for API version less than HONEYCOMB
if (getOrient.getWidth() == getOrient.getHeight())
orientation = Configuration.ORIENTATION_SQUARE;
else
if (getOrient.getWidth() < getOrient.getHeight())
orientation = Configuration.ORIENTATION_PORTRAIT;
else
orientation = Configuration.ORIENTATION_LANDSCAPE;
else
// Do something for API version greater or equal to HONEYCOMB
Point size = new Point();
this.getWindowManager().getDefaultDisplay().getSize(size);
int width = size.x;
int height = size.y;
if (width < height)
orientation = Configuration.ORIENTATION_PORTRAIT;
else
orientation = Configuration.ORIENTATION_LANDSCAPE;
return orientation;
// end getScreenOrientation()
// end class ScanVinFromBarcodeActivity
用于在 SurfaceView 上显示实时摄像头的预览类 (CameraPreview.java):
package com.ty.tyownerspoc.barcode;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.hardware.Camera.CameraInfo;
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback
private SurfaceHolder mHolder;
private Camera mCamera;
private Context context;
public CameraPreview(Context context, Camera camera)
super(context);
mCamera = camera;
this.context = context;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
public void surfaceCreated(SurfaceHolder holder)
// The Surface has been created, now tell the camera where to draw the
// preview.
try
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
catch (IOException e)
public void surfaceDestroyed(SurfaceHolder holder)
// empty. Take care of releasing the Camera preview in your activity.
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)
// preview surface does not exist
return;
// stop preview before making changes
try
mCamera.stopPreview();
catch (Exception e)
// ignore: tried to stop a non-existent preview
Camera.Parameters p = mCamera.getParameters();
// get width & height of the SurfaceView
int SurfaceViewWidth = this.getWidth();
int SurfaceViewHeight = this.getHeight();
List<Size> sizes = p.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, SurfaceViewWidth, SurfaceViewHeight);
// set parameters
p.setPreviewSize(optimalSize.width, optimalSize.height);
/*rotate the image by 90 degrees clockwise , in order to correctly displayed the image , images seem to be -90 degrees (counter clockwise) rotated
* I even tried setting it to p.setRotation(0); , but still no effect.
*/
mCamera.setDisplayOrientation(90);
mCamera.setParameters(p);
// start preview with new settings
try
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
catch (Exception e)
Log.d("CameraPreview , surfaceCreated() , orientation: ",
String.valueOf(e.getMessage()));
// end surfaceChanged()
static Size getOptimalPreviewSize(List <Camera.Size>sizes, int w, int h)
final double ASPECT_TOLERANCE = 0.1;
final double MAX_DOWNSIZE = 1.5;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes)
double ratio = (double) size.width / size.height;
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE)
//if the preview is a lot larger than our display surface ignore it
//reason - on some phones there is not enough heap available to show the larger preview sizes
continue;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff)
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
// Cannot find the one match the aspect ratio, ignore the requirement
//keep the max_downsize requirement
if (optimalSize == null)
minDiff = Double.MAX_VALUE;
for (Size size : sizes)
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff)
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
//everything else failed, just take the closest match
if (optimalSize == null)
minDiff = Double.MAX_VALUE;
for (Size size : sizes)
if (Math.abs(size.height - targetHeight) < minDiff)
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
return optimalSize;
//end Preview class
“ScanVinFromBarcodeActivity”活动的布局 (activity_barcode_vin_scanner.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical"
android:padding="20dip" >
<TextView
android:layout_
android:layout_
android:gravity="center_horizontal"
android:padding="20dip"
android:text="@string/decode_label"
android:textColor="@color/mbackground1" />
<TextView
android:id="@+id/mytext"
android:layout_
android:layout_
android:background="@color/mbackground2"
android:gravity="center_horizontal"
android:padding="20dip"
android:textColor="@color/mytextcolor" />
<TextView
android:layout_
android:layout_
android:gravity="center_horizontal"
android:padding="20dip"
android:text="@string/continue_label"
android:textColor="@color/mytextcolor" />
<Button
android:id="@+id/webbutton"
android:layout_
android:layout_
android:text="@string/web_button"
android:textColor="@color/mytextcolor" />
<FrameLayout
android:id="@+id/FrameLayoutForPreview"
android:layout_
android:layout_
android:layout_weight="1">
<SurfaceView
android:id="@+id/surfaceViewBarcodeScanner"
android:layout_
android:layout_ />
</FrameLayout>
</LinearLayout>
任何帮助将不胜感激。
谢谢
Updated & working onTakePicturen method, the that works , the PictureCallback returns (stopPreview & StartPreview were moved to inside this method in its finally block).
public void onPictureTaken(byte[] imgData, Camera camera)
BinaryBitmap bitmap;
try
Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");
//save image to sd card
savePicture(imgData);
// get the bitmap from camera imageData
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
//down sample
bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
imgData, 0, imgData.length,options);
if (bmpOfTheImageFromCamera != null)
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): bmpOfTheImageFromCamera getByteCount(): ",
String.valueOf(bmpOfTheImageFromCamera.getByteCount()));
//Galaxy S3 , incorrect rotation issue rotate to correct rotation
/*Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);*/
// convert bitmap to binary bitmap
bitmap = cameraBytesToBinaryBitmap(bmpOfTheImageFromCamera);
if (bitmap != null)
// decode the VIN
String VIN = decodeBitmapToString(bitmap);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
VIN);
VINtext.setText(VIN);
else
Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));
else
Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
String.valueOf(bmpOfTheImageFromCamera));
// end try
catch (Exception exc)
exc.getMessage();
Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
exc.getMessage());
finally
globalCamera.stopPreview();
//start previewing again onthe SurfaceView in case use wants to take another pic/scan
globalCamera.startPreview();
// end onPictureTaken()
;// jpegCallback implementation
【问题讨论】:
【参考方案1】:我能找到的唯一可能是罪魁祸首的是,您在 jpegCallback
返回之前再次开始预览。根据javadoc,这是不允许的:
调用此方法后,在 JPEG 回调返回之前,不得调用 startPreview() 或拍摄另一张照片。
回调实际上放在 UI 线程队列中 - 它将在您的 Thread.sleep()
中暂停,并且同一线程将在实际进入回调之前调用 stopPreview()
和 startPreview()
。一般来说,如果你在 UI 线程上调用 Thread.sleep()
- 你做错了。因此,希望如果您删除 sleep()
并将其后的内容放入 jpegCallback
您的问题应该得到解决。
【讨论】:
另外,在 takePicture() 之后不需要stopPreview()
。拍照后系统会停止预览。
@Pescis 你绝对写我把 stopPreview() 和 startPreview() 移到了 takePictiure 方法的末尾,这些方法现在可以工作了,新代码在上面。
请回答这个问题***.com/questions/39061991/…以上是关于Android camera , onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未调用的主要内容,如果未能解决你的问题,请参考以下文章
问题支持Android相机的API和camera2 API问题,怎么解决