第28讲 GeekCamera2连拍实战 - Android Camera2 API

2024年10月25日 第28讲 GeekCamera2连拍实战 极客笔记

本讲是Android Camera专题系列的第28讲,我们介绍Android Camera2 API专题的连拍实战。

更多资源:

资源 描述
在线课程 极客笔记在线课程
知识星球 星球名称:深入浅出Android Camera
星球ID: 17296815
Wechat 极客笔记圈

通过连拍实现三种连续拍图的需求

连拍获取多张图片

连拍获取多张不同曝光的图片

连拍获取多张不同对焦距离的图片

GeekCamera2连拍功能

Burst Type

连拍实现方式

  • captureBurst

  • 多次调用capture方法

点击拍照调用流程

MainActivity#clickedTakePhoto
    MainActivity#takePicture
        MainActivity#takePicturePressed
            Preview#takePicturePressed
                Preview#takePicture
                    Preview#takePhoto
                        Preview#takePhotoWhenFocused 决定burst type
                            CameraController2#setBurstType
                            CameraController2#takePicture
                                CameraController2#takePictureAfterPrecapture

连拍获取多张不同曝光或不同对焦距离的图片

private void takePictureBurstBracketing() {
    if( MyDebug.LOG )
        Log.i(TAG, "takePictureBurstBracketing");
    if( burst_type != BurstType.BURSTTYPE_EXPO && burst_type != BurstType.BURSTTYPE_FOCUS ) {
        Log.e(TAG, "takePictureBurstBracketing called but unexpected burst_type: " + burst_type);
    }

    List<CaptureRequest> requests = new ArrayList<>();
    boolean ok = true;
    ErrorCallback push_take_picture_error_cb = null;

    synchronized( background_camera_lock ) {
        if( mCameraDevice == null || mCameraCaptureSession == null ) {
            if( MyDebug.LOG )
                Log.i(TAG, "no camera or capture session");
            return;
        }
        try {
            if( MyDebug.LOG ) {
                Log.i(TAG, "imageReader: " + imageReader.toString());
                Log.i(TAG, "imageReader surface: " + imageReader.getSurface().toString());
            }

            CaptureRequest.Builder stillBuilder = mCameraDevice.createCaptureRequest(previewIsVideoMode ? CameraDevice.TEMPLATE_VIDEO_SNAPSHOT : CameraDevice.TEMPLATE_STILL_CAPTURE);
            stillBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
            // n.b., don't set RequestTagType.CAPTURE here - we only do it for the last of the burst captures (see below)
            camera_settings.setupBuilder(stillBuilder, true);
            clearPending();
            // shouldn't add preview surface as a target - see note in takePictureAfterPrecapture()
            // but also, adding the preview surface causes the dark/light exposures to be visible, which we don't want
            stillBuilder.addTarget(imageReader.getSurface());
            if( raw_todo )
                stillBuilder.addTarget(imageReaderRaw.getSurface());

            if( burst_type == BurstType.BURSTTYPE_EXPO ) {
                if( MyDebug.LOG )
                    Log.i(TAG, "[CS]expo bracketing");

                /*stillBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
                stillBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);

                stillBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, -6);
                requests.add( stillBuilder.build() );
                stillBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);
                requests.add( stillBuilder.build() );
                stillBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 6);
                requests.add( stillBuilder.build() );*/

                stillBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_OFF);
                if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                    if( MyDebug.LOG )
                        Log.i(TAG, "setting torch for capture");
                    stillBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
                    test_fake_flash_photo++;
                }
                // else don't turn torch off, as user may be in torch on mode

                Range<Integer> iso_range = characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE); // may be null on some devices
                if( iso_range == null ) {
                    Log.e(TAG, "takePictureBurstBracketing called but null iso_range");
                }
                else {
                    // set ISO
                    int iso = 800;
                    // obtain current ISO/etc settings from the capture result - but if we're in manual ISO mode,
                    // might as well use the settings the user has actually requested (also useful for workaround for
                    // OnePlus 3T bug where the reported ISO and exposure_time are wrong in dark scenes)
                    if( camera_settings.has_iso )
                        iso = camera_settings.iso;
                    else if( capture_result_has_iso )
                        iso = capture_result_iso;
                    // see https://sourceforge.net/p/opencamera/tickets/321/ - some devices may have auto ISO that's
                    // outside of the allowed manual iso range!
                    iso = Math.max(iso, iso_range.getLower());
                    iso = Math.min(iso, iso_range.getUpper());
                    stillBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, iso );
                }
                if( capture_result_has_frame_duration  )
                    stillBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, capture_result_frame_duration);
                else
                    stillBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, 1000000000L/30);

                long base_exposure_time = 1000000000L/30;
                if( camera_settings.has_iso )
                    base_exposure_time = camera_settings.exposure_time;
                else if( capture_result_has_exposure_time )
                    base_exposure_time = capture_result_exposure_time;

                int n_half_images = expo_bracketing_n_images/2;
                long min_exposure_time = base_exposure_time;
                long max_exposure_time = base_exposure_time;
                final double scale = Math.pow(2.0, expo_bracketing_stops/(double)n_half_images);
                Range<Long> exposure_time_range = characteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE); // may be null on some devices
                if( exposure_time_range != null ) {
                    min_exposure_time = exposure_time_range.getLower();
                    max_exposure_time = exposure_time_range.getUpper();
                }

                if( MyDebug.LOG ) {
                    Log.i(TAG, "taking expo bracketing with n_images: " + expo_bracketing_n_images);
                    Log.i(TAG, "ISO: " + stillBuilder.get(CaptureRequest.SENSOR_SENSITIVITY));
                    Log.i(TAG, "Frame duration: " + stillBuilder.get(CaptureRequest.SENSOR_FRAME_DURATION));
                    Log.i(TAG, "Base exposure time: " + base_exposure_time);
                    Log.i(TAG, "Min exposure time: " + min_exposure_time);
                    Log.i(TAG, "Max exposure time: " + max_exposure_time);
                }

                // darker images
                for(int i=0;i<n_half_images;i++) {
                    long exposure_time = base_exposure_time;
                    if( exposure_time_range != null ) {
                        double this_scale = scale;
                        for(int j=i;j<n_half_images-1;j++)
                            this_scale *= scale;
                        exposure_time /= this_scale;
                        if( exposure_time < min_exposure_time )
                            exposure_time = min_exposure_time;
                        if( MyDebug.LOG ) {
                            Log.i(TAG, "add burst request for " + i + "th dark image:");
                            Log.i(TAG, "    this_scale: " + this_scale);
                            Log.i(TAG, "    exposure_time: " + exposure_time);
                        }
                        stillBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, exposure_time);
                        stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
                        requests.add( stillBuilder.build() );
                    }
                }

                // base image
                if( MyDebug.LOG )
                    Log.i(TAG, "add burst request for base image");
                stillBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, base_exposure_time);
                stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
                requests.add( stillBuilder.build() );

                // lighter images
                for(int i=0;i<n_half_images;i++) {
                    long exposure_time = base_exposure_time;
                    if( exposure_time_range != null ) {
                        double this_scale = scale;
                        for(int j=0;j<i;j++)
                            this_scale *= scale;
                        exposure_time *= this_scale;
                        if( exposure_time > max_exposure_time )
                            exposure_time = max_exposure_time;
                        if( MyDebug.LOG ) {
                            Log.i(TAG, "add burst request for " + i + "th light image:");
                            Log.i(TAG, "    this_scale: " + this_scale);
                            Log.i(TAG, "    exposure_time: " + exposure_time);
                        }
                        stillBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, exposure_time);
                        if( i == n_half_images - 1 ) {
                            // RequestTagType.CAPTURE should only be set for the last request, otherwise we'll may do things like turning
                            // off torch (for fake flash) before all images are received
                            // More generally, doesn't seem a good idea to be doing the post-capture commands (resetting ae state etc)
                            // multiple times, and before all captures are complete!
                            if( MyDebug.LOG )
                                Log.i(TAG, "set RequestTagType.CAPTURE for last burst request");
                            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE));
                        }
                        else {
                            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
                        }
                        requests.add( stillBuilder.build() );
                    }
                }

                burst_single_request = true;
            } else { // BURSTTYPE_FOCUS
                if( MyDebug.LOG )
                    Log.i(TAG, "[CS] focus bracketing");

                if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                    if( MyDebug.LOG )
                        Log.i(TAG, "setting torch for capture");
                    if( !camera_settings.has_iso )
                        stillBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
                    stillBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
                    test_fake_flash_photo++;
                }

                stillBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_OFF); // just in case

                if( Math.abs(camera_settings.focus_distance - focus_bracketing_source_distance) < 1.0e-5 ) {
                    if( MyDebug.LOG )
                        Log.i(TAG, "current focus matches source");
                }
                else if( Math.abs(camera_settings.focus_distance - focus_bracketing_target_distance) < 1.0e-5 ) {
                    if( MyDebug.LOG )
                        Log.i(TAG, "current focus matches target");
                }
                else {
                    Log.i(TAG, "current focus matches neither source nor target");
                }

                List<Float> focus_distances = setupFocusBracketingDistances(focus_bracketing_source_distance, focus_bracketing_target_distance, focus_bracketing_n_images);
                if( focus_bracketing_add_infinity ) {
                    focus_distances.add(0.0f);
                }
                for(int i=0;i<focus_distances.size();i++) {
                    stillBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, focus_distances.get(i));
                    if( i == focus_distances.size()-1 ) {
                        stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE)); // set capture tag for last only
                    }
                    else {
                        // note, even if we didn't need to set CAPTURE_BURST_IN_PROGRESS, we'd still want
                        // to set a RequestTagObject (e.g., type NONE) so that it can be changed later,
                        // so that cancelling focus bracketing works
                        //stillBuilder.setTag(new RequestTagObject(RequestTagType.NONE));
                        stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
                    }
                    requests.add(stillBuilder.build());

                    focus_bracketing_in_progress = true;
                }

                burst_single_request = false; // we set to false for focus bracketing, as we support bracketing with large numbers of images in this mode
                //burst_single_request = true; // test
            }

            /*
            // testing:
            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
            requests.add( stillBuilder.build() );
            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
            requests.add( stillBuilder.build() );
            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
            requests.add( stillBuilder.build() );
            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
            requests.add( stillBuilder.build() );
            if( MyDebug.LOG )
                Log.i(TAG, "set RequestTagType.CAPTURE for last burst request");
            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE));
            requests.add( stillBuilder.build() );
            */

            n_burst = requests.size();
            n_burst_total = n_burst;
            n_burst_taken = 0;
            n_burst_raw = raw_todo ? n_burst : 0;
            if( MyDebug.LOG ) {
                Log.i(TAG, "[CS] n_burst: " + n_burst);
                Log.i(TAG, "burst_single_request: " + burst_single_request);
            }

            if( !previewIsVideoMode ) {
                mCameraCaptureSession.stopRepeating(); // see note under takePictureAfterPrecapture()
            }
        }
        catch(CameraAccessException e) {
            if( MyDebug.LOG ) {
                Log.e(TAG, "failed to take picture expo burst");
                Log.e(TAG, "reason: " + e.getReason());
                Log.e(TAG, "message: " + e.getMessage());
            }
            e.printStackTrace();
            ok = false;
            jpeg_todo = false;
            raw_todo = false;
            picture_cb = null;
            push_take_picture_error_cb = take_picture_error_cb;
        }
        catch(IllegalStateException e) {
            if( MyDebug.LOG )
                Log.i(TAG, "captureSession already closed!");
            e.printStackTrace();
            ok = false;
            jpeg_todo = false;
            raw_todo = false;
            picture_cb = null;
            // don't report error, as camera is closed or closing
        }
    }

    // need to call callbacks without a lock
    if( ok && picture_cb != null ) {
        if( MyDebug.LOG )
            Log.i(TAG, "call onStarted() in callback");
        picture_cb.onStarted();
    }

    if( ok ) {
        synchronized( background_camera_lock ) {
            if( mCameraDevice == null || mCameraCaptureSession == null ) {
                if( MyDebug.LOG )
                    Log.i(TAG, "no camera or capture session");
                return;
            }
            try {
                modified_from_camera_settings = true;
                if( use_expo_fast_burst && burst_type == BurstType.BURSTTYPE_EXPO ) { // alway use slow burst for focus bracketing
                    if( MyDebug.LOG )
                        Log.i(TAG, "[CS]using fast burst");
                    int sequenceId = mCameraCaptureSession.captureBurst(requests, previewCaptureCallback, mCameraBackgroundHandler);
                    if( MyDebug.LOG )
                        Log.i(TAG, "[CS][create_sequence] takePictureBurstBracketing captureBurst sequenceId:" + sequenceId);
                } else {
                    if( MyDebug.LOG )
                        Log.i(TAG, "using slow burst");
                    slow_burst_capture_requests = requests;
                    slow_burst_start_ms = System.currentTimeMillis();
                    int sequenceId = mCameraCaptureSession.capture(requests.get(0), previewCaptureCallback, mCameraBackgroundHandler);
                    Log.i(TAG, "[CS][create_sequence] takePictureBurstBracketing capture sequenceId:" + sequenceId);
                }

                playSound(MediaActionSound.SHUTTER_CLICK); // play shutter sound asap, otherwise user has the illusion of being slow to take photos
            }
            catch(CameraAccessException e) {
                if( MyDebug.LOG ) {
                    Log.e(TAG, "failed to take picture expo burst");
                    Log.e(TAG, "reason: " + e.getReason());
                    Log.e(TAG, "message: " + e.getMessage());
                }
                e.printStackTrace();
                //noinspection UnusedAssignment
                ok = false;
                jpeg_todo = false;
                raw_todo = false;
                picture_cb = null;
                push_take_picture_error_cb = take_picture_error_cb;
            }
            catch(IllegalStateException e) {
                if( MyDebug.LOG )
                    Log.i(TAG, "captureSession already closed!");
                e.printStackTrace();
                //noinspection UnusedAssignment
                ok = false;
                jpeg_todo = false;
                raw_todo = false;
                picture_cb = null;
                // don't report error, as camera is closed or closing
            }
        }
    }

    // need to call callbacks without a lock
    if( push_take_picture_error_cb != null ) {
        push_take_picture_error_cb.onError();
    }
}

正常连拍

private void takePictureBurst(boolean continuing_fast_burst) {
    if( MyDebug.LOG )
        Log.i(TAG, "takePictureBurst continuing_fast_burst:" + continuing_fast_burst);
    if( burst_type != BurstType.BURSTTYPE_NORMAL && burst_type != BurstType.BURSTTYPE_CONTINUOUS ) {
        Log.e(TAG, "takePictureBurstBracketing called but unexpected burst_type: " + burst_type);
    }

    boolean is_new_burst = true;
    CaptureRequest request = null;
    CaptureRequest last_request = null;
    boolean ok = true;
    ErrorCallback push_take_picture_error_cb = null;

    synchronized( background_camera_lock ) {
        if( mCameraDevice == null || mCameraCaptureSession == null ) {
            if( MyDebug.LOG )
                Log.i(TAG, "no camera or capture session");
            return;
        }
        try {
            if( MyDebug.LOG ) {
                Log.i(TAG, "imageReader: " + imageReader.toString());
                Log.i(TAG, "imageReader surface: " + imageReader.getSurface().toString());
            }

            CaptureRequest.Builder stillBuilder = mCameraDevice.createCaptureRequest(previewIsVideoMode ? CameraDevice.TEMPLATE_VIDEO_SNAPSHOT : CameraDevice.TEMPLATE_STILL_CAPTURE);
            stillBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
            // n.b., don't set RequestTagType.CAPTURE here - we only do it for the last of the burst captures (see below)
            camera_settings.setupBuilder(stillBuilder, true);
            if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                if( MyDebug.LOG )
                    Log.i(TAG, "setting torch for capture");
                if( !camera_settings.has_iso )
                    stillBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
                stillBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
                test_fake_flash_photo++;
            }

            if( burst_type == BurstType.BURSTTYPE_NORMAL && burst_for_noise_reduction ) {
                // must be done after calling setupBuilder(), so we override the default EDGE_MODE and NOISE_REDUCTION_MODE
                if( MyDebug.LOG )
                    Log.i(TAG, "optimise settings for burst_for_noise_reduction");
                stillBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF);
                stillBuilder.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE_OFF);
                stillBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_OFF);
            }

            if( !continuing_fast_burst ) {
                clearPending();
            }
            // shouldn't add preview surface as a target - see note in takePictureAfterPrecapture()
            stillBuilder.addTarget(imageReader.getSurface());
            // RAW target added below

            if( use_fake_precapture_mode && fake_precapture_torch_performed ) {
                stillBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
                test_fake_flash_photo++;
            }
            // else don't turn torch off, as user may be in torch on mode

            if( burst_type == BurstType.BURSTTYPE_CONTINUOUS ) {
                if( MyDebug.LOG )
                    Log.i(TAG, "continuous burst mode");
                raw_todo = false; // RAW works in continuous burst mode, but makes things very slow...
                if( continuing_fast_burst ) {
                    if( MyDebug.LOG )
                        Log.i(TAG, "continuing fast burst");
                    n_burst++;
                    is_new_burst = false;
                    /*if( !continuous_burst_in_progress ) // test bug where we call callback onCompleted() before all burst images are received
                        n_burst = 1;*/
                }
                else {
                    if( MyDebug.LOG )
                        Log.i(TAG, "start continuous burst");
                    continuous_burst_in_progress = true;
                    n_burst = 1;
                    n_burst_taken = 0;
                }
                if( MyDebug.LOG )
                    Log.i(TAG, "n_burst is now " + n_burst);
            }
            else if( burst_for_noise_reduction ) {
                if( MyDebug.LOG )
                    Log.i(TAG, "choose n_burst for burst_for_noise_reduction");
                n_burst = 4;
                n_burst_taken = 0;

                if( capture_result_has_iso ) {
                    // For Nexus 6, max reported ISO is 1196, so the limit for dark scenes shouldn't be more than this
                    // Nokia 8's max reported ISO is 1551
                    // Note that OnePlus 3T has max reported ISO of 800, but this is a device bug
                    if( capture_result_iso >= ISO_FOR_DARK ) {
                        if( MyDebug.LOG )
                            Log.i(TAG, "optimise for dark scene");
                        n_burst = noise_reduction_low_light ? N_IMAGES_NR_DARK_LOW_LIGHT : N_IMAGES_NR_DARK;
                        boolean is_oneplus = Build.MANUFACTURER.toLowerCase(Locale.US).contains("oneplus");
                        // OnePlus 3T at least has bug where manual ISO can't be set to above 800, so dark images end up too dark -
                        // so no point enabling this code, which is meant to brighten the scene, not make it darker!
                        if( !camera_settings.has_iso && !is_oneplus ) {
                            long exposure_time = noise_reduction_low_light ? 1000000000L/3 : 1000000000L/10;
                            if( !capture_result_has_exposure_time || capture_result_exposure_time < exposure_time ) {
                                if( MyDebug.LOG )
                                    Log.i(TAG, "also set long exposure time");
                                modified_from_camera_settings = true;
                                setManualExposureTime(stillBuilder, exposure_time);
                            }
                            else {
                                if( MyDebug.LOG )
                                    Log.i(TAG, "no need to extend exposure time for dark scene, already long enough: " + exposure_time);
                            }
                        }
                    }
                    else if( capture_result_has_exposure_time ) {
                        //final double full_exposure_time_scale = 0.5;
                        final double full_exposure_time_scale = Math.pow(2.0, -0.5);
                        final long fixed_exposure_time = 1000000000L/60; // we only scale the exposure time at all if it's less than this value
                        final long scaled_exposure_time = 1000000000L/120; // we only scale the exposure time by the full_exposure_time_scale if the exposure time is less than this value
                        long exposure_time = capture_result_exposure_time;
                        if( exposure_time <= fixed_exposure_time ) {
                            if( MyDebug.LOG )
                                Log.i(TAG, "optimise for bright scene");
                            //n_burst = 2;
                            n_burst = 3;
                            if( !camera_settings.has_iso ) {
                                double exposure_time_scale = getScaleForExposureTime(exposure_time, fixed_exposure_time, scaled_exposure_time, full_exposure_time_scale);
                                exposure_time *= exposure_time_scale;
                                if( MyDebug.LOG ) {
                                    Log.i(TAG, "reduce exposure shutter speed further, was: " + exposure_time);
                                    Log.i(TAG, "exposure_time_scale: " + exposure_time_scale);
                                }
                                modified_from_camera_settings = true;
                                setManualExposureTime(stillBuilder, exposure_time);
                            }
                        }
                    }
                }
            }
            else {
                if( MyDebug.LOG )
                    Log.i(TAG, "user requested n_burst");
                n_burst = burst_requested_n_images;
                n_burst_taken = 0;
            }
            if( raw_todo )
                stillBuilder.addTarget(imageReaderRaw.getSurface());
            n_burst_total = n_burst;
            n_burst_raw = raw_todo ? n_burst : 0;
            burst_single_request = false;

            if( MyDebug.LOG )
                Log.i(TAG, "n_burst: " + n_burst);

            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE_BURST_IN_PROGRESS));
            request = stillBuilder.build();
            stillBuilder.setTag(new RequestTagObject(RequestTagType.CAPTURE));
            last_request = stillBuilder.build();

            // n.b., don't stop the preview with stop.Repeating when capturing a burst
        }
        catch(CameraAccessException e) {
            if( MyDebug.LOG ) {
                Log.e(TAG, "failed to take picture burst");
                Log.e(TAG, "reason: " + e.getReason());
                Log.e(TAG, "message: " + e.getMessage());
            }
            e.printStackTrace();
            ok = false;
            jpeg_todo = false;
            raw_todo = false;
            picture_cb = null;
            push_take_picture_error_cb = take_picture_error_cb;
        }
    }

    // need to call callbacks without a lock
    if( ok && picture_cb != null && is_new_burst ) {
        if( MyDebug.LOG )
            Log.i(TAG, "call onStarted() in callback");
        picture_cb.onStarted();
    }

    if( ok ) {
        synchronized( background_camera_lock ) {
            if( mCameraDevice == null || mCameraCaptureSession == null ) {
                if( MyDebug.LOG )
                    Log.i(TAG, "no camera or capture session");
                return;
            }
            try {
                final boolean use_burst = true;
                //final boolean use_burst = false;

                if( burst_type == BurstType.BURSTTYPE_CONTINUOUS ) {
                    if( MyDebug.LOG ) {
                        Log.i(TAG, "continuous capture");
                        if( !continuous_burst_in_progress )
                            Log.i(TAG, "    last continuous capture");
                    }
                    continuous_burst_requested_last_capture = !continuous_burst_in_progress;
                    int sequenceId = mCameraCaptureSession.capture(continuous_burst_in_progress ? request : last_request, previewCaptureCallback, mCameraBackgroundHandler);
                    Log.i(TAG, "[create_sequence] takePictureBurst BURSTTYPE_CONTINUOUS capture sequenceId:" + sequenceId);

                    if( continuous_burst_in_progress ) {
                        final int continuous_burst_rate_ms = 100;
                        // also take the next burst after a delay
                        mCameraBackgroundHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                // note, even if continuous_burst_in_progress has become false by this point, still take one last
                                // photo, as need to ensure that we have a request with RequestTagType.CAPTURE, as well as ensuring
                                // we call the onCompleted() method of the callback
                                if( MyDebug.LOG ) {
                                    Log.i(TAG, "take next continuous burst");
                                    Log.i(TAG, "continuous_burst_in_progress: " + continuous_burst_in_progress);
                                    Log.i(TAG, "n_burst: " + n_burst);
                                }
                                if( n_burst >= 10 || n_burst_raw >= 10 ) {
                                    // Nokia 8 in std mode without post-processing options doesn't hit this limit (we only hit this
                                    // if it's set to "n_burst >= 5")
                                    if( MyDebug.LOG ) {
                                        Log.i(TAG, "...but wait for continuous burst, as waiting for too many photos");
                                    }
                                    //throw new RuntimeException(); // test
                                    mCameraBackgroundHandler.postDelayed(this, continuous_burst_rate_ms);
                                }
                                else if( picture_cb.imageQueueWouldBlock(n_burst_raw, n_burst+1) ) {
                                    if( MyDebug.LOG ) {
                                        Log.i(TAG, "...but wait for continuous burst, as image queue would block");
                                    }
                                    //throw new RuntimeException(); // test
                                    mCameraBackgroundHandler.postDelayed(this, continuous_burst_rate_ms);
                                }
                                else {
                                    takePictureBurst(true);
                                }
                            }
                        }, continuous_burst_rate_ms);
                    }
                }
                else if( use_burst ) {
                    List<CaptureRequest> requests = new ArrayList<>();
                    for(int i=0;i<n_burst-1;i++)
                        requests.add(request);
                    requests.add(last_request);
                    if( MyDebug.LOG )
                        Log.i(TAG, "[CS] captureBurst n_burst:" + n_burst);
                    int sequenceId = mCameraCaptureSession.captureBurst(requests, previewCaptureCallback, mCameraBackgroundHandler);
                    if( MyDebug.LOG )
                        Log.i(TAG, "[create_sequence][CS] takePictureBurst use_burst captureBurst sequenceId:" + sequenceId);
                }
                else {
                    final int burst_delay = 100;
                    final CaptureRequest request_f = request;
                    final CaptureRequest last_request_f = last_request;

                    new Runnable() {
                        int n_remaining = n_burst;

                        @Override
                        public void run() {
                            if( MyDebug.LOG ) {
                                Log.i(TAG, "takePictureBurst runnable");
                                if( n_remaining == 1 ) {
                                    Log.i(TAG, "    is last request");
                                }
                            }
                            ErrorCallback push_take_picture_error_cb = null;

                            synchronized( background_camera_lock ) {
                                if( mCameraDevice == null || mCameraCaptureSession == null ) {
                                    if( MyDebug.LOG )
                                        Log.i(TAG, "no camera or capture session");
                                    return;
                                }
                                try {
                                    int sequenceId = mCameraCaptureSession.capture(n_remaining == 1 ? last_request_f : request_f, previewCaptureCallback, mCameraBackgroundHandler);
                                    Log.i(TAG, "[create_sequence] takePictureBurst capture sequenceId:" + sequenceId);
                                    n_remaining--;
                                    if( MyDebug.LOG )
                                        Log.i(TAG, "takePictureBurst n_remaining: " + n_remaining);
                                    if( n_remaining > 0 ) {
                                        mCameraBackgroundHandler.postDelayed(this, burst_delay);
                                    }
                                }
                                catch(CameraAccessException e) {
                                    if( MyDebug.LOG ) {
                                        Log.e(TAG, "failed to take picture burst");
                                        Log.e(TAG, "reason: " + e.getReason());
                                        Log.e(TAG, "message: " + e.getMessage());
                                    }
                                    e.printStackTrace();
                                    jpeg_todo = false;
                                    raw_todo = false;
                                    picture_cb = null;
                                    push_take_picture_error_cb = take_picture_error_cb;
                                }

                                // need to call callbacks without a lock
                                if( push_take_picture_error_cb != null ) {
                                    push_take_picture_error_cb.onError();
                                }
                            }
                        }
                    }.run();
                }

                if( !continuing_fast_burst ) {
                    playSound(MediaActionSound.SHUTTER_CLICK); // play shutter sound asap, otherwise user has the illusion of being slow to take photos
                }
            }
            catch(CameraAccessException e) {
                if( MyDebug.LOG ) {
                    Log.e(TAG, "failed to take picture burst");
                    Log.e(TAG, "reason: " + e.getReason());
                    Log.e(TAG, "message: " + e.getMessage());
                }
                e.printStackTrace();
                //noinspection UnusedAssignment
                ok = false;
                jpeg_todo = false;
                raw_todo = false;
                picture_cb = null;
                push_take_picture_error_cb = take_picture_error_cb;
            }
        }
    }

    // need to call callbacks without a lock
    if( push_take_picture_error_cb != null ) {
        push_take_picture_error_cb.onError();
    }
}

本文链接:http://so.lmcjl.com/news/16208/

展开阅读全文