处理来自onPreviewFrame的帧时出现OutOfMemory错误

时间:2017-06-19 06:35:48

标签: android multithreading memory android-camera

我正在捕获OnPreviewFrame()中的帧,然后在一个线程中处理它们以检查它们是否有效。

public void onPreviewFrame(byte[] data, Camera camera) {
    if (imageFormat == ImageFormat.NV21) {
        //We only accept the NV21(YUV420) format.
        frameCount++;
        if (frameCount > 19 && frameCount % 2 == 0) {
            Camera.Parameters parameters = camera.getParameters();
            FrameModel fModel = new FrameModel(data);
            fModel.setPreviewWidth(parameters.getPreviewSize().width);
           fModel.setPreviewHeight(parameters.getPreviewSize().height);
            fModel.setPicFormat(parameters.getPreviewFormat());
            fModel.setFrameCount(frameCount);
            validateFrame(fModel);
           }
      }       
  }

在validateFrame()中,我将一个ValidatorThread可运行实例提交给具有4个核心和最大线程的ThreadPoolExecutor,以并行处理这些帧。

public class ValidatorThread implements Runnable {

private FrameModel frame;

public ValidatorThread(FrameModel fModel) {
    frame = fModel;
}

@Override
public void run() {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    processNV21Data();
}

private void processNV21Data() {

    YuvImage yuv = new YuvImage(frame.getData(), frame.getPicFormat(),
            frame.getPreviewWidth(), frame.getPreviewHeight(), null);
    frame.releaseData();

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    yuv.compressToJpeg(new Rect(0, 0, frame.getPreviewWidth(), frame.getPreviewHeight()), 100, out);

    byte[] bytes = out.toByteArray();
    yuv = null;

    try {
        if (out != null)
            out.close();
        out = null;
    } catch (IOException e) {
        e.printStackTrace();
    }

    Bitmap baseBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    bytes = null;

    // rotate bitmap 
    baseBitmap = rotateImage(baseBitmap, frame.getRotation());

    //create copy of original bitmap to use later
    Bitmap mCheckedBitmap = baseBitmap.copy(Bitmap.Config.ARGB_8888, true);

    // convert base bitmap to greyscale for validation
    baseBitmap = toGrayscale(baseBitmap);

    boolean isBitmapValid =  Util.isBitmapValid(baseBitmap);

    if (isBitmapValid) {
        baseBitmap.recycle();
        mCheckedBitmap.recycle();
        frame = null;
    } else {
        baseBitmap.recycle();
        mCheckedBitmap.recycle();
        frame = null;
    }
}

public Bitmap toGrayscale(Bitmap bmpOriginal) {
    int width, height;
    height = bmpOriginal.getHeight();
    width = bmpOriginal.getWidth();
    Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    Canvas c = new Canvas(bmpGrayscale);
    Paint paint = new Paint();
    bmpOriginal.recycle();
    return bmpGrayscale;
}
private Bitmap rotateImage(final Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    Bitmap rotatedBitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
    source.recycle();
    return rotatedBitmap;
}

}

FrameModel类有这样的声明:

public class FrameModel {

private byte[] data;
private int previewWidth;
private int previewHeight;
private int picFormat;
private int frameCount;

 public void releaseData() {
    data = null;
}

// getters and setters
}

处理多个帧时出现OutOf Memory错误。

任何人都可以帮助代码所需的内存优化吗?

1 个答案:

答案 0 :(得分:0)

如果从YUV数据生成灰度位图而不通过Jpeg,则可以减少内存使用量。这也会明显加快。

public Bitmap yuv2grayscale(byte[] yuv, int width, int height) {
    int[] pixels = new int[width * height];

    for (int i = 0; i < height*width; i++) {
        int y = yuv[i] & 0xff;
        pixels[i] = 0xFF000000 | y << 16 | y << 16 | y;
    }
    return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.RGB_565);
}

或者,您可以创建RGB_565位图,而无需通过int[width*height]像素数组,并使用NDK操作位图像素。