从相机拍摄照片无需预览

eyu*_*kul 57 android android-service android-camera

我正在编写一个Android 1.5应用程序,它在启动后启动.这是一个Service,应该拍照没有预览.此应用程序将记录某些区域的光密度.我能够拍照,但照片是黑色的.

经过长时间的研究,我遇到了一个关于它的bug线程.如果您不生成预览,则图像将为黑色,因为Android相机需要预览才能设置曝光和对焦.我创建了一个SurfaceView和听众,但onSurfaceCreated()事件永远不会被解雇.

我想原因是,表面没有在视觉上创造.我还看到了静态调用相机的一些例子,MediaStore.CAPTURE_OR_SOMETHING它们拍摄照片并用两行代码保存在所需的文件夹中,但它也没有拍照.

我是否需要使用IPC并bindService()调用此功能?或者是否有另一种方法来实现这一目标?

小智 50

Android平台上的摄像头在给出有效的预览表面之前无法流式传输视频真的很奇怪.似乎该平台的架构师根本没有考虑第三方视频流应用程序.即使对于增强现实案例,图片也可以呈现为某种视觉替代,而不是实时相机流.

无论如何,您只需将预览曲面的大小调整为1x1像素,并将其放在窗口小部件的角落(视觉元素).请注意 - 调整预览面大小,而不是相机框架尺寸.

当然,这种技巧并不能消除消耗一些系统资源和电池的不需要的数据流(用于预览).

  • 有一个1×1表面的捕获:在某些设备(例如三星)上可能会很慢,当目标大小不分4(或可能是8)时,它们无法运行HW图像转换器 (9认同)
  • 我怀疑谷歌故意制造它,否则我无法想象他们的艺术建筑师会这样做. (2认同)

Phi*_*ens 38

我在Android Camera Docs中找到了答案.

注意:可以先使用MediaRecorder而不先创建相机预览,然后跳过此过程的前几个步骤.但是,由于用户通常更喜欢在开始录制之前看到预览,因此这里不讨论该过程.

您可以在上面的链接中找到分步说明.在说明之后,它将说明我在上面提供的报价.

  • 呃,我仍然在这方面失去声望点.我如何删除此帖子?如果您使用RTFM,它可以工作!我发誓.它适用于视频.它适用于图片.我发布了这个,因为我发现它并且它有效.现在,一年后,我仍然被投票支持.我尝试了"删除"链接,它只是问我是否要"投票删除"这篇文章.请不要再向我投票了.相反,请在这里回复并告诉我如何删除这个东西. (6认同)
  • 这非常有趣,非常感谢.会尝试这个.不要担心下来的选票! (5认同)

Fra*_*ank 36

实际上它是可能的,但您必须使用虚拟SurfaceView伪造预览

SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);
Run Code Online (Sandbox Code Playgroud)

更新9/21/11:显然这对每个Android设备都不起作用.

  • 使用高于4.0的SurfaceTexture和setSurfaceTexture (8认同)
  • 这就是事情.非常感谢! (2认同)
  • 为了使它适用于每个设备,必须在某处添加表面并实际创建,最好是使用持有者的回调.仅当视图可见且大小不为0x0时才会调用回调.setAlpha(0)似乎没问题,但仅适用于API 11及更高版本. (2认同)

Sam*_*Sam 34

拍照

在尝试隐藏预览之前先使其工作.

  • 正确设置预览
    • 使用SurfaceView(Android-4.0之前的兼容性)或SurfaceTexture(Android 4+,可以透明)
    • 在拍照之前设置并初始化它
    • 等待SurfaceViewSurfaceHolder(通过getHolder())报告surfaceCreated()TextureView报告onSurfaceTextureAvailableSurfaceTextureListener 设置和初始化前预览.
  • 确保预览可见:
    • 把它添加到 WindowManager
    • 确保其布局大小至少为1x1像素(您可能希望首先将其设为MATCH_PARENTx MATCH_PARENT进行测试)
    • 确保其可见性View.VISIBLE(如果您未指定它,这似乎是默认值)
    • 确保您使用FLAG_HARDWARE_ACCELERATEDLayoutParams,如果这是一个TextureView.
  • 使用takePicture的是JPEG回调,因为文档说所有设备都不支持其他回调

故障排除

  • 如果surfaceCreated/ onSurfaceTextureAvailable没有被调用时,SurfaceView/ TextureView可能不被显示.
  • 如果takePicture失败,请首先确保预览正常.您可以移除takePicture呼叫并让预览运行以查看它是否显示在屏幕上.
  • 如果图像比应该更暗,则可能需要在呼叫前延迟约一秒钟,takePicture以便在预览开始后相机有时间调整其曝光.

隐藏预览

工作示例(在Sony Xperia M,Android 4.3上测试)

/** 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; }
}
Run Code Online (Sandbox Code Playgroud)


小智 21

在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!
  }
}
Run Code Online (Sandbox Code Playgroud)

您还需要实现一个回调类来处理预览数据:

public class CamCallback implements Camera.PreviewCallback{
  public void onPreviewFrame(byte[] data, Camera camera){
     // Process the camera data here
  }
}
Run Code Online (Sandbox Code Playgroud)

使用上面的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);
Run Code Online (Sandbox Code Playgroud)

  • OP声明他将在服务中使用它.我也在寻找一种如何从服务中拍照的方法.通过放置`FrameLayout`你的意思是把它放在启动服务的Activity中?如果服务从后台调用此怎么办?弹出带有这个`FrameLayout`的Activity吗? (3认同)
  • 我怎么能在服务中使用这段代码?我可以从服务中调用CamPreview吗? (2认同)

Vla*_*lad 20

有一种方法可以做到这一点,但它有点棘手.应该做什么,是从服务附加一个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);
Run Code Online (Sandbox Code Playgroud)

然后设置

surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);
Run Code Online (Sandbox Code Playgroud)

其中mHolder是您从表面视图获得的持有者.

这样,您可以使用surfaceview的alpha,使其完全透明,但相机仍然会获得帧.

这就是我的表现.希望能帮助到你 :)

  • 我添加了此权限,异常消失.<uses-permission android:name ="android.permission.SYSTEM_ALERT_WINDOW"/> (5认同)
  • @BVB窗口管理器不会自动删除您添加的视图..因此保存对surfaceview的引用以及退出时添加wm.removeView(surfaceview) (3认同)
  • 所以当我尝试这个解决方案并退出我的应用程序时会发生这种情况:http://i.imgur.com/g8Fmnj6.png (2认同)

mnl*_*mnl 13

我们通过在3.0以下版本中使用虚拟SurfaceView(未添加到实际GUI)解决了这个问题(或者说4.0作为平板电脑上的摄像头服务并不真正有意义).在版本> = 4.0中,这只在模拟器中工作;(使用SurfaceTexture(和setSurfaceTexture())而不是SurfaceView(和setSurfaceView())在这里工作.至少这适用于Nexus S.

我认为这确实是Android框架的一个缺点.