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

本讲是Android Camera专题系列的第28讲,我们介绍Android Camera2 API专题的连拍实战,包括如下内容:

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

视频在线观看:

加入知识星球与更多Camera同学交流

  • 星球名称:深入浅出Android Camera
  • 星球ID: 17296815

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

连拍获取多张图片

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



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

GeekCamera2连拍功能

Burst Type

GeekCamera2连拍功能

连拍实现方式

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

赞(2)
未经允许不得转载:极客笔记 » 第28讲 GeekCamera2连拍实战

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
Android Camera2参考
Android Camera2参考概述
Android Camera2参数参考
Android Camera2参数参考概述
Android Camera2指南
Android Camera2指南概述
Android Camera HAL开发
Android Camera HAL开发概述
Android Camera2 API
第2讲 Android Camera 架构介绍第3讲 Camera2 API 概述第4讲 Open和Close Camera流程第5讲 GeekCamera2介绍第6讲 Open和Close Camera代码实现第7讲 Hardware Level详解第8讲 AVAILABLE_CAPABILITIES详解第9讲 StreamConfigurationMap详解一第10讲 StreamConfigurationMap详解二第11讲 StreamConfigurationMap实战一第12讲 StreamConfigurationMap实战二第13讲 为什么需要OutputConfiguration第14讲 OutputConfiguration API详解第15讲 Surface Sharing实战第16讲 Deferred Surface实战第17讲 Reprocessable Capture Session详解一第18讲 Reprocessable Capture Session详解二第19讲 App如何实现ZSL功能第20讲 SessionConfiguration详解第21讲 createCaptureSession详解第22讲 SessionParameter实战第23讲 CaptureRequest详解第24讲 如何操作VendorTag第25讲 获取Physical Camera数据流第26讲 CameraCaptureSession详解第27讲 CameraCaptureSession.CaptureCallback详解第28讲 GeekCamera2连拍实战第29讲 SlowMotion实战第30讲 CaptureResult详解第31讲 AE自动曝光 Part 1第32讲 AE自动曝光 Part 2第33讲 AE自动曝光实战第34讲 AE手动曝光第35讲 AE手动曝光实战第36讲 Flash闪光灯控制第37讲 拍照打闪实战第38讲 通过CropRegion控制Zoom缩放第39讲 通过ZoomRatio控制Zoom缩放第40讲 Digital Zoom缩放实战第41讲 Touch AE实战第42讲 AF自动对焦第43讲 AF自动对焦 第二部分第44讲 AF自动对焦实战第45讲 手动对焦实战第46讲 AWB自动白平衡第47讲 AWB自动白平衡实战第48讲 FD人脸检测第49讲 Android13 Camera2 New APIs介绍Android 14 Camera2 API New Changes尝鲜