我想从单个相机enter image description here
构建关于相机两个预览的应用程序android我尝试构建应用,我在Android Studio中使用SurfaceView,但我遇到问题,enter image description here
我想要示例代码,你能帮帮我吗? 非常感谢你。
我试过app。
AndroidSurfaceviewExample.java
package th.in.cybernoi.cardboardview;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
public class AndroidSurfaceviewExample extends Activity implements SurfaceHolder.Callback {
TextView testView;
Camera camera;
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
PictureCallback rawCallback;
ShutterCallback shutterCallback;
PictureCallback jpegCallback;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
surfaceHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
outStream.write(data);
outStream.close();
Log.d("Log", "onPictureTaken - wrote bytes: " + data.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Toast.makeText(getApplicationContext(), "Picture Saved", 2000).show();
refreshCamera();
}
};
}
//Surfacrview
public void captureImage(View v) throws IOException {
//take the picture
camera.takePicture(null, null, jpegCallback);
}
public void refreshCamera() {
if (surfaceHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
} catch (Exception e) {
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
refreshCamera();
}
public void surfaceCreated(SurfaceHolder holder) {
try {
// open the camera
camera = Camera.open();
} catch (RuntimeException e) {
// check for exceptions
System.err.println(e);
return;
}
Camera.Parameters param;
param = camera.getParameters();
// modify parameter
param.setPreviewSize(352, 288);
camera.setParameters(param);
try {
// The Surface has been created, now tell the camera where to draw
// the preview.
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
} catch (Exception e) {
// check for exceptions
System.err.println(e);
return;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// stop preview and release camera
camera.stopPreview();
camera.release();
camera = null;
}
}
activity_main.xml中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context="th.in.cybernoi.cardboardview.MainActivity">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
立即更新编辑代码:9/02/16
public class AndroidSurfaceviewExample extends Activity implements SurfaceHolder.Callback {
TextView testView;
Camera camera;
SurfaceHolder camera01;
SurfaceHolder camera02;
SurfaceView surfaceViewLeft;
SurfaceHolder surfaceHolderLeft;
SurfaceView surfaceViewRight;
SurfaceHolder surfaceHolderRight;
PictureCallback rawCallback;
ShutterCallback shutterCallback;
PictureCallback jpegCallback;
/**
* ATTENTION: This was auto-generated to implement the App Indexing API.
* See https://g.co/AppIndexing/AndroidStudio for more information.
*/
private GoogleApiClient client;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceViewLeft = (SurfaceView) findViewById(R.id.surfaceViewLeft);
surfaceHolderLeft = surfaceViewLeft.getHolder();
surfaceViewRight = (SurfaceView) findViewById(R.id.surfaceViewRight);
surfaceHolderRight = surfaceViewRight.getHolder();
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
surfaceHolderLeft.addCallback(this);
surfaceHolderRight.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
surfaceHolderLeft.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolderRight.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
/*
jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
outStream.write(data);
outStream.close();
Log.d("Log", "onPictureTaken - wrote bytes: " + data.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Toast.makeText(getApplicationContext(), "Picture Saved", 2000).show();
refreshCamera();
}
};
*/
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
}
//Surfacrview
public void refreshCamera() {
if (surfaceHolderLeft.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
camera.setPreviewDisplay(surfaceHolderLeft);
camera.startPreview();
} catch (Exception e) {
}
}
public void onPreviewFrame() {
if (surfaceHolderRight.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
surfaceHolderLeft.lockCanvas();
camera.setPreviewDisplay(surfaceHolderRight);
camera.setPreviewCallback((Camera.PreviewCallback) surfaceHolderLeft);
camera.startPreview();
} catch (Exception e) {
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
refreshCamera();
onPreviewFrame();
}
public void surfaceCreated(SurfaceHolder holder) {
try {
// open the camera
camera = Camera.open();
} catch (RuntimeException e) {
// check for exceptions
System.err.println(e);
return;
}
Camera.Parameters param;
param = camera.getParameters();
// modify parameter
param.setPreviewSize(352, 288);
camera.setParameters(param);
try {
// The Surface has been created, now tell the camera where to draw
// the preview.
camera.setPreviewDisplay(surfaceHolderLeft);
camera.startPreview();
} catch (Exception e) {
// check for exceptions
System.err.println(e);
return;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// stop preview and release camera
camera.stopPreview();
camera.release();
camera = null;
}
@Override
public void onStart() {
super.onStart();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
client.connect();
Action viewAction = Action.newAction(
Action.TYPE_VIEW, // TODO: choose an action type.
"AndroidSurfaceviewExample Page", // TODO: Define a title for the content shown.
// TODO: If you have web page content that matches this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
Uri.parse("http://host/path"),
// TODO: Make sure this auto-generated app deep link URI is correct.
Uri.parse("android-app://th.in.cybernoi.cardboardview/http/host/path")
);
AppIndex.AppIndexApi.start(client, viewAction);
}
@Override
public void onStop() {
super.onStop();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
Action viewAction = Action.newAction(
Action.TYPE_VIEW, // TODO: choose an action type.
"AndroidSurfaceviewExample Page", // TODO: Define a title for the content shown.
// TODO: If you have web page content that matches this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
Uri.parse("http://host/path"),
// TODO: Make sure this auto-generated app deep link URI is correct.
Uri.parse("android-app://th.in.cybernoi.cardboardview/http/host/path")
);
AppIndex.AppIndexApi.end(client, viewAction);
client.disconnect();
}
}
答案 0 :(得分:1)
布局中的每个元素都需要具有唯一ID。您已将两个屏幕元素都标识为@+id/surfaceView
。尝试使它们都独一无二。请参阅下面的修改后的布局文件 - 也不要忘记更新源代码以填充两个屏幕元素。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context="th.in.cybernoi.cardboardview.MainActivity">
<SurfaceView
android:id="@+id/surfaceViewLeft"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<SurfaceView
android:id="@+id/surfaceViewRight"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
向程序添加代码以引用两个SurfaceView对象。您只需复制已有的代码即可。您还应该检查程序的其余部分,以获取需要更新的SurfaceView对象的其他引用。
surfaceViewLeft = (SurfaceView) findViewById(R.id.surfaceViewLeft);
surfaceHolderLeft = surfaceViewLeft.getHolder();
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
surfaceHolderLeft.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
surfaceHolderLeft.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceViewRight = (SurfaceView) findViewById(R.id.surfaceViewRight);
surfaceHolderRight = surfaceViewRight.getHolder();
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
surfaceHolderRight.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
surfaceHolderRight.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
关键是要意识到虽然你的左视图和右视图是同一类型的对象(它们都是SurfaceViews
),但它们完全没有意识到彼此。如果您希望某个视图发生某些事情,则必须编写在该视图上显式工作的代码。如果您还希望它在另一个视图中发生,则必须编写在另一个视图中显式工作的代码。这是具有相同对象的两个实例的示例。
更新:进一步挖掘后,我发现上述变化是必要的,但还不够。您对camera.setPreviewDisplay(surfaceHolderLeft);
的调用只允许定义单个预览SurfaceView
,因此您无法使用此方法在两个表面上进行预览。
要完成您要执行的操作,我建议您创建预览图像的位图(可以在名为onPreviewFrame()
的回调中获取,这类似于您的onPictureTaken() callback. The bitmap data will be passed in through the first parameter) Then get a
画布{{1 SurfaceHolder.lockCanvas()for each Surface view (by calling
Canvas.drawBitmap()`。这应该将原始图像绘制到每个表面视图上。
由于您将直接绘制每个surfaceView,因此您应该摆脱将其中一个surfaceView设置为预览的行,以便您与相机竞争以获取在那里绘制。摆脱, and then draw the bitmap you saved onto each Canvas by calling
行而不是写camera.setPreviewDisplay(surfaceHolderLeft);
答案 1 :(得分:0)
我成功地实现了从单个摄像机克隆的预览。请参阅下面的代码段。
private Camera.PreviewCallback cbPreview = new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] bytes, Camera camera) {
int pf = mCamera.getParameters().getPreviewFormat();
if (pf == ImageFormat.NV21)
bmp = decodeNV21(bytes, camera);
else
bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
MainActivity.mImageView.setImageBitmap(bmp);
MainActivity.mImageView.postInvalidate();
}
};
private Bitmap decodeNV21(byte[] data, Camera camera){
Bitmap retimage = null;
int w = camera.getParameters().getPreviewSize().width;
int h = camera.getParameters().getPreviewSize().height;
//Get the YUV image
YuvImage yuv_image = new YuvImage(data, camera.getParameters().getPreviewFormat(), w, h, null);
//Convert Yuv to Jpeg
Rect rect = new Rect(0, 0, w, h);
ByteArrayOutputStream out_stream = new ByteArrayOutputStream();
yuv_image.compressToJpeg(rect, 100, out_stream);
//Convert Yuv to jpeg
retimage = BitmapFactory.decodeByteArray(out_stream.toByteArray(), 0, out_stream.size());
return retimage;
}