Camera预览图解析

网上查询预览的问题,多数是由于 byte[]的数据无法解析成Bitmap,然后会有一引2.2系统的YuvImage类来处理,因为它是jni实现的,所以其它低版本无法直接引用。
搞了一天,只是预览的效果是是一堆看不清的像素,不是一张图片,今天换了个工程试了一次居然行了。。。

还没有找到为什么。先把代码放上,备份:

由于两个工程效果不同,所以把整段代码放上来,有需要的可以参考下。
解码是从网络上抄得的一段。
public static final String TAG = "CameraActivity";
Camera mCamera;
SurfaceView mRemoteView;
Surface mLocalSurface;
SurfaceView mLocalView;
SurfaceHolder mLocalSurfaceHolder;

SurfaceHolder mRemoteSurfaceHolder;
ImageView imageView;

boolean remoteVideoVisible=true;
int previewWidth=240,previewHeight=240;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Window win = getWindow();

win.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.camera_test);

mRemoteView = (SurfaceView) findViewById(R.id.remoteView);
mLocalView = (SurfaceView) findViewById(R.id.localView);

mLocalSurfaceHolder = mLocalView.getHolder();
mLocalSurfaceHolder.addCallback(this);
mLocalSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mLocalSurface = mLocalSurfaceHolder.getSurface();

mRemoteSurfaceHolder = mRemoteView.getHolder();
mRemoteSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
//imageView= (ImageView) findViewById(R.id.iv);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated");
mCamera = Camera.open();
mCamera.setPreviewCallback(this);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
}
}

@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
Log.i(TAG, "surfaceChanged");
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(previewWidth, previewHeight);
parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);
parameters.setPreviewFrameRate(10);
mCamera.setParameters(parameters);
mCamera.startPreview();
}

@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.i(TAG, "surfaceDestroyed");
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}

//如果只是实现预览下面这个方法就可以不使用了。但是我现在实现两个图像,一个是预览的图像,另一个也不知道是什么图像。
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i(TAG, "onPreviewFrame.");

if (remoteVideoVisible) {
int w = camera.getParameters().getPreviewSize().width;
int h = camera.getParameters().getPreviewSize().height;
drawRemoteVideo(data,w,h);
}
}

private void drawRemoteVideo(final byte[] imageData, int width, int height) {
//Log.i(TAG, "drawRemoteVideo");
int[] rgb = decodeYUV420SP(imageData, width, height);
Bitmap bmp = Bitmap.createBitmap(rgb, width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = mRemoteSurfaceHolder.lockCanvas();
canvas.drawBitmap(bmp, 0, 0, null);
mRemoteSurfaceHolder.unlockCanvasAndPost(canvas);

这里处理的图像大小会是截取部分的,因为remoteView的大小与取得的图像不同。所以如果要显示全像,要再处理图像的缩放:
bmp=Bitmap.createScaledBitmap(bmp,mRemoteView.getWidth(), mRemoteView.getHeight(),true);然后再用canvas画出来。
}

这个解码方法是网上抄来的。
public int[] decodeYUV420SP(byte[] yuv420sp, int width, int height) {

final int frameSize = width * height;

int rgb[] = new int[width * height];
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}

int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);

if (r < 0) r = 0;
else if (r > 262143) r = 262143;
if (g < 0) g = 0;
else if (g > 262143) g = 262143;
if (b < 0) b = 0;
else if (b > 262143) b = 262143;

rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) &
0xff00) | ((b >> 10) & 0xff);

}
}
return rgb;
}

还需要实现一些接口: implements SurfaceHolder.Callback, Camera.PreviewCallback 这个接口就是onPreviewFrame方法覆盖实现的。
布局文件:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<SurfaceView android:id="@+id/remoteView"
android:layout_height="100dip"
android:layout_width="100dip">
</SurfaceView>

<TextView
android:id="@+id/txt" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:text="divdder"
android:textColor="@color/red"/>

<SurfaceView android:id="@+id/localView"
android:layout_height="200dip"
android:layout_width="200dip">
</SurfaceView>
</LinearLayout>
运行后就可以看到两个图像了。一个大一个小的。
权限还是需要的
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>

关于int previewWidth=240,previewHeight=240;与decodeYUV420SP的问题:
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(previewWidth, previewHeight);
这个预览大小需要确定,似乎可以解释为码率,有一些固定值,如果不在这些固定值范围就会出错。比如176*144 是最小的,320*240,这个值可以查看的,如果实在无法查询,你随便设置一个,然后看log,会提示一些关于这些的值。
如果这个值太小了,解析出来的图像也小,然后在remoteView设置过大,又会出错(之前的程序会,也许不是这个问题),关于这个方法,也有见过不同的说法,我觉得实践后是正确的。
setPictureSize我想就是成像大小,比如拍照后保存的文件分辨率在这个方法设置。
上面的摄像头没有实现传感器监听,所以图像会倒置,这跟屏幕的方向似乎无关的。需要设置横屏的,但因为Android系统版本太多,不同厂商间的摄像头处理方式又不同,所以有较多的麻烦处
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值