Skip to content

CameraView fix for Android: video recording and rotation#3168

Open
zhitaop wants to merge 6 commits intoCommunityToolkit:mainfrom
zhitaop:fix/camera-videorecord-and-rotation-android
Open

CameraView fix for Android: video recording and rotation#3168
zhitaop wants to merge 6 commits intoCommunityToolkit:mainfrom
zhitaop:fix/camera-videorecord-and-rotation-android

Conversation

@zhitaop
Copy link
Copy Markdown
Contributor

@zhitaop zhitaop commented Mar 20, 2026

Description of Change

  • This PR fixes a bug on Android where attempting to record a video for a second time will fail: CleanupVideoRecordingResources() no longer disposes videoRecorder and videoCapture after each recording session. This prevents the "Null Stream" error caused by trying to record with a disposed object. It also avoids the heavy overhead of rebinding the camera lifecycle every time a new video recording is started.

  • In PlatformStartVideoRecording(), if videoCapture or videoRecorder is null (e.g. due to a resolution change), it calls StartUseCase() to rebuild them.

  • Improves orientation handling by applying rotation consistently to imageCapture, videoCapture, and cameraPreview .

Linked Issues

PR Checklist

  • Has a linked Issue, and the Issue has been approved(bug) or Championed (feature/proposal)
  • Has tests (if omitted, state reason in description)
  • Has samples (if omitted, state reason in description)
  • Rebased on top of main at time of PR
  • Changes adhere to coding standard
  • Documentation created or updated: https://github.com/MicrosoftDocs/CommunityToolkit/pulls

Additional information

Copilot AI review requested due to automatic review settings March 20, 2026 09:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Android CameraManager implementation to improve repeated video recording reliability and to apply device rotation consistently across CameraX use cases (preview, image capture, and video capture).

Changes:

  • Keeps VideoCapture/Recorder alive across recording sessions by stopping disposal in CleanupVideoRecordingResources().
  • Rebuilds video recording use cases on-demand in PlatformStartVideoRecording() when they are missing (e.g., after a resolution change).
  • Centralizes orientation-to-surface-rotation mapping and applies it to imageCapture, videoCapture, and cameraPreview.

Comment on lines +434 to +436
imageCapture?.TargetRotation = targetRotation;
videoCapture?.TargetRotation = targetRotation;
cameraPreview?.TargetRotation = targetRotation;
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnOrientationChanged sets TargetRotation on imageCapture/videoCapture/cameraPreview, but StartUseCase disposes these use cases without first setting the fields to null. If an orientation event fires between Dispose() and the subsequent re-creation, this can end up calling TargetRotation on a disposed Java object and throw. Consider setting the fields to null immediately after disposing in StartUseCase (or temporarily disabling the OrientationListener during rebuild) so the callback never observes disposed instances.

Suggested change
imageCapture?.TargetRotation = targetRotation;
videoCapture?.TargetRotation = targetRotation;
cameraPreview?.TargetRotation = targetRotation;
try
{
if (imageCapture is not null && imageCapture.Handle != IntPtr.Zero)
{
imageCapture.TargetRotation = targetRotation;
}
if (videoCapture is not null && videoCapture.Handle != IntPtr.Zero)
{
videoCapture.TargetRotation = targetRotation;
}
if (cameraPreview is not null && cameraPreview.Handle != IntPtr.Zero)
{
cameraPreview.TargetRotation = targetRotation;
}
}
catch (ObjectDisposedException)
{
// Ignore orientation updates for disposed camera use cases.
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

{
if (videoCapture is null || videoRecorder is null)
{
await StartUseCase(token);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PlatformStartVideoRecording can call StartUseCase(token) to recreate imageCapture/videoCapture/cameraPreview. Because target rotation is only applied from OnOrientationChanged, newly recreated use cases will keep the default TargetRotation until the device orientation changes again (which may never happen), leading to incorrect rotation after a rebuild (e.g., after a resolution change). Consider applying an initial TargetRotation immediately after rebuilding (e.g., from previewView.Display?.Rotation / current display rotation, or cache the last computed target rotation and reapply it).

Suggested change
await StartUseCase(token);
await StartUseCase(token);
if (previewView is not null)
{
var display = previewView.Display;
if (display is not null)
{
var rotation = (int)display.Rotation;
if (imageCapture is not null)
{
imageCapture.TargetRotation = rotation;
}
if (videoCapture is not null)
{
videoCapture.TargetRotation = rotation;
}
if (cameraPreview is not null)
{
cameraPreview.TargetRotation = rotation;
}
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +430 to +433
void OnOrientationChanged(int rotation)
{
if (imageCapture is not null)
{
imageCapture.TargetRotation = rotation switch
{
>= 45 and < 135 => (int)SurfaceOrientation.Rotation270,
>= 135 and < 225 => (int)SurfaceOrientation.Rotation180,
>= 225 and < 315 => (int)SurfaceOrientation.Rotation90,
_ => (int)SurfaceOrientation.Rotation0
};
}
var targetRotation = GetSurfaceRotation(rotation);

Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OnOrientationChanged(int rotation) parameter is receiving raw orientation degrees from OrientationEventListener, not a surface rotation value. Renaming the parameter (e.g., to orientationDegrees) would avoid confusion with the computed targetRotation (surface rotation) used for TargetRotation assignments.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] CameraView Video Recording issues on iOS and Android [BUG] CameraView preview doesn't rotate on certain Android devices

3 participants