Android Camera2 ориентация для эмулятора камеры неправильно - PullRequest
0 голосов
/ 06 июля 2018

Я сейчас следую учебному пособию и пытаюсь настроить Android Camera 2. Это код для него.

MainActivity:

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Camera;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {


private static final int REQUEST_CAMERA_PERMISSION_RESULT = 0;
private Button btnCapture;
private TextureView mTextureView;
private TextureView.SurfaceTextureListener mSurfaceTextListener = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
        setUpCamera(i,i1);
        connectCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

    }
};
private CameraDevice mCameraDevice;
private  HandlerThread mBackgroundHandlerThread;
private Handler mBackgroundHandler;
private static SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0,0);
    ORIENTATIONS.append(Surface.ROTATION_90,90);
    ORIENTATIONS.append(Surface.ROTATION_180,180);
    ORIENTATIONS.append(Surface.ROTATION_270,270);
}
private Size mPreviewSize;
private CaptureRequest.Builder mCaptureRequestBuilder;

private CameraDevice.StateCallback mCameraDeviceStateCallBack = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(@NonNull CameraDevice cameraDevice) {
        mCameraDevice = cameraDevice;
        startPreview();
    }

    @Override
    public void onDisconnected(@NonNull CameraDevice cameraDevice) {
        cameraDevice.close();
        mCameraDevice = null;
    }

    @Override
    public void onError(@NonNull CameraDevice cameraDevice, int i) {
        cameraDevice.close();
        mCameraDevice = null;
    }
};
private String mCameraId;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mTextureView = findViewById(R.id.textureView);
}




@Override
protected void onResume() {
    super.onResume();

    startBackgroundThread();

    if (mTextureView.isAvailable()) {
        setUpCamera(mTextureView.getWidth(),mTextureView.getHeight());
        connectCamera();
    } else {
        mTextureView.setSurfaceTextureListener(mSurfaceTextListener);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA_PERMISSION_RESULT) {
        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(getApplicationContext(),
                    "Application will not run without camera services ",
                    Toast.LENGTH_SHORT).show();
        }
    }
}

private void closeCamera() {
    if (mCameraDevice != null) {
        mCameraDevice.close();
        mCameraDevice = null;
    }
}


private void startBackgroundThread() {
    mBackgroundHandlerThread = new HandlerThread("camera2VideoImage");
    mBackgroundHandlerThread.start();

    mBackgroundHandler = new Handler(mBackgroundHandlerThread.getLooper());
}

private void stopBackgroundThread() {
    mBackgroundHandlerThread.quitSafely();
    try {
        mBackgroundHandlerThread.join();
        mBackgroundHandlerThread = null;
        mBackgroundHandler = null;
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private static int sensorToDeviceRotation(CameraCharacteristics cameraCharacteristics, int deviceOrientation) {
    int sensorOrientation = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    deviceOrientation = ORIENTATIONS.get(deviceOrientation);
    return ((sensorOrientation + deviceOrientation + 360) % 360);

}


private void setUpCamera(int width, int height) {
    CameraManager cameraManager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);
    try {
        for (String cameraId : cameraManager.getCameraIdList()) {
            CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
            if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
                    CameraCharacteristics.LENS_FACING_FRONT) {
                continue;
            }
            StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

            int deviceOrientation = getWindowManager().getDefaultDisplay().getRotation();
            int totalRotation = sensorToDeviceRotation(cameraCharacteristics,deviceOrientation);
            boolean swapRotation = totalRotation == 90 || totalRotation == 270;
            int rotatedWidth = width;
            int rotatedHeight = height;
            if (swapRotation) {
                rotatedWidth = height;
                rotatedHeight = width;
            }
            mPreviewSize = chooseOptimaSize(map.getOutputSizes(SurfaceTexture.class), rotatedWidth ,
                    rotatedHeight);
            mCameraId = cameraId;
            return;
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

private void connectCamera() {
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
                    PackageManager.PERMISSION_GRANTED) {
                cameraManager.openCamera(mCameraId, mCameraDeviceStateCallBack, mBackgroundHandler);
            } else {
                if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                    Toast.makeText(this,
                            "Video app requires access to camera"
                            ,Toast.LENGTH_SHORT).show();
                }
                requestPermissions(new String[] {Manifest.permission.CAMERA},
                        REQUEST_CAMERA_PERMISSION_RESULT);
            }
        } else {
            cameraManager.openCamera(mCameraId, mCameraDeviceStateCallBack, mBackgroundHandler);
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

private void startPreview() {
    SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
    surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());
    Surface previewSurface = new Surface(surfaceTexture);
    try {
        mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        mCaptureRequestBuilder.addTarget(previewSurface);

        mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                try {
                    cameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(),
                            null, mBackgroundHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(getApplicationContext(),"unable to setup preview",
                        Toast.LENGTH_SHORT).show();
            }
        } , null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}



private static class CompareSizeByArea implements Comparator<Size> {

    @Override
    public int compare(Size lhs, Size rhs) {
        return Long.signum((long)lhs.getWidth() * lhs.getHeight() /
                (long) rhs.getWidth() * rhs.getHeight());
    }
}

private static Size chooseOptimaSize(Size[] choices, int width, int height) {
    List<Size> bigEnough = new ArrayList<Size>();
    for (Size option : choices) {
        if (option.getHeight() == option.getWidth() * height / width &&
                option.getWidth() >= width && option.getHeight() >= height) {
            bigEnough.add(option);
        }
    }
    if (bigEnough.size() > 0) {
        return Collections.min(bigEnough, new CompareSizeByArea());
    } else {
        return choices[0];
    }

}


@Override
protected void onPause() {
    closeCamera();
    super.onPause();
    stopBackgroundThread();
    }
}

открытый класс MainActivity расширяет AppCompatActivity {

private static final int REQUEST_CAMERA_PERMISSION_RESULT = 0;
private Button btnCapture;
private TextureView mTextureView;
private TextureView.SurfaceTextureListener mSurfaceTextListener = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
        setUpCamera(i,i1);
        connectCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

    }
};
private CameraDevice mCameraDevice;
private  HandlerThread mBackgroundHandlerThread;
private Handler mBackgroundHandler;
private static SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0,0);
    ORIENTATIONS.append(Surface.ROTATION_90,90);
    ORIENTATIONS.append(Surface.ROTATION_180,180);
    ORIENTATIONS.append(Surface.ROTATION_270,270);
}
private Size mPreviewSize;
private CaptureRequest.Builder mCaptureRequestBuilder;

private CameraDevice.StateCallback mCameraDeviceStateCallBack = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(@NonNull CameraDevice cameraDevice) {
        mCameraDevice = cameraDevice;
        startPreview();
    }

    @Override
    public void onDisconnected(@NonNull CameraDevice cameraDevice) {
        cameraDevice.close();
        mCameraDevice = null;
    }

    @Override
    public void onError(@NonNull CameraDevice cameraDevice, int i) {
        cameraDevice.close();
        mCameraDevice = null;
    }
};
private String mCameraId;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mTextureView = findViewById(R.id.textureView);
}




@Override
protected void onResume() {
    super.onResume();

    startBackgroundThread();

    if (mTextureView.isAvailable()) {
        setUpCamera(mTextureView.getWidth(),mTextureView.getHeight());
        connectCamera();
    } else {
        mTextureView.setSurfaceTextureListener(mSurfaceTextListener);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA_PERMISSION_RESULT) {
        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(getApplicationContext(),
                    "Application will not run without camera services ",
                    Toast.LENGTH_SHORT).show();
        }
    }
}

private void closeCamera() {
    if (mCameraDevice != null) {
        mCameraDevice.close();
        mCameraDevice = null;
    }
}


private void startBackgroundThread() {
    mBackgroundHandlerThread = new HandlerThread("camera2VideoImage");
    mBackgroundHandlerThread.start();

    mBackgroundHandler = new Handler(mBackgroundHandlerThread.getLooper());
}

private void stopBackgroundThread() {
    mBackgroundHandlerThread.quitSafely();
    try {
        mBackgroundHandlerThread.join();
        mBackgroundHandlerThread = null;
        mBackgroundHandler = null;
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private static int sensorToDeviceRotation(CameraCharacteristics cameraCharacteristics, int deviceOrientation) {
    int sensorOrientation = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    deviceOrientation = ORIENTATIONS.get(deviceOrientation);
    return ((sensorOrientation + deviceOrientation + 360) % 360);

}


private void setUpCamera(int width, int height) {
    CameraManager cameraManager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);
    try {
        for (String cameraId : cameraManager.getCameraIdList()) {
            CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
            if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
                    CameraCharacteristics.LENS_FACING_FRONT) {
                continue;
            }
            StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

            int deviceOrientation = getWindowManager().getDefaultDisplay().getRotation();
            int totalRotation = sensorToDeviceRotation(cameraCharacteristics,deviceOrientation);
            boolean swapRotation = totalRotation == 90 || totalRotation == 270;
            int rotatedWidth = width;
            int rotatedHeight = height;
            if (swapRotation) {
                rotatedWidth = height;
                rotatedHeight = width;
            }
            mPreviewSize = chooseOptimaSize(map.getOutputSizes(SurfaceTexture.class), rotatedWidth ,
                    rotatedHeight);
            mCameraId = cameraId;
            return;
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

private void connectCamera() {
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
                    PackageManager.PERMISSION_GRANTED) {
                cameraManager.openCamera(mCameraId, mCameraDeviceStateCallBack, mBackgroundHandler);
            } else {
                if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                    Toast.makeText(this,
                            "Video app requires access to camera"
                            ,Toast.LENGTH_SHORT).show();
                }
                requestPermissions(new String[] {Manifest.permission.CAMERA},
                        REQUEST_CAMERA_PERMISSION_RESULT);
            }
        } else {
            cameraManager.openCamera(mCameraId, mCameraDeviceStateCallBack, mBackgroundHandler);
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

private void startPreview() {
    SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
    surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());
    Surface previewSurface = new Surface(surfaceTexture);
    try {
        mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        mCaptureRequestBuilder.addTarget(previewSurface);

        mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                try {
                    cameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(),
                            null, mBackgroundHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(getApplicationContext(),"unable to setup preview",
                        Toast.LENGTH_SHORT).show();
            }
        } , null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}



private static class CompareSizeByArea implements Comparator<Size> {

    @Override
    public int compare(Size lhs, Size rhs) {
        return Long.signum((long)lhs.getWidth() * lhs.getHeight() /
                (long) rhs.getWidth() * rhs.getHeight());
    }
}

private static Size chooseOptimaSize(Size[] choices, int width, int height) {
    List<Size> bigEnough = new ArrayList<Size>();
    for (Size option : choices) {
        if (option.getHeight() == option.getWidth() * height / width &&
                option.getWidth() >= width && option.getHeight() >= height) {
            bigEnough.add(option);
        }
    }
    if (bigEnough.size() > 0) {
        return Collections.min(bigEnough, new CompareSizeByArea());
    } else {
        return choices[0];
    }

}


@Override
protected void onPause() {
    closeCamera();
    super.onPause();
    stopBackgroundThread();
}

}

activity_main xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextureView
   android:id="@+id/textureView"
   android:layout_width="match_parent"
   android:layout_above="@+id/buttonCapture"
   android:layout_height="match_parent" />

<Button
    android:layout_width="match_parent"
    android:layout_alignParentBottom="true"
    android:layout_height="wrap_content"
    android:id="@+id/buttonCapture"
    android:text="CAPTURE"/>


</RelativeLayout>

манифест:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidcamera2api">

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.hardware.camera2.full" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".CameraActivity"></activity>
</application>

</manifest>

В настоящее время используется SDK 27.

В настоящее время у меня нет реального телефона для тестирования, поэтому я использую эмулятор. Однако, когда я запускаю свое приложение, хотя телефон находится в портретном режиме, камера становится альбомной. Это выглядит примерно так.

enter image description here

Я думал, что код, который обрабатывал это, был в методе setUpCamera, где я поменял местами ширину и высоту, если ориентация равна 90 или 270? Ценю помощь!

1 Ответ

0 голосов
/ 06 июля 2018

Я протестировал ваш код в Google Pixel 2 (Android 8.1 - тот же API, что и у вас), и картинка отображается правильно - голова вверху, в отличие от скриншота, который вы опубликовали.

Итак, я заключаю, что это ошибка в используемом вами эмуляторе.

Я бы сделал это изменение только для вашего AndroidManifest.xml:

<activity android:name=".MainActivity"
          android:screenOrientation="portrait">

Таким образом, вы получите такое же поведение, как, например, Samsung и Google используют свои приложения Camera на своих флагманских телефонах. Ориентация макета пользовательского интерфейса заблокирована (при повороте устройства в альбомную ориентацию поворачиваются только изображения значков, но макет пользовательского интерфейса не поворачивается).

...