Android 为什么camera2 API中的视频会被拉伸?

Android 为什么camera2 API中的视频会被拉伸?,android,aspect-ratio,android-camera2,Android,Aspect Ratio,Android Camera2,我正在构建一个视频摄像头应用程序,它始终处于横向模式,并始终从前摄像头录制。 但是当我录制视频时,它看起来像被拉伸了。如何解决它? 我在哪里犯错 public class MainActivity extends AppCompatActivity { public static final int REQUEST_CAMERA_PERMISSION_RESULT = 0; public static final int REQUEST_WRITE_EXTERNAL_STORAGE_PERMI

我正在构建一个视频摄像头应用程序,它始终处于横向模式,并始终从前摄像头录制。 但是当我录制视频时,它看起来像被拉伸了。如何解决它? 我在哪里犯错

public class MainActivity extends AppCompatActivity {

public static final int REQUEST_CAMERA_PERMISSION_RESULT = 0;
public static final int REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION_RESULT = 1;
private TextureView mTextureView;
//public ImageView mImageView;
private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
        //Toast.makeText(getApplicationContext(),"TextureView is Available",Toast.LENGTH_SHORT).show();
        setupCamera(width, height);
        connectCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {

    }

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

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

    }
};
private CameraDevice mCameraDevice;
private CameraDevice.StateCallback mCameraDeviceStateCallback;

{
    mCameraDeviceStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(CameraDevice camera) {
            mCameraDevice = camera;
            mMediaRecorder = new MediaRecorder();

            if (mIsRecording) {
                try {
                    createVideoFileName();
                } catch (IOException e) {
                    Log.e("Error", e.getMessage());
                    e.printStackTrace();
                }
                startRecord();
                mMediaRecorder.start();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mChronometer.setBase(SystemClock.elapsedRealtime());
                        mChronometer.setVisibility(View.VISIBLE);
                        mChronometer.start();
                    }
                });


            } else {

                startPreview();

            }


            //Toast.makeText(getApplicationContext(),"Camera connected",Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            camera.close();
            mCameraDevice = null;


        }


        @Override
        public void onError(CameraDevice camera, int i) {

        }
    };
}

private HandlerThread mBackgroundHandlerThread;
private Handler mBackgroundHandler;
private String mCameraId;
private Size mPreviewSize;
private Size mVideoSize;
private MediaRecorder mMediaRecorder;
private Chronometer mChronometer;
private ImageView thumb;
//private String V1, V2, V3, V4, V5;
//    private Map<String, String> mapA = new HashMap<>();
// private ImageView[] IMGS = {mImageView1, mImageView2, mImageView3, mImageView4, mImageView5};
private int mTotalRotation;
private CaptureRequest.Builder mCaptureRequestBuilder;
public static int count;
public static int max = 5;
private ImageButton mRecordImageButton;
private boolean mIsRecording = false;
public static File mVideoFolder;
private static File mRawVideoFolder;
public static String mVideoFileName;

//Test
private List<Bitmap> bitMapsAvailable = new ArrayList<>();
private List<String> bitMapsFilePath = new ArrayList<>();
private int bitMapIndex;
CameraCaptureSession storedSession;
private ArrayAdapter bitMapAdapter;
private ArrayAdapter bitMapFileAdapter;
private static SparseIntArray ORIENTATIONS = new SparseIntArray();
public int index;
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 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());
    }
}


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    createVideoFolder();

    mMediaRecorder = new MediaRecorder();
    mChronometer = (Chronometer) findViewById(R.id.chronometer);
    mTextureView = (TextureView) findViewById(R.id.textureView);

    mRecordImageButton = (ImageButton) findViewById(R.id.videoButton);
    mRecordImageButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mIsRecording) {
                mChronometer.stop();
                mChronometer.setVisibility(View.INVISIBLE);
                mIsRecording = false;
                mRecordImageButton.setImageResource(R.mipmap.start_recording);
                //Toast.makeText(getApplicationContext(),"Started",Toast.LENGTH_SHORT).show();

                if(storedSession != null){
                    try {
                        storedSession.stopRepeating();
                        storedSession.abortCaptures();
                    }catch (CameraAccessException e){
                        throw new RuntimeException(e.getMessage());
                    }
                }
                mMediaRecorder.stop();
                mMediaRecorder.reset();
                startPreview();
                //Create bitmap with current video file path
                Bitmap bitMap = ThumbnailUtils.createVideoThumbnail(mVideoFileName, MediaStore.Video.Thumbnails.MICRO_KIND);
                //Add bitmap to array list
                bitMapsAvailable.add(bitMap);
                bitMapsFilePath.add(mVideoFileName);
                // Shows  thumbnails
                showThumbnails();
            } else {
                checkWriteStoragePermission();
            }
        }

    });
}

private void showThumbnails() {

    LinearLayout layout = (LinearLayout) findViewById(R.id.thumbnails);

    bitMapAdapter = new ArrayAdapter(this, R.layout.activity_main, bitMapsAvailable);
    bitMapFileAdapter = new ArrayAdapter(this, R.layout.activity_main, bitMapsFilePath);


    bitMapIndex = 0;
    if (layout.getChildCount() > 0) {
        layout.removeAllViews();
    }
    for (Bitmap eachBitMap : bitMapsAvailable) {

        bitMapIndex++;
        ImageView thumb = new ImageView(this);
        thumb.setId(new Integer(bitMapIndex+ 17));
        thumb.setLayoutParams(new android.view.ViewGroup.LayoutParams(100, 80));
        thumb.setImageBitmap(eachBitMap);
        // Adds the view to the layout
        thumb.setOnClickListener(previewThumb(thumb));


        layout.addView(thumb);
    }
}
View.OnClickListener previewThumb(final ImageView imageview) {

    return new View.OnClickListener() {
        public void onClick(View arg0) {

            index = imageview.getId()-18;
            imageview.setBackgroundColor(0xff999999);
            // Start NewActivity.class
            Intent myIntent = new Intent(MainActivity.this,
                    VideoViewActivity.class);

            Bundle bundle = new Bundle();

            bundle.putStringArrayList("bitMapsAvailable", new ArrayList(bitMapsAvailable));
            bundle.putStringArrayList("bitMapsFilePath", new ArrayList(bitMapsFilePath));
            //Add your data to bundle
            bundle.putInt("urlIndex", index);
            myIntent.putExtras(bundle);
            bitMapAdapter.notifyDataSetChanged();
            bitMapFileAdapter.notifyDataSetChanged();

          //  startActivity(myIntent);
            startActivityForResult(myIntent, 111);

        }
    };
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        if (requestCode == 111) {
            int indexToBeDeleted = data.getIntExtra("indexToBeDeleted",index);
            //bitMapsFilePath.remove(indexToBeDeleted);
            bitMapsAvailable.remove(indexToBeDeleted);
        }
    }
}
@Override
protected void onResume() {
    super.onResume();
    startBackgroundThread();
    if (mTextureView.isAvailable()) {
        setupCamera(mTextureView.getWidth(), mTextureView.getHeight());
        connectCamera();
    } else {
        mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permission, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permission, grantResults);
    if (requestCode == REQUEST_CAMERA_PERMISSION_RESULT) {
        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(getApplicationContext(), "Application will not run without camera service", Toast.LENGTH_SHORT).show();
        }
        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(getApplicationContext(), "Application will not have audio on record ", Toast.LENGTH_SHORT).show();
        }
    }
    if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION_RESULT) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            mIsRecording = true;
            mRecordImageButton.setImageResource(R.mipmap.ic_launcher);
            try {
                createVideoFileName();
            } catch (IOException e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }
            Toast.makeText(this, "Permission Successfully Granted", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "App needs to save video to run", Toast.LENGTH_SHORT).show();
        }
    }
}


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

}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    View decorView = getWindow().getDecorView();
    if (hasFocus) {
        decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    }
}

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_BACK) {
                continue;
            }
            StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            //int deviceOrientation = getWindowManager().getDefaultDisplay().getRotation();
            mTotalRotation = sensorToDeviceRotation(cameraCharacteristics);
            boolean swapRotation = mTotalRotation == 90 || mTotalRotation == 270;
            int rotateWidth = width;
            int rotateHeight = height;
            if (swapRotation) {
                rotateWidth = height;
                rotateHeight = width;
            }
            mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), rotateWidth, rotateHeight);
            mVideoSize = chooseOptimalSize(map.getOutputSizes(MediaRecorder.class), rotateWidth, rotateHeight);
            mCameraId = cameraId;

            return;
        }
    } catch (CameraAccessException e) {
        Log.e("Error", e.getMessage());
        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 required access to camera", Toast.LENGTH_SHORT).show();
                }
                requestPermissions(new String[]{android.Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA_PERMISSION_RESULT);
            }
        } else {
            cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, mBackgroundHandler);
        }
    } catch (CameraAccessException e) {
        Log.e("Error", e.getMessage());
        e.printStackTrace();
    }
}

private void startRecord() {
    if (this.bitMapsAvailable.size() < max) {

        try {
            setupMediaRecorder();
            SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
            surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            Surface previewSurface = new Surface(surfaceTexture);
            Surface recordSurface = mMediaRecorder.getSurface();
            mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
            mCaptureRequestBuilder.addTarget(previewSurface);
            mCaptureRequestBuilder.addTarget(recordSurface);

            mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, recordSurface), new CameraCaptureSession.StateCallback() {

                @Override
                public void onConfigured(CameraCaptureSession session) {
                    try {
                        session.setRepeatingRequest(mCaptureRequestBuilder.build(), null, null);
                        storedSession = session;
                    } catch (CameraAccessException e) {
                        Log.e("Error", e.getMessage());
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {

                }
            }, null);
        } catch (Exception e) {
            Log.e("Error", e.getMessage());
            e.printStackTrace();
        }
    } else {

    }

}

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(CameraCaptureSession session) {
                try {
                    session.stopRepeating();
                    session.abortCaptures();
                    session.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mBackgroundHandler);

                } catch (CameraAccessException e) {
                    Log.e("Error", e.getMessage());
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(getApplicationContext(), "Unable to setup Camera Preview", Toast.LENGTH_SHORT).show();
            }
        }, null);
    } catch (CameraAccessException e) {
        Log.e("Error", e.getMessage());
        e.printStackTrace();
    }
}

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

    }
}

private void startBackgroundThread() {
    mBackgroundHandlerThread = new HandlerThread("AuthorTV");
    mBackgroundHandlerThread.start();
    mBackgroundHandler = new Handler(mBackgroundHandlerThread.getLooper());
}

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

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

private static Size chooseOptimalSize(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];
    }
}


private void setupMediaRecorder() throws IOException {
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mMediaRecorder.setOutputFile(mVideoFileName);
    mMediaRecorder.setVideoEncodingBitRate(1000000);
    mMediaRecorder.setVideoFrameRate(30);
    mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
    mMediaRecorder.setOrientationHint(mTotalRotation);
    mMediaRecorder.prepare();
}
public类MainActivity扩展了AppCompatActivity{
公共静态最终int请求\摄像机\权限\结果=0;
公共静态最终整数请求\写入\外部\存储\权限\结果=1;
私有纹理视图mTextureView;
//公共图像视图mImageView;
私有TextureView.SurfaceTextRelistener mSurfaceTextureListener=新的TextureView.SurfaceTextRelistener(){
@凌驾
SurfaceTexture上的公共空心可用(SurfaceTexture SurfaceTexture,int-width,int-height){
//Toast.makeText(getApplicationContext(),“TextureView可用”,Toast.LENGTH_SHORT.show();
设置摄像机(宽度、高度);
连接摄像头();
}
@凌驾
SurfaceTextureSizeChanged上的公共空心(SurfaceTexture SurfaceTexture,int-width,int-height){
}
@凌驾
公共布尔onSurfaceTextureDestroyed(SurfaceTexture SurfaceTexture){
返回false;
}
@凌驾
已更新SurfaceTexture上的公共空间(SurfaceTexture SurfaceTexture){
}
};
私人摄像设备;
私有CameraDevice.StateCallback mCameraDeviceStateCallback;
{
mCameraDeviceStateCallback=新的CameraDevice.StateCallback(){
@凌驾
打开公共空间(摄像头副摄像头){
mCameraDevice=摄像机;
mMediaRecorder=新的MediaRecorder();
如果(错误记录){
试一试{
createVideoFileName();
}捕获(IOE异常){
Log.e(“Error”,e.getMessage());
e、 printStackTrace();
}
startRecord();
mMediaRecorder.start();
runOnUiThread(新的Runnable(){
@凌驾
公开募捐{
mchrometer.setBase(SystemClock.elapsedRealtime());
mchrometer.setVisibility(视图.可见);
mchrometer.start();
}
});
}否则{
startPreview();
}
//Toast.makeText(getApplicationContext(),“摄像头已连接”,Toast.LENGTH\u SHORT.show();
}
@凌驾
已断开连接的公共空间(摄像头副摄像头){
摄像机关闭();
mCameraDevice=null;
}
@凌驾
公共无效onError(摄像头设备摄像头,int i){
}
};
}
私有HandlerThread mBackgroundHandlerThread;
私人处理程序mBackgroundHandler;
私人字符串mCameraId;
私人规模的mPreviewSize;
私人规模;
专用媒体记录器;
私人天文钟;
私用图像查看拇指;
//私有字符串V1、V2、V3、V4、V5;
//私有映射mapA=newhashmap();
//私有图像视图[]IMGS={mImageView1、mImageView2、mImageView3、mImageView4、mImageView5};
私人国际旋转;
私有CaptureRequest.Builder mCaptureRequestBuilder;
公共静态整数计数;
公共静态int max=5;
私有图像按钮mRecordImageButton;
私有布尔错误记录=假;
公共静态文件文件夹;
私有静态文件文件夹;
公共静态字符串文件名;
//试验
私有列表bitMapsAvailable=新ArrayList();
私有列表bitMapsFilePath=new ArrayList();
私有int位图索引;
CameraCaptureSession存储会话;
专用ArrayAdapter位图适配器;
专用阵列适配器bitMapFileAdapter;
专用静态SparseIntArray方向=新SparseIntArray();
公共整数指数;
静止的{
方向。附加(Surface.ROTATION_0,0);
方向。附加(Surface.ROTATION_90,90);
方向。附加(Surface.ROTATION_180,180);
方向。附加(Surface.ROTATION_270,270);
}
私有静态类CompareSizeByArea实现Comparator{
@凌驾
公共整数比较(大小lhs,大小rhs){
返回Long.signum((Long)lhs.getWidth()*lhs.getHeight()/(Long)rhs.getWidth()*rhs.getHeight());
}
}
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//setRequestedOrientation(ActivityInfo.SCREEN\u ORIENTATION\u横向);
createVideoFolder();
mMediaRecorder=新的MediaRecorder();
McChronometer=(计时器)findViewById(R.id.计时器);
mTextureView=(TextureView)findViewById(R.id.TextureView);
mRecordImageButton=(ImageButton)findViewById(R.id.videoButton);
mRecordImageButton.setOnClickListener(新视图.OnClickListener(){
@凌驾
公共void onClick(视图v){
如果(错误记录){
mchrometer.stop();
mchrometer.setVisibility(视图.不可见);
错误记录=错误;
mRecordImageButton.setImageResource(R.mipmap.start\u录制);
//Toast.makeText(getApplicationContext(),“Started”,Toast.LENGTH\u SHORT.show();
if(storedSession!=null){
试一试{
storedSession.stopRepeating();
storedSession.abortCaptures();
}捕获(CameraAccessE异常){
抛出新的RuntimeException(例如getMessage());
}
}
mMediaRecorder.stop();
mMediaRecorder.reset();
startPreview();
//使用当前视频文件路径创建位图
位图Bitmap=ThumbnailUtils.createVideoThumbnail(mVideoFileName,MediaStore.Video.Thumbnails.MICRO_-KIND);
//将位图添加到数组列表
bitMapsAvailable.add(位图);
添加(mVideoFileName);
//显示缩略图
嘘