I have absolutely no idea how to implement this. It works fine on my Razr, but fails on a lot of Samsung devices. I have a SurfaceView running a camera preview. I've included a button to use the flashlight. The exception is thrown where I inserted the try block:
if (hasFlash()) {
btnLight = (ImageButton) findViewById(R.id.btn_light);
btnLight.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Parameters p = camera.getParameters();
if (p.getSupportedFlashModes() != null) {
if (p.getSupportedFlashModes().contains(
Parameters.FLASH_MODE_TORCH)) {
if (isLightOn) {
p.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(p);
btnLight.setImageResource(R.drawable.light_on);
isLightOn = false;
} else {
try {
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(p);
btnLight.setImageResource(R.drawable.light_off);
isLightOn = true;
} catch (RuntimeException e){
// I don't know how to make this stupid thing work for all phones
}
}
}
}
}
});
In Android documentation you may read:
public void setParameters (Camera.Parameters params)
Throws
RuntimeException if any parameter is invalid or not supported.
It means that you're using a camera parameter (FLASH_MODE) that is not supported by your Samsung device and therefore it won't work on this model. I assume that it probably just doesn't have flash light built-in at all.
Related
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: ");
}
}
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();
}
}
The question is how to configure android camera focus to achieve both on demand and auto focusing. Different devices behaves differently. Samsung S3 and Note2 works well with autofocusCallback and FOCUS_MODE_CONTINUOUS_VIDEO and FOCUS_MODE_AUTO.Samsung S4 do not work with FOCUS_MODE_AUTO. LG G3 do not work with autofocusCallback properly in this phone callback returns only if image had been changed during focus.Samsung S5 do not call autofocusCallback if FOCUS_MODE_CONTINUOUS_VIDEO been choosed. There is a request focus method called after preview where established.
public synchronized void RequestFocus()
{
if(false== mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS))
return;
if(mCamera!=null)
{
try
{
if(isFocusing)
return;
mCamera.cancelAutoFocus();
isFocusing=true;
mCamera.autoFocus(mAutoFocusCallback);
/*
Thread focusThread = new Thread(){
#Override
public synchronized void run() {
try
{
if(isFocusing)
return;
Thread.sleep(40);
if(isFocusing)
return;
isFocusing=true;
}
catch (InterruptedException e)
{
isFocusing=false;
}
catch(Exception ex)
{
isFocusing=false;
}
}};
focusThread.start();*/
}
catch(Exception ex)
{
Log.d("RequestFocus",ex.getMessage());
isFocusing=false;
}
}
}
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
I wanted to make use of the zxing library to detect qrcodes in my app. But for the apps viewing purpose, i had to change the custom display orientation to portrait. Hence i had to integrate the whole zxing library into my app and addded camera.setDisplayOrientation(90) to the openDriver() method.
After doing this, the program works, but I get "Runtime exceptions : Fail to connect to camera service" randomly.
public void openDriver(SurfaceHolder holder) throws IOException {
if (camera == null) {
camera = Camera.open();
camera.setDisplayOrientation(90);
if (camera == null) {
throw new IOException();
}
}
camera.setPreviewDisplay(holder);
if (!initialized) {
initialized = true;
configManager.initFromCameraParameters(camera);
}
configManager.setDesiredCameraParameters(camera);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
reverseImage = prefs.getBoolean(PreferencesActivity.KEY_REVERSE_IMAGE, false);
if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
FlashlightManager.enableFlashlight();
}
}
public void closeDriver() {
if (camera != null) {
FlashlightManager.disableFlashlight();
camera.release();
camera = null;
framingRect = null;
framingRectInPreview = null;
}
}
/**
* Asks the camera hardware to begin drawing preview frames to the screen.
*/
public void startPreview() {
if (camera != null && !previewing) {
camera.startPreview();
previewing = true;
}
}
/**
* Tells the camera to stop drawing preview frames.
*/
public void stopPreview() {
if (camera != null && previewing) {
if (!useOneShotPreviewCallback) {
camera.setPreviewCallback(null);
}
camera.stopPreview();
previewCallback.setHandler(null, 0);
autoFocusCallback.setHandler(null, 0);
previewing = false;
}
}
I doubt that the orientation change is causing that. I have found you will get that error whenever an activity stops but fails to call Camera.release in their onPause. The result is that the next time you try to do Camera.open you get that runtime error since the driver still considers it open regardless of the app/activity that opened it being gone.
You can easily get this to happen while debugging/testing stuff when something throws an exception and brings the activity down. You need to be very diligent about catching all exceptions and being sure to release the camera before finishing the activity.
BTW, are you finding you need to power cycle the device in order to be able to open the camera again?