我正在编写一个Android 1.5应用程序,该应用程序在启动后立即启动。这是Service
,应该在没有预览的情况下拍摄照片。这个程序会记录某些区域的光密度。我可以拍照,但照片是黑色的。
经过长时间的研究,我遇到了一个有关它的错误线程。如果您不生成预览,则图像将为黑色,因为Android相机需要预览才能设置曝光和对焦。我创建了一个SurfaceView
和侦听器,但是onSurfaceCreated()
事件从未触发过。
我想原因是,表面不是在视觉上创建的。我也看到了一些使用MediaStore.CAPTURE_OR_SOMETHING
静态调用相机的示例,该相机拍摄照片并用两行代码保存在所需的文件夹中,但它也不拍摄照片。
我需要使用IPC和bindService()
来调用此功能吗?还是有替代方法可以实现这一目标?
真的很奇怪,Android平台上的相机在提供有效的预览界面之前无法流式传输视频。似乎该平台的架构师根本没有考虑过第三方视频流应用程序。即使在增强现实的情况下,图片也可以呈现为某种视觉替代,而不是实时的相机流。
无论如何,您可以简单地将预览图的大小调整为1x1像素,然后将其放置在小部件(可视元素)的角落。请注意-调整预览表面的大小,而不是相机框架的大小。
当然,这种技巧不能消除不必要的数据流(用于预览),因为它会消耗一些系统资源和电池。
我在Android Camera Docs中找到了答案。
注意:可以在不创建相机的情况下使用
MediaRecorder
首先预览,然后跳过此过程的前几个步骤。然而,因为用户通常更喜欢在开始观看之前先预览记录,此处不讨论该过程。
您可以在上面的链接中找到分步说明。在说明之后,它将声明我在上面提供的报价。
实际上是可行的,但是您必须使用虚拟的SurfaceView伪造预览
SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);
Update 9/21/11:显然,这不适用于所有Android设备。
先尝试使用此功能,然后再尝试隐藏预览。
SurfaceView
(Android 4.0之前的兼容性)或SurfaceTexture
(Android 4+,可以设为透明)SurfaceView
的SurfaceHolder
(通过getHolder()
报告surfaceCreated()
或TextureView
向onSurfaceTextureAvailable
报告其SurfaceTextureListener
。WindowManager
MATCH_PARENT
x MATCH_PARENT
进行测试)View.VISIBLE
(如果未指定,则为默认值)FLAG_HARDWARE_ACCELERATED
,请确保使用LayoutParams
中的TextureView
。takePicture
的JPEG回调,因为文档指出并非所有设备都支持其他回调surfaceCreated
/ onSurfaceTextureAvailable
,则可能未显示SurfaceView
/ TextureView
。takePicture
失败,请首先确保预览正常工作。您可以删除takePicture
通话,让预览运行以查看它是否显示在屏幕上。takePicture
之前延迟一秒钟,以便相机在预览开始后有时间调整其曝光。使预览View
1x1大小以使其可见度最小化[or try 8x16 for possibly more reliability)
new WindowManager.LayoutParams(1, 1, /*...*/)
将预览移出中心以降低其可见度:
new WindowManager.LayoutParams(width, height,
Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
使预览透明(仅适用于TextureView
)
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
width, height, /*...*/
PixelFormat.TRANSPARENT);
params.alpha = 0;
/** Takes a single photo on service start. */
public class PhotoTakingService extends Service {
@Override
public void onCreate() {
super.onCreate();
takePhoto(this);
}
@SuppressWarnings("deprecation")
private static void takePhoto(final Context context) {
final SurfaceView preview = new SurfaceView(context);
SurfaceHolder holder = preview.getHolder();
// deprecated setting, but required on Android versions prior to 3.0
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new Callback() {
@Override
//The preview must happen at or after this point or takePicture fails
public void surfaceCreated(SurfaceHolder holder) {
showMessage("Surface created");
Camera camera = null;
try {
camera = Camera.open();
showMessage("Opened camera");
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
throw new RuntimeException(e);
}
camera.startPreview();
showMessage("Started preview");
camera.takePicture(null, null, new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
showMessage("Took picture");
camera.release();
}
});
} catch (Exception e) {
if (camera != null)
camera.release();
throw new RuntimeException(e);
}
}
@Override public void surfaceDestroyed(SurfaceHolder holder) {}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
});
WindowManager wm = (WindowManager)context
.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
1, 1, //Must be at least 1x1
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
0,
//Don't know if this is a safe default
PixelFormat.UNKNOWN);
//Don't set the preview visibility to GONE or INVISIBLE
wm.addView(preview, params);
}
private static void showMessage(String message) {
Log.i("Camera", message);
}
@Override public IBinder onBind(Intent intent) { return null; }
}
在Android 4.0及更高版本(API级别> = 14)上,可以使用TextureView预览摄像头流并使它不可见,以便不向用户显示。方法如下:
首先创建一个类以实现SurfaceTextureListener,该类将获取预览图面的创建/更新回调。此类也将照相机对象作为输入,因此一旦创建表面,它便可以调用照相机的startPreview函数:
public class CamPreview extends TextureView implements SurfaceTextureListener {
private Camera mCamera;
public CamPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
try{
mCamera.setPreviewTexture(surface);
} catch (IOException t) {}
mCamera.startPreview();
this.setVisibility(INVISIBLE); // Make the surface invisible as soon as it is created
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Put code here to handle texture size change if you want to
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Update your view here!
}
}
您还需要实现一个回调类来处理预览数据:
public class CamCallback implements Camera.PreviewCallback{
public void onPreviewFrame(byte[] data, Camera camera){
// Process the camera data here
}
}
使用上面的CamPreview和CamCallback类在活动的onCreate()或类似的启动函数中设置摄像机:
// Setup the camera and the preview object
Camera mCamera = Camera.open(0);
CamPreview camPreview = new CamPreview(Context,mCamera);
camPreview.setSurfaceTextureListener(camPreview);
// Connect the preview object to a FrameLayout in your UI
// You'll have to create a FrameLayout object in your UI to place this preview in
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraView);
preview.addView(camPreview);
// Attach a callback for preview
CamCallback camCallback = new CamCallback();
mCamera.setPreviewCallback(camCallback);
有一种方法可以做到,但是有点棘手。应该做的是从服务中将一个Surfaceholder附加到窗口管理器中。
WindowManager wm = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE); params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT); wm.addView(surfaceview, params);
然后设置
surfaceview.setZOrderOnTop(true); mHolder.setFormat(PixelFormat.TRANSPARENT);
其中mHolder是您从表面视图获得的支架。
这样,您可以使用Surfaceview的Alpha,使其完全透明,但相机仍会获得帧。
这就是我的做法。希望对您有所帮助:)
我们通过使用虚拟的SurfaceView(未添加到实际的GUI中)解决了这个问题,该版本低于3.0(或者说4.0作为平板电脑上的摄像头服务没有任何意义)。在> = 4.0版本中,这仅在仿真器中有效;(在这里可以使用SurfaceTexture(和setSurfaceTexture())代替SurfaceView(和setSurfaceView())。至少这在Nexus S上有效。
在“ Sam的工作示例”中(谢谢Sam ...)
您可以尝试使用此工作代码,此服务单击正面图片,如果要捕获背面的相机图片,请在代码中取消注释背面的相机并注释正面的相机。