2017-04-06 4 views
0

J'essaye de traiter des cadres avec l'utilisation de ImageReader tout en affichant également l'aperçu de la caméra vidéo sur un SurfaceView. Si la seule surface cible que j'ajoute est SurfaceView, l'aperçu est fluide, mais si j'ajoute un ImageReader comme deuxième cible, l'aperçu commence à être très lent. Pourquoi cela pourrait-il être? J'ai essayé de créer un HandlerThread et un Handler et de les utiliser, mais cela n'a rien changé. La seule chose que je pense actuellement est d'acquérir et de fermer l'image suivante.Camera2API ajoutant ImageReader en tant que surface cible laggs mon aperçu de la caméra

public void startBackgroundThread() { 
    handlerThread = new HandlerThread("Image Processing Thread"); 
    handlerThread.start(); 
    handler = new Handler(handlerThread.getLooper()); 
} 

Configuration de l'appareil photo ici:

public void configureCamera() { 
    try { 
     CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); 
     StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 

     Size[] sizes = configs.getOutputSizes(ImageFormat.YUV_420_888); 
     imageReader = ImageReader.newInstance(sizes[0].getWidth(), sizes[0].getHeight(), ImageFormat.YUV_420_888, 1); 

     Range<Integer>[] ranges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); 
     fpsRange = ranges[ranges.length - 1]; 

     imageReader.setOnImageAvailableListener(this, handler); 

     cameraSurfaceView.getHolder().setFixedSize(sizes[0].getWidth(), sizes[0].getHeight()); 
    } catch (CameraAccessException | NullPointerException e) { 
     e.printStackTrace(); 
    } 
} 

À partir de l'aperçu de la caméra:

private void startCamera() { 
    try { 
     cameraManager.openCamera("0", new CameraDevice.StateCallback() { 
      @Override 
      public void onOpened(@NonNull CameraDevice camera) { 
       cameraDevice = camera; 

       try { 
        cameraDevice.createCaptureSession(Arrays.asList(cameraSurfaceView.getHolder().getSurface(), imageReader.getSurface()), 
          new CameraCaptureSession.StateCallback() { 
           @Override 
           public void onConfigured(@NonNull CameraCaptureSession session) { 
            captureSession = session; 
            try { 
             requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 

             requestBuilder.addTarget(cameraSurfaceView.getHolder().getSurface()); 
             requestBuilder.addTarget(imageReader.getSurface()); 

             requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); 
             requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange); 

             captureRequest = requestBuilder.build(); 
             cameraReady = true; 

             captureSession.setRepeatingRequest(captureRequest, null, handler); 

             onStartButtonClick(startButton); 
            } catch (CameraAccessException e) { 
             e.printStackTrace(); 
            } 
           } 

           @Override 
           public void onConfigureFailed(@NonNull CameraCaptureSession session) { 

           } 
          }, null); 
       } catch (CameraAccessException e) { 
        e.printStackTrace(); 
       } 
      } 

Et vient ici OnImageAvailableListener du ImageReader (ce qui est mon activité principale actuellement):

@Override 
public void onImageAvailable(ImageReader reader) { 
    reader.acquireNextImage().close(); 
} 

Ces extraits de code résident tous dans mon activité principale actuellement. Le téléphone que j'utilise est un Motorola Moto X Play à des fins de test. Le cameraSurfaceView est juste un simple SurfaceView sans aucune personnalisation.

+0

J'ai réussi pour obtenir un nombre de fps plus élevé en diminuant la résolution de l'aperçu mais ce n'est pas une solution idéale – masm64

Répondre

0

Il est un peu difficile de voir à partir des indentations, mais il semble que vous exécutiez le gestionnaire de caméra sur le thread en cours à partir de l'extrait de code.

Le dernier paramètre de cameraManager.openCamera indique le thread à utiliser. S'il est nul, il utilise uniquement le thread en cours. De la documentation applications:

Paramètres cameraId Corde: L'identificateur unique du dispositif de caméra pour ouvrir

rappel CameraDevice.StateCallback: La fonction de rappel qui est appelée une fois que l'appareil est ouvert

gestionnaire Handler : Le gestionnaire sur lequel le rappel doit être appelé, ou null pour utiliser le looper du thread en cours.

Si vous regardez dans l'exemple sur GitHub pour Camera2Basic() vous pouvez voir qu'ils spécifient un gestionnaire distinct:

manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); 

C'est démarré sur un thread séparé:

/** 
    * Starts a background thread and its {@link Handler}. 
    */ 
    private void startBackgroundThread() { 
     mBackgroundThread = new HandlerThread("CameraBackground"); 
     mBackgroundThread.start(); 
     mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 
    } 
+0

Merci pour le conseil mais j'ai déjà essayé cela et cela ne fait malheureusement aucune différence. – masm64

+0

Dans votre extrait de code ci-dessus, vous créez un gestionnaire, mais il ne semble pas être utilisé dans l'appel openCamera - voir le null à la fin. Cela entraînera l'exécution du rappel sur votre thread principal. – Mick

+0

Je sais mais dans le code que je suis en cours d'exécution la valeur donnée n'est pas null mais l'objet gestionnaire lui-même – masm64