本讲是Android Camera专题系列的第28讲,我们介绍Android Camera2 API专题的连拍实战。
更多资源:
资源 | 描述 |
---|---|
在线课程 | 极客笔记在线课程 |
知识星球 | 星球名称:深入浅出Android Camera 星球ID: 17296815 |
极客笔记圈 |
通过连拍实现三种连续拍图的需求
连拍获取多张图片
连拍获取多张不同曝光的图片
连拍获取多张不同对焦距离的图片
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();
}
}