java - Android:尝试第二次录制时 Camera2 API 崩溃

标签 java android android-camera2

我已经尝试了几个小时了,但似乎仍然无法让它发挥作用。

使用this作为引用。我编写了一个记录和保存视频的应用程序。我运行引用应用程序没有任何问题,但当我运行自己的应用程序时出现了许多问题。

问题: 该应用程序在录制视频、保存视频等方面没有问题。但是,在录制并保存第一个视频后,如果我尝试录制第二个视频,它会崩溃。第二个错误据说是由 MediaRecorder:prepare failed: -2147483648 引起的,这又导致了 IllegalStateException。(日志如下)我该如何解决这个问题?

enter image description here

list 权限:

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

代码:

public class VideoCapture extends AppCompatActivity {
    static final int REQUEST_CODE_CAMERA = 0;
    static final int REQUEST_CODE_EXTERNAL_WRITE = 1;
    static final int REQUEST_CODE_MICROPHONE = 2;

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

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

    File videoFolder;
    File videoFile;

    ImageButton recordButton;
    Chronometer chronometer;

    String cameraID;
    MediaRecorder mediaRecorder;

    Size previewSize;
    Size videoSize;
    int totalRotation;

    boolean isRecording;

    HandlerThread backgroundHandlerThread;
    Handler backgroundHandler;

    CaptureRequest.Builder captureRequestBuilder;
    CameraCaptureSession previewCaptureSession;
    CameraCaptureSession recordCaptureSession;

    TextureView textureView;
    TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            setupCamera(width, height);
            startCamera();
            Log.e("onSurfaceTextureAvail", "startCamera width:"+String.valueOf(width)+" height" +String.valueOf(height));
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            Log.e("onSurfaceTextureDestroy","Surface destroyed" + String.valueOf(surface));
            return false;
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            Log.e("onSurfaceTextureSizeCha","Surface changed" + String.valueOf(surface) +"width" +String.valueOf(width)+" height" +String.valueOf(height));
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {

        }
    };

    CameraDevice cameraDevice;
    CameraDevice.StateCallback cameraDeviceStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            cameraDevice = camera;
            mediaRecorder = new MediaRecorder();
            Log.e("cameraDeviceStateCalbac","onOpened mediaRecorder = new MediaRecorder(); camera:"+String.valueOf(camera));

            /**
             * Application will "Reset" when granting write storage permission and call onPause and onResume.
             * The check is to ensure that the camera will correctly follow up with start record.
             **/

            Log.e("cameraDeviceStateCalbac", "onOpened calling startPreview");
            startPreview();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            Log.e("cameraDeviceStateCalbac", "onDisconnected closing camera" + String.valueOf(camera));
            camera.close();
            cameraDevice = null;
            Log.e("cameraDeviceStateCalbac", "onDisconnected nulling camera to value:" + String.valueOf(cameraDevice));
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            Log.e("cameraDeviceStateCalbac", "onError closing camera" + String.valueOf(camera)+"Error value: "+String.valueOf(error));
            camera.close();
            cameraDevice = null;
            Log.e("cameraDeviceStateCalbac", "onError nulling camera to value:" + String.valueOf(cameraDevice));
        }
    };

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK)) {
            finish();
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_videocapture);

        Log.e("onCreate", "Running");
        if (checkCameraPermission() != PackageManager.PERMISSION_GRANTED) requestCameraPermission();
        if (checkWriteExternalStoragePermission() != PackageManager.PERMISSION_GRANTED)
            requestWriteExternalStoragePermission();
        if (checkMicrophonePermission() != PackageManager.PERMISSION_GRANTED)
            requestMicrophonePermission();

        Log.e("onCreate", "Calling createVideoFolder");
        createVideoFolder();
        isRecording = false;
        Log.e("onCreate", "setting isRecording Value" + String.valueOf(isRecording));

        // Chronometer is the timer during a video recording
        chronometer = (Chronometer) findViewById(R.id.chronometer);
        // TextureView is the container that displays the preview
        textureView = (TextureView) findViewById(R.id.textureView);

        // Set up video capture click handler
        recordButton = (ImageButton) findViewById(R.id.recordButton);
        Log.e("onCreate", "recordButton setOnClickListener");
        recordButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) // Record/Stop button click
            {
                Log.e("recordOnClick","RUNNING");
                if (!isRecording) // Start Recording
                {
                    Log.e("recordOnClick","isRecording = false");
                    if (checkCameraPermission() == PackageManager.PERMISSION_GRANTED &&
                            checkWriteExternalStoragePermission() == PackageManager.PERMISSION_GRANTED) {
                        try {


                            isRecording = true;
                            Log.e("recordOnClick"," set isRecording = true. Final value: "+String.valueOf(isRecording));
                            recordButton.setImageResource(R.mipmap.btn_video_busy);

                            Log.e("recordOnClick", "Calling createVideoFile()");
                            createVideoFile();

                            Log.e("recordOnClick", "Calling startRecord()");
                            startRecord();

                            Log.e("recordOnClick", "Calling  mediaRecorder.start()");
                            mediaRecorder.start();
                            Log.e("recordOnClick", "If you see this, mediaRecorder.start() passed.");
                            chronometer.setBase(SystemClock.elapsedRealtime());
                            chronometer.setVisibility(View.VISIBLE);
                            chronometer.start();

                        } catch (IllegalArgumentException iaEx) {
                            Log.e("recordOnClick","click to record failed.");
                            StringWriter writer = new StringWriter();
                            PrintWriter printWriter = new PrintWriter(writer);
                            iaEx.printStackTrace(printWriter);
                            printWriter.flush();
                            String stackTrace = writer.toString();
                            Toast.makeText(getApplicationContext(), stackTrace, Toast.LENGTH_LONG).show();
                        } catch (IOException ex) {
                            Log.e("recordOnClick","click to record failed.");
                        }
                    }
                } else // Stop recording
                {

                    Log.e("recordOnClick","isRecording = true");
                    isRecording = false;
                    Log.e("recordOnClick"," set isRecording = false. Final value: "+String.valueOf(isRecording));
                    chronometer.stop();
                    chronometer.setVisibility(View.INVISIBLE);

                    recordButton.setImageResource(R.mipmap.btn_video_online);

                    Log.e("recordOnClick","Calling mediaRecorder.stop()");
                    mediaRecorder.stop();
                    Log.e("recordOnClick", "If you see this, mediaRecorder.stop() passed.");

                    Log.e("recordOnClick","Calling mediaRecorder.reset()");
                    mediaRecorder.reset();
                    Log.e("recordOnClick", "If you see this, mediaRecorder.reset() passed.");

                    Intent mediaStoreUpdateIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                    mediaStoreUpdateIntent.setData(Uri.fromFile(videoFile));
                    sendBroadcast(mediaStoreUpdateIntent);

                    Log.e("recordOnClick","Calling startPreview()");
                    startPreview();
                    Log.e("recordOnClick", "If you see this, startPreview() passed.");

                    Intent intent = new Intent(VideoCapture.this, FrameExtraction.class);
                    intent.putExtra("videoUri", Uri.fromFile(videoFile));
                    Log.e("recordOnClick","Video Uri Saved" + String.valueOf(Uri.fromFile(videoFile)));
                    startActivity(intent);
                    finish();


                }
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_CAMERA) 
        {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(getApplicationContext(), "Application will not be able to capture videos without camera permission.", Toast.LENGTH_SHORT).show();
            }
        } else if (requestCode == REQUEST_CODE_EXTERNAL_WRITE) 
        {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(getApplicationContext(), "Application doesn't have write permission. Videos cannot be saved.", Toast.LENGTH_SHORT).show();
            }
        } else if (requestCode == REQUEST_CODE_MICROPHONE) {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(getApplicationContext(), "Application doesn't have microphone permission. Videos will have no audio.", Toast.LENGTH_SHORT).show();
            }
        }
    }

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

        startBackgroundThread();

        if (textureView.isAvailable()) {
            setupCamera(textureView.getWidth(), textureView.getHeight());
            startCamera();
            Log.e("RESUME", "RESUME STARTCAMERA");
        } else {
            textureView.setSurfaceTextureListener(surfaceTextureListener);
            Log.e("RESUME", "ELSE, SET TEXTURE LISTENER");
        }
    }

    @Override
    protected void onPause() {

        Log.e("PAUSE", "PAUSE CloseCAMERA");
        cameraDevice.close();
        cameraDevice = null;
        stopBackgroundThread();
        super.onPause();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocas) {
        super.onWindowFocusChanged(hasFocas);
        View decorView = getWindow().getDecorView();
        if (hasFocas) {
            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) {
        Log.e("setupCamera","RUNNING");

        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            identification number
            for (String currentCameraId : cameraManager.getCameraIdList()) {

                CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(currentCameraId);
                if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT)
                    continue;


                int deviceOrientation = getWindowManager().getDefaultDisplay().getRotation();
                totalRotation = sensorToDeviceRotation(cameraCharacteristics, deviceOrientation);
                boolean swapRotation = totalRotation == 90 || totalRotation == 270;
                int rotatedWidth = width;
                int rotatedHeight = height;
                if (swapRotation) {
                    rotatedWidth = height;
                    rotatedHeight = width;
                }


                StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

                previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), rotatedWidth, rotatedHeight);
                videoSize = chooseOptimalSize(map.getOutputSizes(MediaRecorder.class), rotatedWidth, rotatedHeight);
                cameraID = currentCameraId;
                return;
            }
        } catch (CameraAccessException e) {
        }
    }

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

    private Size chooseOptimalSize(Size[] choices, int width, int height) {
        List<Size> bigEnough = new ArrayList<>();
        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 startCamera() {
        Log.e("startCamera()","RUNNING");
        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (checkCameraPermission() == PackageManager.PERMISSION_GRANTED)
                    cameraManager.openCamera(cameraID, cameraDeviceStateCallback, backgroundHandler);
                else {
                    requestCameraPermission();
                }
            } else
                cameraManager.openCamera(cameraID, cameraDeviceStateCallback, backgroundHandler);
        } catch (CameraAccessException e) {
            Log.e("startCamera()", "Failed to cameraManager.openCamera" + String.valueOf(e));
        }
    }


    private void startPreview() {
        Log.e("startPreview","RUNNING");
        SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
        surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
        Surface previewSurface = new Surface(surfaceTexture);

        try {
            captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            captureRequestBuilder.addTarget(previewSurface);

            cameraDevice.createCaptureSession(Arrays.asList(previewSurface),
                    new CameraCaptureSession.StateCallback() {
                        @Override
                        public void onConfigured(CameraCaptureSession session) {
                            previewCaptureSession = session;
                            try {
                                previewCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);
                            } catch (CameraAccessException e) {
                            }
                        }

                        @Override
                        public void onConfigureFailed(CameraCaptureSession session) {
                            Log.e("startPreview", "onConfigureFailed");
                        }
                    }, null);
        } catch (CameraAccessException e) {
            Log.e("startPreviewCATCH", "StartPreview FAILED CATCH: " + String.valueOf(e));
        }
    }


    private void startRecord() {

        try {
            Log.e("startRecord", "RUNNING");

            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mediaRecorder.setOutputFile(videoFile.getAbsolutePath());
            mediaRecorder.setVideoEncodingBitRate(1000000);
            mediaRecorder.setVideoFrameRate(60);
            mediaRecorder.setVideoSize(videoSize.getWidth(), videoSize.getHeight());
            mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mediaRecorder.setOrientationHint(totalRotation);
        }catch(Exception e){

        }
        try {

            mediaRecorder.prepare();
            Log.e("startRecord","mediaRecorder.prepare()");

            SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
            surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
            Surface previewSurface = new Surface(surfaceTexture);
            Surface recordSurface = mediaRecorder.getSurface();
            captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
            captureRequestBuilder.addTarget(previewSurface);
            captureRequestBuilder.addTarget(recordSurface);

            cameraDevice.createCaptureSession(Arrays.asList(previewSurface, recordSurface),
                    new CameraCaptureSession.StateCallback() {
                        @Override
                        public void onConfigured(CameraCaptureSession session) {
                            recordCaptureSession = session;
                            try {
                                recordCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
                            } catch (CameraAccessException e) {
                            }
                        }

                        @Override
                        public void onConfigureFailed(CameraCaptureSession session) {
                        }
                    }, null);
        } catch (IOException ioEx) {
            Log.e("prepare Failed", String.valueOf(ioEx));
        } // mediaRecorder.prepare()
        catch (CameraAccessException caEx) {
        } // cameraDevice.createCaptureSession()
    }


    private void closeCamera() {
        if (cameraDevice != null) {
            cameraDevice.close();
            cameraDevice = null;
        }
        if (mediaRecorder != null) {
            mediaRecorder.reset();

            Log.e("closeCamera()","mediaRecorder.reset()");
            mediaRecorder = null;

            Log.e("closeCamera()","mediaRecorder = null");
        }
    }


    private int checkCameraPermission() {
        return ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA);
    }


    private int checkWriteExternalStoragePermission() {
        return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }


    private int checkMicrophonePermission() {
        return ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);
    }


    private void requestCameraPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_CAMERA);
    }


    private void requestWriteExternalStoragePermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_EXTERNAL_WRITE);
    }


    private void requestMicrophonePermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE_MICROPHONE);
    }


    private void createVideoFolder() {
        // Create the folder for video files to be written to if it doesn't exist.
        // Battery Videos is the folder name created under the movies public directory.
        Log.e("createVideoFolder", "RUNNING");
        videoFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), "Battery Videos");
        if (!videoFolder.exists()) videoFolder.mkdirs();
    }


    private File createVideoFile() throws IOException {
        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String fileName = "Video_" + timestamp + ".mp4";
        videoFile = new File(videoFolder, fileName);
        return videoFile;
    }


    private void startBackgroundThread() {
        backgroundHandlerThread = new HandlerThread("Camera2VideoImage");
        backgroundHandlerThread.start();
        backgroundHandler = new Handler(backgroundHandlerThread.getLooper());
    }


    private void stopBackgroundThread() {
        backgroundHandlerThread.quitSafely();
        try {
            backgroundHandlerThread.join();
            backgroundHandlerThread = null;
            backgroundHandler = null;
        } catch (InterruptedException e) {
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
Log.e("onDestroy", "It Happened");
        mediaRecorder.release();
    }
}

startRecord() 中的堆栈跟踪:

   W/System.err: java.io.IOException: prepare failed.
W/System.err:     at android.media.MediaRecorder._prepare(Native Method)
W/System.err:     at android.media.MediaRecorder.prepare(MediaRecorder.java:782)
W/System.err:     at com.application.batterysoc.activity.VideoCapture.startRecord(VideoCapture.java:490)
W/System.err:     at com.application.batterysoc.activity.VideoCapture.access$600(VideoCapture.java:55)
W/System.err:     at com.application.batterysoc.activity.VideoCapture$3.onClick(VideoCapture.java:214)
W/System.err:     at android.view.View.performClick(View.java:4861)
W/System.err:     at android.view.View$PerformClick.run(View.java:19980)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:739)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err:     at android.os.Looper.loop(Looper.java:211)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5373)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1020)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:815)
E/recordOnClick: Calling  mediaRecorder.start()

最佳答案

我已经解决了这个问题,引用this site

删除行 mediaRecorder.setVideoFrameRate(60);

应用程序现在运行没有任何问题。

关于java - Android:尝试第二次录制时 Camera2 API 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45902858/

相关文章:

android - 如何从Soundcloud获取音频?

android - ADP同时远程调试两台设备

Android camera2 输出到 ImageReader 格式 YUV_420_888 仍然很慢

java - 为什么打开闪光灯会改变相机的对焦模式?

java - 枚举单例如何运作?

java - NullPointerException 问题

android - 如何发布带有第三方依赖的android SDK?

java - 使用 TextureView 缩放 Camera2 预览

JavaFX "Enter"和 "Tab"事件

java - Equinox 与 SWT/Jface