2015-08-05 20 views
9

Sto cercando (di nuovo) per creare la logica anteprima fotocamera che effettivamente funziona correttamente, per tutti gli scenari:Come si determina l'orientamento di default dei fotogrammi di anteprima della videocamera?

  • qualsiasi dispositivo: telefono, tablet, tostapane, qualunque sia
  • qualsiasi fotocamera: anteriore-rivestimento, posteriore-rivestimento, in senso longitudinale, cane-facing, qualunque
  • android.hardware.Camera e android.hardware.camera2
  • orientamento verticale e orizzontale dei dispositivi

Poiché il mio minSdkVersion è 15, e poiché non sono particolarmente preoccupato per le prestazioni, sto cercando di utilizzare un TextureView. E, seguendo il consiglio di Fadden in luoghi come here e here, sto cercando di utilizzare setTransform() su quel TextureView con un appropriato Matrix che:

  • orienta l'anteprima correttamente, prendendo l'orientamento del dispositivo in considerazione
  • riempie la TextureView completamente, al prezzo del ritaglio in cui il rapporto di aspetto TextureView non corrisponde al rapporto di aspetto del fotogramma di anteprima
  • non allunga l'immagine, in modo che un'anteprima di un elemento quadrato (ad esempio, un 3 "Post-It Note ®) mostra il quadrato nel p recensione

Nel mio caso, lo TextureView riempie lo schermo, meno la barra di stato e la barra di navigazione.

A partire dalla adjustAspectRatio() da Grafika's PlayMovieActivity.java, ora ho questo:

private void adjustAspectRatio(int videoWidth, int videoHeight, 
           int rotation) { 
    if (iCanHazPhone) { 
     int temp=videoWidth; 
     videoWidth=videoHeight; 
     videoHeight=temp; 
    } 

    int viewWidth=getWidth(); 
    int viewHeight=getHeight(); 
    double aspectRatio=(double)videoHeight/(double)videoWidth; 
    int newWidth, newHeight; 

    if (getHeight()>(int)(viewWidth*aspectRatio)) { 
     newWidth=(int)(viewHeight/aspectRatio); 
     newHeight=viewHeight; 
    } 
    else { 
     newWidth=viewWidth; 
     newHeight=(int)(viewWidth*aspectRatio); 
    } 

    int xoff=(viewWidth-newWidth)/2; 
    int yoff=(viewHeight-newHeight)/2; 

    Matrix txform=new Matrix(); 

    getTransform(txform); 

    float xscale=(float)newWidth/(float)viewWidth; 
    float yscale=(float)newHeight/(float)viewHeight; 

    txform.setScale(xscale, yscale); 

    switch(rotation) { 
     case Surface.ROTATION_90: 
     txform.postRotate(270, newWidth/2, newHeight/2); 
     break; 

     case Surface.ROTATION_270: 
     txform.postRotate(90, newWidth/2, newHeight/2); 
     break; 
    } 

    txform.postTranslate(xoff, yoff); 

    setTransform(txform); 
    } 

Qui, videoWidth e videoHeight sono le dimensioni della fotocamera anteprima, e il metodo stesso è implementato su una sottoclasse di TextureView. Sto chiamando questo metodo quando ho stabilito quale sia la dimensione dell'anteprima della fotocamera e dopo che lo stesso TextureView è stato ridimensionato.

Questo sembra essere vicino ma non completamente corretto. In particolare, lo modellino — ha capovolto la larghezza e l'altezza del video — è una pugnalata al buio, poiché senza questo, mentre un Tablet Sony Z2 funziona bene, un Nexus 5 risulta orribile (un'anteprima allungata che non riempie lo schermo).

Con iCanHazPhone impostato true, ottengo buoni risultati su un Nexus 5:

Nexus 5, Camera2 Preview, Landscape, Rear-Facing Camera

Nexus 5, Camera2 Preview, Portrait, Rear-Facing Camera

Con iCanHazPhone insieme a false, ho cose del genere:

Nexus 5, Camera2 Preview, Portrait, Rear-Facing Camera, Stretched

Allo stesso modo, con iCanHazPhone set per false, ottengo buoni risultati su un Sony Tablet Z2:

SONY Tablet Z2, Camera2 Preview, Landscape, Rear-Facing Camera

Ma se I flip a true, ottengo:

SONY Tablet Z2, Camera2 Preview, Landscape, Rear-Facing Camera, Stretched

Il mio attuale la teoria è che i diversi dispositivi hanno diversi orientamenti predefiniti della fotocamera e, a seconda dell'orientamento predefinito, è necessario capovolgere la larghezza e l'altezza dell'anteprima nei miei calcoli.

Quindi, le domande:

  1. è la macchina fotografica garantita (tanto quanto qualsiasi coinvolgono hardware Android) per avere un orientamento di default che corrisponde all'orientamento del dispositivo di default? Ad esempio, un Nexus 9 funziona correttamente con iCanHazPhone impostato su true, ad indicare che non si tratta di telefono o tablet ma di impostazione predefinita rispetto a predefinito.

  2. C'è un modo migliore per affrontare questo?

+0

Non molto tempo fa ho ricevuto le seguenti e-mail brevi relative a una delle mie applicazioni della fotocamera; _ Abbiamo rilevato che su dispositivi con orientamento orizzontale predefinito, come Nexus 10, l'immagine di anteprima appare ruotata di 90 gradi e stirata. Che cosa mai significhi - mi è ancora sconosciuto - ma quello che stai spiegando qui mi ha ricordato questa vecchia email. Eppure da solo, come posso gestire la rotazione della telecamera su tali dispositivi che non possiedo :) – harism

+0

@harism: Beh, usando [questo algoritmo] (http://stackoverflow.com/a/31806201/115145) per rilevare i default- i dispositivi portrait vs. default-landscape sembrano funzionare per me, in alternativa al valore hard-coded di 'iCanHazPhone' citato nella mia domanda. Questo presume che le mie supposizioni siano corrette, che è la vera domanda. Ma i tuoi sintomi suonano sicuramente come quello che stavo vedendo nei miei test. – CommonsWare

+0

Fantastico, grazie per avermelo fatto notare. Questo mi aiuta anche a correggere la mia app, spero. – harism

risposta

5

La risposta a entrambe le domande è: utilizzare l'orientamento del sensore fornito dalle API Camera/Camera2 per regolare l'immagine di anteprima.

Per calcolare rotazione relativa fotocamera a schermo (che può essere utilizzato per trasformare l'anteprima) che uso:

static int getRelativeImageOrientation(int displayRotation, int sensorOrientation, 
             boolean isFrontFacing, boolean compensateForMirroring) { 
    int result; 
    if (isFrontFacing) { 
     result = (sensorOrientation + displayRotation) % 360; 
     if (compensateForMirroring) { 
      result = (360 - result) % 360; 
     } 
    } else { 
     result = (sensorOrientation - displayRotation + 360) % 360; 
    } 
    return result; 
} 

dove displayRotation è l'attuale rotazione del display:

static int getDisplayRotation(Context context) { 
    WindowManager windowManager = (WindowManager) context 
      .getSystemService(Context.WINDOW_SERVICE); 
    int rotation = windowManager.getDefaultDisplay().getRotation(); 
    switch (rotation) { 
     case Surface.ROTATION_0: 
      return 0; 
     case Surface.ROTATION_90: 
      return 90; 
     case Surface.ROTATION_180: 
      return 180; 
     case Surface.ROTATION_270: 
      return 270; 
    } 
    return 0; 
} 

sensorOrientation per la macchina legacy:

Camera.CameraInfo.orientation 

e per la Camera2:

CameraCharacteristics#get(CameraCharacteristics.SENSOR_ORIENTATION) 

Si dovrebbe passare falsa per compansateForMirror per il calcolo di anteprima della fotocamera orientamento e passano vero nel calcolo eredità Camera JPG orientamento.

Ho provato questo in un certo numero di dispositivi - sembra funzionare, anche se non posso garantire che questo è a prova di proiettile;]

0

è possibile controllare la mia risposta here per l'orientamento delle immagini. Solo tu hai bisogno di mettere la corretta rotazione della superficie.