Switching cameras for video on the fly on android - android

I know it is possible as the camera app that comes with my droid phone does it, but for the life of me, I don't seem to be able to switch cameras on the fly either for video or a standard camera (which leads me to suspect I'm not doing it right!)
Currently, I have an event for the button
btnSwitchCamera.Click += new EventHandler(btnSwitchCamera_Click);
prior to that, I check for the number of cameras - if there is only one camera, the event is not enabled.
The switch code looks like this
private void btnSwitchCamera_Click(object s, EventArgs e)
{
if (isBackCamera == false)
{
try
{
RunOnUiThread(delegate
{
camera.Release();
camera = Android.Hardware.Camera.Open(1);
});
}
catch (Java.Lang.RuntimeException)
{
alertMsg(context, Application.Context.Resources.GetString(Resource.String.videoErrorTitle),
Application.Context.Resources.GetString(Resource.String.videoFailToConnect));
return;
}
isBackCamera = true;
}
else
{
try
{
RunOnUiThread(delegate
{
camera.Release();
camera = Android.Hardware.Camera.Open(0);
});
}
catch (Java.Lang.RuntimeException)
{
alertMsg(context, Application.Context.Resources.GetString(Resource.String.videoErrorTitle),
Application.Context.Resources.GetString(Resource.String.videoFailToConnect));
return;
}
isBackCamera = false;
}
}
If I click the button, the app dies claiming that I cannot connect to the service.
The video record code is nothing special - it's a bog standard set the surface, do the holder, and start/stop recording.
Am I doing this right? From the docs, I need to release the camera then open the camera with the appropriate camera number (Android.Hardware.Camera.NumberOfCameras - 1)
The manifest is correctly set.
Thanks
Paul

Related

How to automatically turn on flashlight for video recording when the scene is dark?

My app shows a preview and video recording starts with a button press.
What I'm trying to achieve is to automatically turn on flashlight (torch mode) as soon as the video recording starts.
However I couldn't find a way to do so. On Camera2 API we can use FLASH_MODE_AUTO which will use the flashlight when capturing photo when the scene is dark, but that doesn't work for video recording.
There's this FLASH_MODE_TORCH which I could use to turn on the flashlight just like I wanted, but there isn't a FLASH_MODE_TORCH_AUTO to automatically do so when the scene is dark..
There were some answers that uses Ambient light sensor (Sensor.TYPE_LIGHT) of the device to determine whether we are in a dark scene, however that uses the front ambient light sensor instead of the camera itself I think. This is not ideal as the ambient light can be low but the rear camera is able to adjust exposure level to achieve good enough image quality without using flash. So ideally if the camera says 'flash is required' then only the app activates FLASH_MODE_TORCH.
Since the app shows a preview the device already know whether flash is needed before the button press, is there a way to determine whether flash is required during preview?
Please try the below method where you need you can use it
below is for Camera API
public void switchFlashOnMode() {
Camera.Parameters p = getCamera().getParameters();
try {
//p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
p.setFlashMode(Parameters.FLASH_MODE_AUTO);
getCamera().setParameters(p);
getCamera().startPreview();
isFlashTorch = true;
}catch (Exception e){
e.printStackTrace();
}
}
public void switchFlashOffMode() {
Camera.Parameters p = getCamera().getParameters();
try {
p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
getCamera().setParameters(p);
Thread.sleep(200);
getCamera().stopPreview();
isFlashTorch = false;
}catch (Exception e){
e.printStackTrace();
}
}
below is for Camera2 API
void switchFlashMode() {
if (!flashSupport) return;
try {
if (isFlashTorch) {
isFlashTorch = false;
requestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
} else {
isFlashTorch = true;
//requestBuilder.set(CaptureRequest.FLASH_MODE,CameraMetadata.FLASH_MODE_TORCH);
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
}
cameraCaptureSession.setRepeatingRequest(requestBuilder.build(), null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
hope it will help you
Finally figured this out, gpuser's answer used the right flag but it is not complete - still need to code the callback and turn on the torchlight when needed.
I also found that for video recording, we still use the same Camera2 API init and configuration steps, just that some of the callbacks will be fired multiple times, so I added a flag to perform the flash detection only once.
1)After camera started capturing, run this code
performAutoTorchDetectionOnce = true; // set this flag first, will be used later
captureRequestBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // CONTROL_AE_MODE_ON_AUTO_FLASH is important here, to enable flash detection
captureSession.setRepeatingRequest(captureRequestBuilder.build(), captureCallback, null);
2)And this is my captureCallback implementation, change it depending on your needs. The gist of it is that eventually the camera capture will fall into one of the two states, CONTROL_AE_STATE_CONVERGED or CONTROL_AE_STATE_FLASH_REQUIRED. These two states mean that auto exposure algorithm has finished running, if it is converged means no flash is needed whereas flash_required will mean that we have to turn on flash. In the latter we will then need to manually turn on the flash in the next step.
private CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
long timestamp, long frameNumber) {
super.onCaptureStarted(session, request, timestamp, frameNumber);
}
#Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState != null) {
if (performAutoTorchDetectionOnce) {
if (aeState == CameraMetadata.CONTROL_AE_STATE_CONVERGED // CONTROL_AE_STATE_CONVERGED means Auto-exposure has finished
|| aeState == CameraMetadata.CONTROL_AE_STATE_FLASH_REQUIRED) { // CONTROL_AE_STATE_FLASH_REQUIRED means Auto-exposure has finished, but flash is required
performAutoTorchDetectionOnce = false;
enableTorch(aeState == CameraMetadata.CONTROL_AE_STATE_FLASH_REQUIRED);
}
}
}
}
};
3)Here's the enableTorch implementation. I tried leaving CONTROL_AE_MODE as CONTROL_AE_MODE_ON_AUTO_FLASH but it didn't work, torch light does not turn on, so I have to change it to CONTROL_AE_MODE_ON.
public synchronized void enableTorch(boolean enable) {
Timber.d("enableTorch(" + enable + ") called");
try {
if (isCaptureStarted()) {
if (enable) {
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
} else {
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
}
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
captureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
}
} catch (CameraAccessException e) {
Timber.e(e, "enableTorch(" + enable + ") failed: ");
}
}

How to restart camera preview on Xamarin forms when using a custom renderer view for a camera preview

I am trying to resume my Camera preview with android after putting the app to sleep or changing between apps. Or even starting a different app which uses the camera feature but the Camera crashed with getParameters() being null.
Is there a way to retrieve the control other the camera preview when resuming using the Xamarin forms application.
I have tried to use Camera.Restart() and didn't work.
public void SurfaceCreated(ISurfaceHolder holder)
{
try
{
if (Preview != null)
{
Preview.StopPreview();
Preview.Reconnect();
Preview.SetPreviewDisplay(holder);
Preview.EnableShutterSound(true);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(#" ERROR: ", ex.Message);
}
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
Preview.StopPreview();
Preview.Release();
}
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
{
Camera.Parameters parameters = Preview.GetParameters();
parameters.FocusMode = Camera.Parameters.FocusModeContinuousPicture;
IList<Camera.Size> previewSizes = parameters.SupportedPreviewSizes;
// You need to choose the most appropriate previewSize for your app
Camera.Size previewSize = previewSizes[0];
parameters.SetPreviewSize(previewSize.Width, previewSize.Height);
Preview.SetParameters(parameters);
Preview.StartPreview();
}
I was able to get it to work by reading more thoroughly about the behaviour of the Android camera(Camera1) hardware.
If you are working on Xamarin and trying to create camera view within the app the best way to do it is making a custom renderer and create a camera view in each platform. Like shown here:
https://learn.microsoft.com/en-ca/xamarin/xamarin-forms/app-fundamentals/custom-renderer/view
but this example only shows how to create the camera preview, there is no camera hardware lifecycle or taking a pictures included.
To solve the issue for the question above I had simply to do Camera.Open(0) to gain control over the camera again within the lifecycle of Xamarin forms pages.
Here is what I did(in CameraPreview class in Xamarin Forms):
Created Camera open event handler:
public event EventHandler CloseCameraRequest;
Created a method to invoke the event:
public void OpenCamera()
{
OpenCameraRequest?.Invoke(this, EventArgs.Empty);
}
Registered the handler in the android camera native class:
protected override void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)
{
base.OnElementChanged(e);
if (Control == null)
{
_nativeCameraPreview = new NativeCameraPreview(Context);
_nativeCameraPreview.PhotoCaptured += OnPhotoCaptured;
SetNativeControl(_nativeCameraPreview);
}
Control.Preview = Camera.Open(0);
if (e.OldElement != null)
{
e.NewElement.OpenCameraRequest -= OnOpenCameraRequest;
}
if (e.NewElement != null)
{
e.NewElement.OpenCameraRequest += OnOpenCameraRequest;
}
}
private void OnOpenCameraRequest(object sender, EventArgs e)
{
Control.Preview = Camera.Open(0);
}
Invoked the request all the way from Xamarin forms page OnAppearing method:
protected override void OnAppearing()
{
base.OnAppearing();
CameraPreview.OpenCamera();
}
This fixed the issue of resuming camera preview after opening other application which uses the camera or putting the app to sleep where camera preview will timeout.

Camera Error "Can't Connect to the Camera" or in some phones the error appear "Camera is using by another app"

I have implemented to record audio in background using Android MediaRecorder if the audio recording is in progress and user open's the native camera to record video, it gives
Camera Error "Can't Connect to the Camera"
or on some phones, the error appears as
Your camera is in use by another application
If I stop mediarecorder then the native camera video recording works fine, I searched for events to know when Camera is going to start video so then in my app I stop the mediarecorder, I found the BroadcastReceiver with filters
<receiver android:name=".receiver.CameraReceiver">
<intent-filter android:priority="10000">
<action android:name="android.Medintent.action.CAMERA_BUTTON" />
<action android:name="android.hardware.action.NEW_PICTURE" />
<action android:name="android.hardware.action.NEW_VIDEO" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
</receiver>
NEW_VIDEO and NEW_PICTURE events fired when picture or video is captured and saved in the directory. Anyone knows how can this issue be solved? I want to know in my app the event when Native/Camera Apps going to record video. Thanks in advance
Even i had the same issue.
Once camera resource is being used by an application,untill it is released, you can use them in some other app or even a service.
If any service is using the camera resource, untill it releases the same we cannot use camera hardware.
You can doublecheck if camera hardware is being used using this code :-
private boolean isCameraInUse() {
Log.v(TAG, "isCameraInUse()");
boolean isCameraInUse = false;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)//Use Camera Api for Version Code < 23 and mCamera manager above it.
{
String cameraId = null;
CameraManager camManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
// Usually front mCamera is at 0 position.
try {
cameraId = camManager.getCameraIdList()[0];
} catch (CameraAccessException e) {
Log.e(TAG, Log.getStackTraceString(e));
isCameraInUse = true;
}
try {
camManager.setTorchMode(cameraId, true);
camManager.setTorchMode(cameraId, false);
} catch (CameraAccessException e) {
Log.e(TAG, Log.getStackTraceString(e));
isCameraInUse = true;
}
} else {
Camera c = null;
try {
c = Camera.open();
} catch (RuntimeException e) {
Log.e(TAG, Log.getStackTraceString(e));
turnFlashOff(mContext);
isCameraInUse = true;
} finally {
if (c != null) c.release();
}
}
return isCameraInUse;
}
CameraManager.AvailabilityCallback provides onCameraAvailable(String cameraId) method to detect whether a camera is available or not.
https://developer.android.com/reference/android/hardware/camera2/CameraManager.AvailabilityCallback.html
Getting all camera IDs is the same as the above as shown by #GAGAN.
CameraManager camManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
// Usually front mCamera is at position 0.
try {
cameraId = camManager.getCameraIdList()[0];
} catch (CameraAccessException e) {
Log.e(TAG, Log.getStackTraceString(e));
Log.cat("Error: ", e.getReason() + "");
if (e.CAMERA_DISABLED) { /* ENABLE CAMERA */ }
else if ( /* ... you can go to the link below to do various logic */ ) {
//...
}
else { /* do nothing */ }
}
https://developer.android.com/reference/android/hardware/camera2/CameraAccessException.html#CAMERA_IN_USE
Note: Until other applications use your camera hardware, they are using purposefully used by them. So until these apps don't free your hardware, you can't use that, it's clear. You can't know whether these apps actually need the camera. We believe process reserves hardware when it needs.
But, we can setup the listener on when camera becomes available (free) so that you can use it immediately.
CameraManager.AvailabilityCallback availabilityCallback = new CameraManager.AvailabilityCallback() {
#Override
public void onCameraAvailable(String cameraId) {
// your further stuffs. You must put all of your camera related logic here.
}
public void onCameraUnavailable(String cameraId) {
//you can logcat camera not available
}
};
The use of CameraManager.AvailabilityCallback abstract class is when you instantiate object, you do anonymous instantiation implementing callbacks like onCameraAvailable(), which is actually a listener that you are getting from the Camera Observer.
Answer: If you would have put your camera processing commands inside onCameraAvailable() callback, I guarantee, you wouldn't have got the error that you showed.
If the camera is used by other apps in the background, it means, those other apps are kind of buggy, because the camera needs foreground preview; they are not releasing the camera even after they are done with it. Nobody uses camera in background. Even in this case also, you are not supposed to kill camera process forcefully, because memory leaks can happen. Because you don't know how other processes are using the camera.

Surfaceview using Mobile Vision Displays the Camera in Landscape

I am using SurfaceView and Google's Mobile Vision library. For many devices it looks fine but when using with few devices like Nexus 7 the camera view comes in Landscape mode. Which makes it difficult for Scanning barcodes etc as it is difficult to focus and position correctly.
In Vision library as I have explored there is no method such that they return the hardware camera so we can manage the orientation like if the camera view returns landscape then we can dynamically rotate the view to make it look like portrait.
So wanted to ask if there is any way for Devices like Nexus 7 to change the Camera or View to Portrait.
Any help will be welcomed! Thanks
Many tabs has their camera mounted rotated, so that when held horizontally, the picture will be taken as "portrait", even though the image is actually wider than it is high.
I learned it the hard way, on an app i built some time ago. The only way was to check the screen-aspect vs the image-aspect and image rotation.
By comparing these, you can infer whether a camera image is rotated correctly, or whether it needs a 90 degree post-rotation.
I found a solution for myself getting an idea from this persons answer:
https://stackoverflow.com/a/41634379/5028531
So what I did:
cameraPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
try {
cameraSource.start(cameraPreview.getHolder());
Field[] declaredFields = CameraSource.class.getDeclaredFields();
for (Field field : declaredFields) {
if (field.getType() == Camera.class) {
field.setAccessible(true);
try {
Camera camera = (Camera) field.get(cameraSource);
if (camera != null) {
Camera.Parameters params= camera.getParameters();
camera.setDisplayOrientation(0);
}
} catch (IllegalAccessException | RuntimeException e) {
e.getMessage();
}
break;
}
}
} catch (IOException e) {
Log.e("CAMERA SOURCE", e.getMessage());
e.printStackTrace();
}
} else {
Log.w("CAMERA SOURCE", "Permission not granted");
Toast.makeText(getActivity(), "Camera permission denied", Toast.LENGTH_SHORT).show();
}
}

How to set the camera light on torch in Tokbox?

I am using TokBox for an android project. I need to add a button which would turn the flash light on torch mode.
Tokbox Publisher object already provides a swapCamera() method which switches between all the available cameras of the device. But I couldn't find any API to change the flash light mode for the currently selected camera.
I tried getting an instance of android.hardware.Camera myself to change it manually, but it didn't work because I got the "java.lang.RuntimeException: Fail to connect to camera service" exception. It is because the Camera object is being used by Tokbox and is not released.
I could find no way to access the Camera instance that Tokbox is using either. It is even deprecated since android API level 21.
Can anyone suggest a way to change the camera parameters? I have access to the View that the video is being previewed on it.
I needed to stop the stream to be able to start the camera app to take a picture. I've found code to release the camera and attach it. Maybe you can use this code to release the camera, turn on the light and then attach the camera again
The following code releases the camera:
public void ReleaseCamera()
{
if (_myPub != null) {
_myPub.PublishVideo = false;
BaseVideoCapturer bvc = _myPub.Capturer;
if (bvc != null) {
bvc.StopCapture ();
bvc.Destroy ();
}
}
}
And this code attaches the camera again:
public void AttachCamera()
{
if (_myPub != null) {
BaseVideoCapturer bvc = _myPub.Capturer;
if (bvc != null) {
if (bvc.IsCaptureStarted == false) {
bvc.Init ();
bvc.StartCapture ();
_myPub.PublishVideo = true;
}
}
}
}
torch light will work only with back camera, so if you are publishing video with front camera then it will freez tokbox video.
if(publisher.cameraPosition == .back){
if let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo), device.hasTorch {
do {
try device.lockForConfiguration()
let torchOn = !device.isTorchActive
try device.setTorchModeOnWithLevel(1.0)
device.torchMode = torchOn ? .on : .off
device.unlockForConfiguration()
} catch {
print("error")
}
}
}

Categories

Resources