Pergunta

Estou escrevendo um aplicativo Android 1.5 que começa logo após a inicialização. Isto é um Service e deve tirar uma foto sem visualização. Este aplicativo registrará a densidade da luz em algumas áreas. Consegui tirar uma foto, mas a foto estava preta.

Depois de pesquisar por um longo tempo, me deparei com um tópico de insetos sobre isso. Se você não gerar uma visualização, a imagem será preta, já que a câmera Android precisa de visualização para configurar a exposição e o foco. Eu criei um SurfaceView e o ouvinte, mas o onSurfaceCreated() evento nunca é demitido.

Eu acho que o motivo é que a superfície não está sendo criada visualmente. Eu também vi alguns exemplos de ligar para a câmera estaticamente com MediaStore.CAPTURE_OR_SOMETHING O que tira uma foto e salva na pasta desejada com duas linhas de código, mas também não tira uma foto.

Eu preciso usar o IPC e bindService() chamar essa função? Ou existe um método alternativo para conseguir isso?

Foi útil?

Solução

É realmente estranho que a câmera na plataforma Android não possa transmitir vídeo até que ela tenha uma superfície de visualização válida. Parece que os arquitetos da plataforma não estavam pensando nos aplicativos de streaming de vídeo em terceiros. Mesmo para o caso de realidade aumentada, a imagem pode ser apresentada como algum tipo de substituição visual, não um fluxo de câmera em tempo real.

Enfim, você pode simplesmente redimensionar a superfície de visualização para 1x1 pixels e coloque -o em algum lugar no canto do widget (elemento visual). Preste atenção - redimensione a superfície de visualização, não o tamanho da estrutura da câmera.

É claro que esse truque não elimina o fluxo de dados indesejados (para visualização), que consome alguns recursos e bateria do sistema.

Outras dicas

Eu encontrei a resposta para isso no Docs da câmera Android.

Nota: é possível usar MediaRecorder sem criar uma visualização da câmera primeiro e pule as primeiras etapas desse processo. No entanto, como os usuários normalmente preferem ver uma visualização antes de iniciar uma gravação, esse processo não é discutido aqui.

Você pode encontrar as instruções passo a passo no link acima. Após as instruções, ele indicará a citação que eu forneci acima.

Na verdade, é possível, mas você precisa fingir a visualização com uma visão de superfície fictícia

SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);

Atualização 21/09/11: Aparentemente, isso não funciona para todos os dispositivos Android.

Tirando a foto

Faça isso funcionando primeiro antes de tentar ocultar a visualização.

  • Configure corretamente a visualização
    • Use um SurfaceView (Compatibilidade pré-Android-4.0) ou SurfaceTexture (Android 4+, pode ser feito transparente)
    • Defina e inicialize antes de tirar a foto
    • Espere pelo SurfaceView's SurfaceHolder (através da getHolder()) reportar surfaceCreated() ou o TextureView reportar onSurfaceTextureAvailable para o seu SurfaceTextureListener Antes de definir e inicializar a visualização.
  • Verifique se a visualização está visível:
    • Adicione ao WindowManager
    • Verifique se o tamanho do layout é de pelo menos 1x1 pixels (você pode querer começar fazendo MATCH_PARENT x MATCH_PARENT para teste)
    • Garantir que sua visibilidade é View.VISIBLE (o que parece ser o padrão se você não especificar)
    • Certifique -se de usar o FLAG_HARDWARE_ACCELERATED no LayoutParams Se for um TextureView.
  • Usar takePictureO retorno de chamada JPEG do JPEG, já que a documentação diz que os outros retornos de chamada não são suportados em todos os dispositivos

Solução de problemas

  • Se surfaceCreated/onSurfaceTextureAvailable não é chamado, o SurfaceView/TextureView Provavelmente não está sendo exibido.
  • Se takePicture Falha, primeiro verifique se a visualização está funcionando corretamente. Você pode remover o seu takePicture Ligue e deixe a visualização executar para ver se ele é exibido na tela.
  • Se a imagem estiver mais escura do que deveria, pode ser necessário atrasar por cerca de um segundo antes de ligar takePicture para que a câmera tenha tempo para ajustar sua exposição assim que a visualização foi iniciada.

Escondendo a visualização

  • Faça a visualização View 1x1 Tamanho para minimizar sua visibilidade (ou tente 8x16 para possivelmente mais confiabilidade)

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • Mova a pré -visualização do centro para reduzir sua percepção:

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • Fazer a visualização transparente (só funciona para TextureView)

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        width, height, /*...*/
        PixelFormat.TRANSPARENT);
    params.alpha = 0;
    

Exemplo de trabalho (testado no 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; }
}

No Android 4.0 e acima (nível de API> = 14), você pode usar TextureView Para visualizar o fluxo da câmera e torná -lo invisível para não mostrá -lo ao usuário. Aqui está como:

Primeiro, crie uma classe para implementar um SurfaceTextureListener que obterá os retornos de chamada Criar/atualizar para a superfície de visualização. Esta classe também pega um objeto de câmera como entrada, para que possa chamar a função StartPreview da câmera assim que a superfície for criada:

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!
  }
}

Você também precisará implementar uma classe de retorno de chamada para processar os dados de visualização:

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

Use as classes acima do campeonato e CamCallback para configurar a câmera na função OnCreate () da sua atividade () ou de inicialização similar:

// 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);

Existe uma maneira de fazer isso, mas é um pouco complicado. O que deve ser feito é anexar um detentor de superfície ao gerente de janela do serviço

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);

e depois defina

surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);

Onde o Moldler é o titular que você obtém da visão da superfície.

Dessa forma, você pode brincar com o alfa do SurfaceView, torná -lo completamente transparente, mas a câmera ainda receberá quadros.

É assim que eu faço isso. espero que ajude :)

Resolvamos esse problema usando uma visão de superfície fictícia (não adicionada à GUI real) em versões abaixo de 3.0 (ou digamos 4.0 como serviço de câmera em um tablet não faz sentido). Nas versões> = 4.0, isso funcionou apenas no emulador; (o uso da SurfaceTexture (e SetSurfaceTexture ()) em vez de SurfaceView (e SetSurfaceView ()) funcionou aqui. Pelo menos isso funciona no Nexus S.

Eu acho que isso realmente é uma falha na estrutura do Android.

No "Exemplo de trabalho de Sam" (obrigado Sam ...)

se na istruction "wm.addview (visualizar, params);"

Obtenha a exceção "Não é possível adicionar janela android.view.viewroot - permissão negada para este tipo de janela"

Resolva usando esta permissão no AndroidManifest:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Você pode experimentar este código de trabalho, este serviço Clique em Frente Picture, se desejar capturar a imagem da câmera traseira, e o Uncomment Backcamera no código e comente o Frontcamera.

NOTA:- Permita permissão de câmera e armazenamento para aplicar e iniciar o Serviço da atividade ou em qualquer lugar.

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        CapturePhoto();
    }

    private void CapturePhoto() {

        Log.d("kkkk","Preparing to take photo");
        Camera camera = null;

        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

            int frontCamera = 1;
            //int backCamera=0;

            Camera.getCameraInfo(frontCamera, cameraInfo);

            try {
                camera = Camera.open(frontCamera);
            } catch (RuntimeException e) {
                Log.d("kkkk","Camera not available: " + 1);
                camera = null;
                //e.printStackTrace();
            }
            try {
                if (null == camera) {
                    Log.d("kkkk","Could not get camera instance");
                } else {
                    Log.d("kkkk","Got the camera, creating the dummy surface texture");
                     try {
                         camera.setPreviewTexture(new SurfaceTexture(0));
                        camera.startPreview();
                    } catch (Exception e) {
                        Log.d("kkkk","Could not set the surface preview texture");
                        e.printStackTrace();
                    }
                    camera.takePicture(null, null, new Camera.PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            File pictureFileDir=new File("/sdcard/CaptureByService");

                            if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
                                pictureFileDir.mkdirs();
                            }
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
                            String date = dateFormat.format(new Date());
                            String photoFile = "ServiceClickedPic_" + "_" + date + ".jpg";
                            String filename = pictureFileDir.getPath() + File.separator + photoFile;
                            File mainPicture = new File(filename);

                            try {
                                FileOutputStream fos = new FileOutputStream(mainPicture);
                                fos.write(data);
                                fos.close();
                                Log.d("kkkk","image saved");
                            } catch (Exception error) {
                                Log.d("kkkk","Image could not be saved");
                            }
                            camera.release();
                        }
                    });
                }
            } catch (Exception e) {
                camera.release();
            }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top