Loading app/src/main/java/net/sourceforge/opencamera/MainActivity.java +10 −2 Original line number Diff line number Diff line Loading @@ -3461,6 +3461,11 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen if( !update_camera ) { // don't try to update camera } else if( preview.isPreviewStarting() ) { // ideally we should avoid getting into this state - we shouldn't allow changing settings if preview is still opening (e.g., we prevent opening // the popup menu if preview is not started, and we close camera when going to settings) Log.e(TAG, "updateForSettings: preview is still starting"); } else if( need_reopen || preview.getCameraController() == null ) { // if camera couldn't be opened before, might as well try again if( allow_dim ) applicationInterface.getDrawPreview().setDimPreview(true); Loading @@ -3486,7 +3491,10 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen // Even if allow_dim==false, still run as a postDelayed (a) for consistency, (b) to allow UI to run for a bit (to avoid risk of slow frames). handler.postDelayed(new Runnable() { public void run() { preview.setupCamera(false); // if we ever support calling this with wait_until_started==true, beware of trying to change resolution repeatedly when the popup menu // is open - as currently we'll have problems if trying to calling updateForSettings when preview is already starting on background // thread (see above) preview.setupCamera(false, true, null); } }, DrawPreview.dim_effect_time_c+16); // +16 to allow time for a frame update to run } Loading Loading @@ -3683,7 +3691,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen // starting the preview will disable the PausePreviewOnBackPressedCallback, so no need to do it here if( MyDebug.LOG ) Log.d(TAG, "preview was paused, so unpause it"); preview.startCameraPreview(); preview.startCameraPreview(true, null); } else { // shouldn't be here (if preview isn't paused, this callback shouldn't be enabled), but just in case Loading app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +2 −2 Original line number Diff line number Diff line Loading @@ -3833,7 +3833,7 @@ public class MyApplicationInterface extends BasicApplicationInterface { } if( done ) { clearLastImages(); preview.startCameraPreview(); preview.startCameraPreview(true, null); } } } Loading Loading @@ -3907,7 +3907,7 @@ public class MyApplicationInterface extends BasicApplicationInterface { } clearLastImages(); drawPreview.clearGhostImage(); // doesn't make sense to show the last image as a ghost, if the user has trashed it! preview.startCameraPreview(); preview.startCameraPreview(true, null); } // Calling updateGalleryIcon() immediately has problem that it still returns the latest image that we've just deleted! // But works okay if we call after a delay. 100ms works fine on Nexus 7 and Galaxy Nexus, but set to 500 just to be safe. Loading app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java +19 −1 Original line number Diff line number Diff line Loading @@ -660,7 +660,25 @@ public abstract class CameraController { /** Starts the camera preview. * @throws CameraControllerException if the camera preview fails to start. */ public abstract void startPreview() throws CameraControllerException; /** Starts the camera preview. * @param wait_until_started Whether to wait until the preview is started. Only relevant for * CameraController2; CameraController1 will always wait. * @param runnable If non-null, a runnable to be called once preview is started. If * wait_until_started==true, or using CameraController1, this will be * called on the current thread, before this method exits. Otherwise, * this will be called on the UI thread, after this method exits (once * the preview has started). * @param on_failed If non-null, a runnable to be called if the preview fails to start. * Only relevant for wait_until_started==false and when using * CameraController2. In such cases, failing to start the camera preview * may result in either CameraControllerException being thrown, or * on_failed being called on the UI thread after this method exits * (depending on when the failure occurs). If either of these happens, * the "runnable" runnable will not be called. * @throws CameraControllerException Failed to start preview. In this case, the runnable will not * be called. */ public abstract void startPreview(boolean wait_until_started, Runnable runnable, Runnable on_failed) throws CameraControllerException; /** Only relevant for CameraController2: stops the repeating burst for the previous (so effectively * stops the preview), but does not close the capture session for the preview (for that, using * stopPreview() instead of stopRepeating()). Loading app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java +5 −2 Original line number Diff line number Diff line Loading @@ -1577,7 +1577,7 @@ public class CameraController1 extends CameraController { } @Override public void startPreview() throws CameraControllerException { public void startPreview(boolean wait_until_started, Runnable runnable, Runnable on_failed) throws CameraControllerException { if( MyDebug.LOG ) Log.d(TAG, "startPreview"); try { Loading @@ -1589,6 +1589,9 @@ public class CameraController1 extends CameraController { e.printStackTrace(); throw new CameraControllerException(); } if( runnable != null ) { runnable.run(); } } @Override Loading Loading @@ -1838,7 +1841,7 @@ public class CameraController1 extends CameraController { // need to start preview again: otherwise fail to take subsequent photos on Nexus 6 // and Nexus 7; on Galaxy Nexus we succeed, but exposure compensation has no effect try { startPreview(); startPreview(true, null, null); } catch(CameraControllerException e) { if( MyDebug.LOG ) Loading app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +242 −53 Original line number Diff line number Diff line Loading @@ -2451,12 +2451,22 @@ public class CameraController2 extends CameraController { if( MyDebug.LOG ) Log.d(TAG, "release: " + this); closeCaptureSession(); CameraDevice camera_to_close = this.camera; synchronized( background_camera_lock ) { // set all to null straight away, as this can be called on background thread, but also // don't want to be in an incomplete state for other threads where camera is non-null but // previewBuilder is null previewBuilder = null; previewIsVideoMode = false; if( camera != null ) { camera.close(); camera = null; } if( camera_to_close != null ) { if( MyDebug.LOG ) Log.d(TAG, "close camera: " + camera_to_close); camera_to_close.close(); if( MyDebug.LOG ) Log.d(TAG, "close camera complete: " + camera_to_close); } closePictureImageReader(); /*if( previewImageReader != null ) { previewImageReader.close(); Loading @@ -2476,6 +2486,8 @@ public class CameraController2 extends CameraController { e.printStackTrace(); } } if( MyDebug.LOG ) Log.d(TAG, "release exit: " + this); } /** Enforce a minimum number of points in tonemap curves - needed due to Galaxy S10e having wrong behaviour if fewer Loading Loading @@ -6210,7 +6222,60 @@ public class CameraController2 extends CameraController { return outputs; } private void createCaptureSession(final MediaRecorder video_recorder, boolean want_photo_video_recording) throws CameraControllerException { private abstract static class CreateCaptureSessionFunction { public abstract void call() throws CameraAccessException; } /** Function to support calling a function either on background thread or not depending on wait_until_started. */ private void launchCameraSession(boolean wait_until_started, CreateCaptureSessionFunction function, Runnable on_failed) throws CameraAccessException { if( wait_until_started ) { /*try { Thread.sleep(6000); // test slow to start preview //Thread.sleep(25000); // test slow to start preview } catch(InterruptedException e) { throw new RuntimeException(e); }*/ function.call(); } else { handler.post(new Runnable() { @Override public void run() { try { /*try { Thread.sleep(6000); // test slow to start preview //Thread.sleep(25000); // test slow to start preview } catch(InterruptedException e) { throw new RuntimeException(e); }*/ function.call(); } catch(CameraAccessException e) { Log.e(TAG, "exception create extension session on background thread"); e.printStackTrace(); //myStateCallback.onConfigureFailed(); if( on_failed != null ) { // if waiting, failure will be indicated via CameraControllerException thrown from this method final Activity activity = (Activity)context; activity.runOnUiThread(new Runnable() { @Override public void run() { if( MyDebug.LOG ) Log.d(TAG, "call on_failed as preview failed to start"); on_failed.run(); } }); } } } }); } } private void createCaptureSession(boolean wait_until_started, Runnable runnable, Runnable on_failed, final MediaRecorder video_recorder, boolean want_photo_video_recording) throws CameraControllerException { if( MyDebug.LOG ) Log.d(TAG, "create capture session"); Loading Loading @@ -6327,18 +6392,46 @@ public class CameraController2 extends CameraController { class MyStateCallback extends CameraCaptureSession.StateCallback { private boolean callback_done; // must synchronize on this and notifyAll when setting to true private void onFailure() { if( on_failed != null && !wait_until_started ) { // if waiting, failure will be indicated on main thread below via CameraControllerException final Activity activity = (Activity)context; activity.runOnUiThread(new Runnable() { @Override public void run() { if( MyDebug.LOG ) Log.d(TAG, "call on_failed as preview failed to start"); on_failed.run(); } }); } } void onConfigured(@NonNull CameraCaptureSession session, @NonNull CameraExtensionSession eSession) { boolean success = false; // whether we successfully started the preview /*try { Thread.sleep(6000); // test slow to start preview //Thread.sleep(25000); // test slow to start preview } catch(InterruptedException e) { throw new RuntimeException(e); }*/ synchronized( background_camera_lock ) { if( camera == null ) { if( MyDebug.LOG ) { Log.d(TAG, "camera is closed"); } synchronized( background_camera_lock ) { callback_done = true; background_camera_lock.notifyAll(); } // don't call onFailure() - if camera has closed in the meantime, no need to report to user (e.g. this might be going to Settings // whilst preview was starting) return; } synchronized( background_camera_lock ) { if( MyDebug.LOG ) { Log.d(TAG, "camera: " + camera); Log.d(TAG, "previewBuilder: " + previewBuilder); } captureSession = session; extensionSession = eSession; previewBuilder.addTarget(surface_texture); Loading @@ -6350,6 +6443,7 @@ public class CameraController2 extends CameraController { } try { setRepeatingRequest(); success = true; } catch(CameraAccessException e) { if( MyDebug.LOG ) { Loading @@ -6359,7 +6453,8 @@ public class CameraController2 extends CameraController { } e.printStackTrace(); // we indicate that we failed to start the preview by setting captureSession back to null // this will cause a CameraControllerException to be thrown below // this will cause a CameraControllerException to be thrown below (if wait_until_started==true), // or via the on_failed callback (if wait_until_started==false) captureSession = null; extensionSession = null; } Loading @@ -6368,6 +6463,31 @@ public class CameraController2 extends CameraController { callback_done = true; background_camera_lock.notifyAll(); } if( success && runnable != null && !wait_until_started ) { // if not waiting, we run the runnable on UI thread now that preview is started final Activity activity = (Activity)context; activity.runOnUiThread(new Runnable() { @Override public void run() { if( MyDebug.LOG ) Log.d(TAG, "call runnable as preview now started"); synchronized( background_camera_lock ) { if( camera == null ) { if( MyDebug.LOG ) { Log.d(TAG, "but camera is closed in the meantime"); } // don't call onFailure() - if camera has closed in the meantime, no need to report to user (e.g. this might be going to Settings // whilst preview was starting) return; } } runnable.run(); } }); } else if( !success ) { onFailure(); } } @Override Loading @@ -6383,7 +6503,9 @@ public class CameraController2 extends CameraController { callback_done = true; background_camera_lock.notifyAll(); } // don't throw CameraControllerException here, as won't be caught - instead we throw CameraControllerException below onFailure(); // don't throw CameraControllerException here, as won't be caught - instead we throw CameraControllerException below (if wait_until_started==true), // or via the on_failed callback (if wait_until_started==false) } @Override Loading Loading @@ -6512,8 +6634,18 @@ public class CameraController2 extends CameraController { } } ); launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createExtensionSession(extensionConfiguration); } }, on_failed); } is_video_high_speed = false; } else if( video_recorder != null && want_video_high_speed && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) { Loading @@ -6523,13 +6655,33 @@ public class CameraController2 extends CameraController { if( ( cameraIdSPhysical != null || want_jpeg_r ) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ) { List<OutputConfiguration> outputs = createOutputConfigurationList(surfaces, preview_surface); SessionConfiguration sessionConfiguration = new SessionConfiguration(SessionConfiguration.SESSION_HIGH_SPEED, outputs, executor, myStateCallback); launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createCaptureSession(sessionConfiguration); } }, on_failed); } else { launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createConstrainedHighSpeedCaptureSession(surfaces, myStateCallback, handler); } }, on_failed); } is_video_high_speed = true; } else { Loading @@ -6542,13 +6694,35 @@ public class CameraController2 extends CameraController { myStateCallback, handler);*/ SessionConfiguration sessionConfiguration = new SessionConfiguration(SessionConfiguration.SESSION_REGULAR, outputs, executor, myStateCallback); launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createCaptureSession(sessionConfiguration); } }, on_failed); } else { launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { /*if( true ) throw new CameraAccessException(CameraAccessException.CAMERA_ERROR); // test*/ if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createCaptureSession(surfaces, myStateCallback, handler); } }, on_failed); } is_video_high_speed = false; } catch(NullPointerException e) { Loading @@ -6563,6 +6737,8 @@ public class CameraController2 extends CameraController { throw new CameraControllerException(); } } if( wait_until_started ) { if( MyDebug.LOG ) Log.d(TAG, "wait until session created..."); // n.b., we use the background_camera_lock lock instead of a separate lock, so that it's safe to call this Loading Loading @@ -6593,6 +6769,16 @@ public class CameraController2 extends CameraController { throw new CameraControllerException(); } } if( runnable != null ) { runnable.run(); } } else { if( MyDebug.LOG ) Log.d(TAG, "NOT waiting until session created"); // runnable is instead run from callback once preview is started } } catch(CameraAccessException e) { if( MyDebug.LOG ) { Loading @@ -6616,7 +6802,7 @@ public class CameraController2 extends CameraController { } @Override public void startPreview() throws CameraControllerException { public void startPreview(boolean wait_until_started, Runnable runnable, Runnable on_failed) throws CameraControllerException { if( MyDebug.LOG ) Log.d(TAG, "startPreview"); Loading Loading @@ -6648,10 +6834,13 @@ public class CameraController2 extends CameraController { // do via CameraControllerException instead of preview_error_cb, so caller immediately knows preview has failed throw new CameraControllerException(); } if( runnable != null ) { runnable.run(); } return; } } createCaptureSession(null, false); createCaptureSession(wait_until_started, runnable, on_failed, null, false); } @Override Loading Loading @@ -8446,7 +8635,7 @@ public class CameraController2 extends CameraController { previewIsVideoMode = true; previewBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); camera_settings.setupBuilder(previewBuilder, false); createCaptureSession(video_recorder, want_photo_video_recording); createCaptureSession(true, null, null, video_recorder, want_photo_video_recording); } catch(CameraAccessException e) { if( MyDebug.LOG ) { Loading @@ -8466,7 +8655,7 @@ public class CameraController2 extends CameraController { // if we change where we play the STOP_VIDEO_RECORDING sound, make sure it can't be heard in resultant video playSound(MediaActionSound.STOP_VIDEO_RECORDING); createPreviewRequest(); createCaptureSession(null, false); createCaptureSession(true, null, null, null, false); /*if( MyDebug.LOG ) Log.d(TAG, "add preview surface to previewBuilder"); Surface surface = getPreviewSurface(); Loading Loading
app/src/main/java/net/sourceforge/opencamera/MainActivity.java +10 −2 Original line number Diff line number Diff line Loading @@ -3461,6 +3461,11 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen if( !update_camera ) { // don't try to update camera } else if( preview.isPreviewStarting() ) { // ideally we should avoid getting into this state - we shouldn't allow changing settings if preview is still opening (e.g., we prevent opening // the popup menu if preview is not started, and we close camera when going to settings) Log.e(TAG, "updateForSettings: preview is still starting"); } else if( need_reopen || preview.getCameraController() == null ) { // if camera couldn't be opened before, might as well try again if( allow_dim ) applicationInterface.getDrawPreview().setDimPreview(true); Loading @@ -3486,7 +3491,10 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen // Even if allow_dim==false, still run as a postDelayed (a) for consistency, (b) to allow UI to run for a bit (to avoid risk of slow frames). handler.postDelayed(new Runnable() { public void run() { preview.setupCamera(false); // if we ever support calling this with wait_until_started==true, beware of trying to change resolution repeatedly when the popup menu // is open - as currently we'll have problems if trying to calling updateForSettings when preview is already starting on background // thread (see above) preview.setupCamera(false, true, null); } }, DrawPreview.dim_effect_time_c+16); // +16 to allow time for a frame update to run } Loading Loading @@ -3683,7 +3691,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen // starting the preview will disable the PausePreviewOnBackPressedCallback, so no need to do it here if( MyDebug.LOG ) Log.d(TAG, "preview was paused, so unpause it"); preview.startCameraPreview(); preview.startCameraPreview(true, null); } else { // shouldn't be here (if preview isn't paused, this callback shouldn't be enabled), but just in case Loading
app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +2 −2 Original line number Diff line number Diff line Loading @@ -3833,7 +3833,7 @@ public class MyApplicationInterface extends BasicApplicationInterface { } if( done ) { clearLastImages(); preview.startCameraPreview(); preview.startCameraPreview(true, null); } } } Loading Loading @@ -3907,7 +3907,7 @@ public class MyApplicationInterface extends BasicApplicationInterface { } clearLastImages(); drawPreview.clearGhostImage(); // doesn't make sense to show the last image as a ghost, if the user has trashed it! preview.startCameraPreview(); preview.startCameraPreview(true, null); } // Calling updateGalleryIcon() immediately has problem that it still returns the latest image that we've just deleted! // But works okay if we call after a delay. 100ms works fine on Nexus 7 and Galaxy Nexus, but set to 500 just to be safe. Loading
app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java +19 −1 Original line number Diff line number Diff line Loading @@ -660,7 +660,25 @@ public abstract class CameraController { /** Starts the camera preview. * @throws CameraControllerException if the camera preview fails to start. */ public abstract void startPreview() throws CameraControllerException; /** Starts the camera preview. * @param wait_until_started Whether to wait until the preview is started. Only relevant for * CameraController2; CameraController1 will always wait. * @param runnable If non-null, a runnable to be called once preview is started. If * wait_until_started==true, or using CameraController1, this will be * called on the current thread, before this method exits. Otherwise, * this will be called on the UI thread, after this method exits (once * the preview has started). * @param on_failed If non-null, a runnable to be called if the preview fails to start. * Only relevant for wait_until_started==false and when using * CameraController2. In such cases, failing to start the camera preview * may result in either CameraControllerException being thrown, or * on_failed being called on the UI thread after this method exits * (depending on when the failure occurs). If either of these happens, * the "runnable" runnable will not be called. * @throws CameraControllerException Failed to start preview. In this case, the runnable will not * be called. */ public abstract void startPreview(boolean wait_until_started, Runnable runnable, Runnable on_failed) throws CameraControllerException; /** Only relevant for CameraController2: stops the repeating burst for the previous (so effectively * stops the preview), but does not close the capture session for the preview (for that, using * stopPreview() instead of stopRepeating()). Loading
app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java +5 −2 Original line number Diff line number Diff line Loading @@ -1577,7 +1577,7 @@ public class CameraController1 extends CameraController { } @Override public void startPreview() throws CameraControllerException { public void startPreview(boolean wait_until_started, Runnable runnable, Runnable on_failed) throws CameraControllerException { if( MyDebug.LOG ) Log.d(TAG, "startPreview"); try { Loading @@ -1589,6 +1589,9 @@ public class CameraController1 extends CameraController { e.printStackTrace(); throw new CameraControllerException(); } if( runnable != null ) { runnable.run(); } } @Override Loading Loading @@ -1838,7 +1841,7 @@ public class CameraController1 extends CameraController { // need to start preview again: otherwise fail to take subsequent photos on Nexus 6 // and Nexus 7; on Galaxy Nexus we succeed, but exposure compensation has no effect try { startPreview(); startPreview(true, null, null); } catch(CameraControllerException e) { if( MyDebug.LOG ) Loading
app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +242 −53 Original line number Diff line number Diff line Loading @@ -2451,12 +2451,22 @@ public class CameraController2 extends CameraController { if( MyDebug.LOG ) Log.d(TAG, "release: " + this); closeCaptureSession(); CameraDevice camera_to_close = this.camera; synchronized( background_camera_lock ) { // set all to null straight away, as this can be called on background thread, but also // don't want to be in an incomplete state for other threads where camera is non-null but // previewBuilder is null previewBuilder = null; previewIsVideoMode = false; if( camera != null ) { camera.close(); camera = null; } if( camera_to_close != null ) { if( MyDebug.LOG ) Log.d(TAG, "close camera: " + camera_to_close); camera_to_close.close(); if( MyDebug.LOG ) Log.d(TAG, "close camera complete: " + camera_to_close); } closePictureImageReader(); /*if( previewImageReader != null ) { previewImageReader.close(); Loading @@ -2476,6 +2486,8 @@ public class CameraController2 extends CameraController { e.printStackTrace(); } } if( MyDebug.LOG ) Log.d(TAG, "release exit: " + this); } /** Enforce a minimum number of points in tonemap curves - needed due to Galaxy S10e having wrong behaviour if fewer Loading Loading @@ -6210,7 +6222,60 @@ public class CameraController2 extends CameraController { return outputs; } private void createCaptureSession(final MediaRecorder video_recorder, boolean want_photo_video_recording) throws CameraControllerException { private abstract static class CreateCaptureSessionFunction { public abstract void call() throws CameraAccessException; } /** Function to support calling a function either on background thread or not depending on wait_until_started. */ private void launchCameraSession(boolean wait_until_started, CreateCaptureSessionFunction function, Runnable on_failed) throws CameraAccessException { if( wait_until_started ) { /*try { Thread.sleep(6000); // test slow to start preview //Thread.sleep(25000); // test slow to start preview } catch(InterruptedException e) { throw new RuntimeException(e); }*/ function.call(); } else { handler.post(new Runnable() { @Override public void run() { try { /*try { Thread.sleep(6000); // test slow to start preview //Thread.sleep(25000); // test slow to start preview } catch(InterruptedException e) { throw new RuntimeException(e); }*/ function.call(); } catch(CameraAccessException e) { Log.e(TAG, "exception create extension session on background thread"); e.printStackTrace(); //myStateCallback.onConfigureFailed(); if( on_failed != null ) { // if waiting, failure will be indicated via CameraControllerException thrown from this method final Activity activity = (Activity)context; activity.runOnUiThread(new Runnable() { @Override public void run() { if( MyDebug.LOG ) Log.d(TAG, "call on_failed as preview failed to start"); on_failed.run(); } }); } } } }); } } private void createCaptureSession(boolean wait_until_started, Runnable runnable, Runnable on_failed, final MediaRecorder video_recorder, boolean want_photo_video_recording) throws CameraControllerException { if( MyDebug.LOG ) Log.d(TAG, "create capture session"); Loading Loading @@ -6327,18 +6392,46 @@ public class CameraController2 extends CameraController { class MyStateCallback extends CameraCaptureSession.StateCallback { private boolean callback_done; // must synchronize on this and notifyAll when setting to true private void onFailure() { if( on_failed != null && !wait_until_started ) { // if waiting, failure will be indicated on main thread below via CameraControllerException final Activity activity = (Activity)context; activity.runOnUiThread(new Runnable() { @Override public void run() { if( MyDebug.LOG ) Log.d(TAG, "call on_failed as preview failed to start"); on_failed.run(); } }); } } void onConfigured(@NonNull CameraCaptureSession session, @NonNull CameraExtensionSession eSession) { boolean success = false; // whether we successfully started the preview /*try { Thread.sleep(6000); // test slow to start preview //Thread.sleep(25000); // test slow to start preview } catch(InterruptedException e) { throw new RuntimeException(e); }*/ synchronized( background_camera_lock ) { if( camera == null ) { if( MyDebug.LOG ) { Log.d(TAG, "camera is closed"); } synchronized( background_camera_lock ) { callback_done = true; background_camera_lock.notifyAll(); } // don't call onFailure() - if camera has closed in the meantime, no need to report to user (e.g. this might be going to Settings // whilst preview was starting) return; } synchronized( background_camera_lock ) { if( MyDebug.LOG ) { Log.d(TAG, "camera: " + camera); Log.d(TAG, "previewBuilder: " + previewBuilder); } captureSession = session; extensionSession = eSession; previewBuilder.addTarget(surface_texture); Loading @@ -6350,6 +6443,7 @@ public class CameraController2 extends CameraController { } try { setRepeatingRequest(); success = true; } catch(CameraAccessException e) { if( MyDebug.LOG ) { Loading @@ -6359,7 +6453,8 @@ public class CameraController2 extends CameraController { } e.printStackTrace(); // we indicate that we failed to start the preview by setting captureSession back to null // this will cause a CameraControllerException to be thrown below // this will cause a CameraControllerException to be thrown below (if wait_until_started==true), // or via the on_failed callback (if wait_until_started==false) captureSession = null; extensionSession = null; } Loading @@ -6368,6 +6463,31 @@ public class CameraController2 extends CameraController { callback_done = true; background_camera_lock.notifyAll(); } if( success && runnable != null && !wait_until_started ) { // if not waiting, we run the runnable on UI thread now that preview is started final Activity activity = (Activity)context; activity.runOnUiThread(new Runnable() { @Override public void run() { if( MyDebug.LOG ) Log.d(TAG, "call runnable as preview now started"); synchronized( background_camera_lock ) { if( camera == null ) { if( MyDebug.LOG ) { Log.d(TAG, "but camera is closed in the meantime"); } // don't call onFailure() - if camera has closed in the meantime, no need to report to user (e.g. this might be going to Settings // whilst preview was starting) return; } } runnable.run(); } }); } else if( !success ) { onFailure(); } } @Override Loading @@ -6383,7 +6503,9 @@ public class CameraController2 extends CameraController { callback_done = true; background_camera_lock.notifyAll(); } // don't throw CameraControllerException here, as won't be caught - instead we throw CameraControllerException below onFailure(); // don't throw CameraControllerException here, as won't be caught - instead we throw CameraControllerException below (if wait_until_started==true), // or via the on_failed callback (if wait_until_started==false) } @Override Loading Loading @@ -6512,8 +6634,18 @@ public class CameraController2 extends CameraController { } } ); launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createExtensionSession(extensionConfiguration); } }, on_failed); } is_video_high_speed = false; } else if( video_recorder != null && want_video_high_speed && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) { Loading @@ -6523,13 +6655,33 @@ public class CameraController2 extends CameraController { if( ( cameraIdSPhysical != null || want_jpeg_r ) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ) { List<OutputConfiguration> outputs = createOutputConfigurationList(surfaces, preview_surface); SessionConfiguration sessionConfiguration = new SessionConfiguration(SessionConfiguration.SESSION_HIGH_SPEED, outputs, executor, myStateCallback); launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createCaptureSession(sessionConfiguration); } }, on_failed); } else { launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createConstrainedHighSpeedCaptureSession(surfaces, myStateCallback, handler); } }, on_failed); } is_video_high_speed = true; } else { Loading @@ -6542,13 +6694,35 @@ public class CameraController2 extends CameraController { myStateCallback, handler);*/ SessionConfiguration sessionConfiguration = new SessionConfiguration(SessionConfiguration.SESSION_REGULAR, outputs, executor, myStateCallback); launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createCaptureSession(sessionConfiguration); } }, on_failed); } else { launchCameraSession(wait_until_started, new CreateCaptureSessionFunction() { @Override public void call() throws CameraAccessException { /*if( true ) throw new CameraAccessException(CameraAccessException.CAMERA_ERROR); // test*/ if( camera == null ) { // just in case - don't throw exception as we don't want to show error toast, as it may be that another request to start preview is already active Log.e(TAG, "camera is no longer open"); return; } camera.createCaptureSession(surfaces, myStateCallback, handler); } }, on_failed); } is_video_high_speed = false; } catch(NullPointerException e) { Loading @@ -6563,6 +6737,8 @@ public class CameraController2 extends CameraController { throw new CameraControllerException(); } } if( wait_until_started ) { if( MyDebug.LOG ) Log.d(TAG, "wait until session created..."); // n.b., we use the background_camera_lock lock instead of a separate lock, so that it's safe to call this Loading Loading @@ -6593,6 +6769,16 @@ public class CameraController2 extends CameraController { throw new CameraControllerException(); } } if( runnable != null ) { runnable.run(); } } else { if( MyDebug.LOG ) Log.d(TAG, "NOT waiting until session created"); // runnable is instead run from callback once preview is started } } catch(CameraAccessException e) { if( MyDebug.LOG ) { Loading @@ -6616,7 +6802,7 @@ public class CameraController2 extends CameraController { } @Override public void startPreview() throws CameraControllerException { public void startPreview(boolean wait_until_started, Runnable runnable, Runnable on_failed) throws CameraControllerException { if( MyDebug.LOG ) Log.d(TAG, "startPreview"); Loading Loading @@ -6648,10 +6834,13 @@ public class CameraController2 extends CameraController { // do via CameraControllerException instead of preview_error_cb, so caller immediately knows preview has failed throw new CameraControllerException(); } if( runnable != null ) { runnable.run(); } return; } } createCaptureSession(null, false); createCaptureSession(wait_until_started, runnable, on_failed, null, false); } @Override Loading Loading @@ -8446,7 +8635,7 @@ public class CameraController2 extends CameraController { previewIsVideoMode = true; previewBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); camera_settings.setupBuilder(previewBuilder, false); createCaptureSession(video_recorder, want_photo_video_recording); createCaptureSession(true, null, null, video_recorder, want_photo_video_recording); } catch(CameraAccessException e) { if( MyDebug.LOG ) { Loading @@ -8466,7 +8655,7 @@ public class CameraController2 extends CameraController { // if we change where we play the STOP_VIDEO_RECORDING sound, make sure it can't be heard in resultant video playSound(MediaActionSound.STOP_VIDEO_RECORDING); createPreviewRequest(); createCaptureSession(null, false); createCaptureSession(true, null, null, null, false); /*if( MyDebug.LOG ) Log.d(TAG, "add preview surface to previewBuilder"); Surface surface = getPreviewSurface(); Loading