我对此进行了一些研究,并整理了一个可行的(ish)示例。这是我发现的。从相机中获取原始数据非常容易。它作为 YUV 字节数组返回。您需要手动将其绘制到表面上才能对其进行修改。为此,您需要有一个可以手动运行绘图调用的 SurfaceView。您可以设置几个标志来完成此操作。
为了手动进行绘图调用,您需要将字节数组转换为某种位图。此时位图和 BitmapDecoder 似乎不能很好地处理 YUV 字节数组。已经为此提交了一个错误,但不要不知道该状态是什么。所以人们一直在尝试自己将字节数组解码成RGB格式。
似乎手动进行解码有点慢,人们已经取得了不同程度的成功。像这样的事情可能真的应该在 NDK 级别使用本机代码来完成。
仍然可以让它工作。另外,我的小演示只是我花了几个小时一起破解东西(我想这样做让我的想象力有点过分了;))。因此,通过一些调整,您可能会大大改善我已经设法开始工作的内容。
这个小代码片段还包含我发现的其他一些宝石。如果您想要的只是能够在表面上绘图,您可以覆盖表面的 onDraw 函数 - 您可能会分析返回的相机图像并绘制覆盖 - 这比尝试处理每一帧要快得多。此外,我更改了 SurfaceHolder.SURFACE_TYPE_NORMAL,如果您希望显示相机预览,则需要这些设置。因此,对代码进行了一些更改 - 注释掉的代码:
//try { mCamera.setPreviewDisplay(holder); } catch (IOException e)
// { Log.e("Camera", "mCamera.setPreviewDisplay(holder);"); }
还有:
SurfaceHolder.SURFACE_TYPE_NORMAL //SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS - for preview to work
应该允许您将基于相机预览的帧叠加在真实预览之上。
无论如何,这是一段工作代码 - 应该可以让您开始。
只需在您的一个视图中添加一行代码,如下所示:
<pathtocustomview.MySurfaceView android:id="@+id/surface_camera"
android:layout_width="fill_parent" android:layout_height="10dip"
android:layout_weight="1">
</pathtocustomview.MySurfaceView>
并将此类包含在您的源代码中的某处:
package pathtocustomview;
import java.io.IOException;
import java.nio.Buffer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class MySurfaceView extends SurfaceView implements Callback,
Camera.PreviewCallback {
private SurfaceHolder mHolder;
private Camera mCamera;
private boolean isPreviewRunning = false;
private byte [] rgbbuffer = new byte[256 * 256];
private int [] rgbints = new int[256 * 256];
protected final Paint rectanglePaint = new Paint();
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
rectanglePaint.setARGB(100, 200, 0, 0);
rectanglePaint.setStyle(Paint.Style.FILL);
rectanglePaint.setStrokeWidth(2);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(new Rect((int) Math.random() * 100,
(int) Math.random() * 100, 200, 200), rectanglePaint);
Log.w(this.getClass().getName(), "On Draw Called");
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
synchronized (this) {
this.setWillNotDraw(false); // This allows us to make our own draw
// calls to this canvas
mCamera = Camera.open();
Camera.Parameters p = mCamera.getParameters();
p.setPreviewSize(240, 160);
mCamera.setParameters(p);
//try { mCamera.setPreviewDisplay(holder); } catch (IOException e)
// { Log.e("Camera", "mCamera.setPreviewDisplay(holder);"); }
mCamera.startPreview();
mCamera.setPreviewCallback(this);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized (this) {
try {
if (mCamera != null) {
mCamera.stopPreview();
isPreviewRunning = false;
mCamera.release();
}
} catch (Exception e) {
Log.e("Camera", e.getMessage());
}
}
}
public void onPreviewFrame(byte[] data, Camera camera) {
Log.d("Camera", "Got a camera frame");
Canvas c = null;
if(mHolder == null){
return;
}
try {
synchronized (mHolder) {
c = mHolder.lockCanvas(null);
// Do your drawing here
// So this data value you're getting back is formatted in YUV format and you can't do much
// with it until you convert it to rgb
int bwCounter=0;
int yuvsCounter=0;
for (int y=0;y<160;y++) {
System.arraycopy(data, yuvsCounter, rgbbuffer, bwCounter, 240);
yuvsCounter=yuvsCounter+240;
bwCounter=bwCounter+256;
}
for(int i = 0; i < rgbints.length; i++){
rgbints[i] = (int)rgbbuffer[i];
}
//decodeYUV(rgbbuffer, data, 100, 100);
c.drawBitmap(rgbints, 0, 256, 0, 0, 256, 256, false, new Paint());
Log.d("SOMETHING", "Got Bitmap");
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mHolder.unlockCanvasAndPost(c);
}
}
}
}