I am using Barcode Scanner in my app. In some mobile it is giving the run-time exception Fail to connect to camera.
Here is the code I am using for Camera
/**
* Opens the camera and applies the user settings.
*
* #throws RuntimeException if the method fails
*/
#SuppressLint("InlinedApi")
private Camera createCamera() {
int requestedCameraId = getIdForRequestedCamera(mFacing);
if (requestedCameraId == -1) {
throw new RuntimeException("Could not find requested camera.");
}
Camera camera = Camera.open(requestedCameraId);
SizePair sizePair = selectSizePair(camera, mRequestedPreviewWidth, mRequestedPreviewHeight);
if (sizePair == null) {
throw new RuntimeException("Could not find suitable preview size.");
}
Size pictureSize = sizePair.pictureSize();
mPreviewSize = sizePair.previewSize();
int[] previewFpsRange = selectPreviewFpsRange(camera, mRequestedFps);
if (previewFpsRange == null) {
throw new RuntimeException("Could not find suitable preview frames per second range.");
}
Camera.Parameters parameters = camera.getParameters();
if (pictureSize != null) {
parameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
}
parameters.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
parameters.setPreviewFpsRange(
previewFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
parameters.setPreviewFormat(ImageFormat.NV21);
setRotation(camera, parameters, requestedCameraId);
if (mFocusMode != null) {
if (parameters.getSupportedFocusModes().contains(
mFocusMode)) {
parameters.setFocusMode(mFocusMode);
} else {
Log.i(TAG, "Camera focus mode: " + mFocusMode + " is not supported on this device.");
}
}
// setting mFocusMode to the one set in the params
mFocusMode = parameters.getFocusMode();
if (mFlashMode != null) {
if (parameters.getSupportedFlashModes() != null) {
if (parameters.getSupportedFlashModes().contains(
mFlashMode)) {
parameters.setFlashMode(mFlashMode);
} else {
Log.i(TAG, "Camera flash mode: " + mFlashMode + " is not supported on this device.");
}
}
}
// setting mFlashMode to the one set in the params
mFlashMode = parameters.getFlashMode();
camera.setParameters(parameters);
// Four frame buffers are needed for working with the camera:
//
// one for the frame that is currently being executed upon in doing detection
// one for the next pending frame to process immediately upon completing detection
// two for the frames that the camera uses to populate future preview images
camera.setPreviewCallbackWithBuffer(new CameraPreviewCallback());
camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));
camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));
camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));
camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));
return camera;
}
* Stops the camera.
*/
#Override
protected void onPause() {
super.onPause();
if (mPreview != null) {
mPreview.stop();
}
}
/**
* Releases the resources associated with the camera source, the associated detectors, and the
* rest of the processing pipeline.
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (mPreview != null) {
mPreview.release();
}
}
/**
* Restarts the camera.
*/
#Override
protected void onResume() {
super.onResume();
startCameraSource();
}
The run time exception I am getting is
java.lang.RuntimeException: Fail to connect to camera service
at android.hardware.Camera.<init>(Camera.java:532)
at android.hardware.Camera.open(Camera.java:360)
Any help Regarding this issue because in some devices it is working but in some it is not working I am not able to get the issue.
Always check if the camera is available or not before accesing it.
private Camera mCamera;
/**
* Opens the camera and applies the user settings.
*
* #throws RuntimeException if the method fails
*/
#SuppressLint("InlinedApi")
private Camera createCamera() {
int requestedCameraId = getIdForRequestedCamera(mFacing);
if (requestedCameraId == -1) {
throw new RuntimeException("Could not find requested camera.");
}
if (mCamera != null){
mCamera.release();
mCamera = null;
}
mCamera= Camera.open(requestedCameraId);
if(camera == null){
Toast.makeText(mContext, "Camera service is not available currently.", Toast.LENGTH_LONG.show())
}
//....
}
}
And relsease the camera before onDestroy().
/**
* Releases the resources associated with the camera source, the associated detectors, and the
* rest of the processing pipeline.
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (mPreview != null) {
mPreview.release();
mCamera.release();
mCamera = null;
}
Related
I am developing an app for my project and I don't want my main camera to have autofocus. I want my camera to have a specific focus value that can focus on objects that are about 5-10 cm from the main camera.
Here is the code on how the CameraManager is implemented:
private String chooseCamera() {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (final String cameraId : manager.getCameraIdList()) {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// Fallback to camera1 API for internal cameras that don't have full support.
// This should help with legacy situations where using the camera2 API causes
// distorted or otherwise broken previews.
useCamera2API =
(facing == CameraCharacteristics.LENS_FACING_FRONT)
|| isHardwareLevelSupported(
characteristics);
LOGGER.i("Camera API lv2?: %s", useCamera2API);
// manager.setTorchMode(cameraId, true);
return cameraId;
}
} catch (CameraAccessException e) {
LOGGER.e(e, "Not allowed to access camera");
}
return null;
}
protected void setFragment() {
String cameraId = chooseCamera();
Fragment fragment;
if (useCamera2API) {
CameraConnectionFragment camera2Fragment =
CameraConnectionFragment.newInstance(
(size, rotation) -> {
previewHeight = size.getHeight();
previewWidth = size.getWidth();
CameraActivity.this.onPreviewSizeChosen(size, rotation);
},
this,
getLayoutId(),
getDesiredPreviewFrameSize());
camera2Fragment.setCamera(cameraId);
fragment = camera2Fragment;
} else {
fragment =
new LegacyCameraConnectionFragment(this, getLayoutId(), getDesiredPreviewFrameSize());
}
getFragmentManager().beginTransaction().replace(R.id.container, fragment).commit();
}
You need to first turn off autofocus by using CaptureRequest.CONTROL_AF_MODE and then set desired focus distance value using this request key, CaptureRequest.LENS_FOCUS_DISTANCE.
To know if a device supports manual sensor or not, query characteristics and check for support.
https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
Hey guys I have a problem with Google mobile vision API and especially with data-matrix format .
I'm able to scan almost every Barcode format but when I want to scan data-matrix, things getting harder . I can only detect one in 10, it's very embarrasing.
I followed the doc and I added followed detection formats like follow :
1D barcodes: EAN-13, EAN-8, UPC-A, UPC-E, Code-39, Code-93, Code-128, ITF, Codabar
2D barcodes: QR Code, Data Matrix, PDF-417, AZTEC
My Activity looks like this (took on : Google vision Android Github)
public class QrScanActivity extends AppCompatActivity implements BarcodeTracker.BarcodeTrackerListener {
private static final String TAG = QrScanActivity.class.getSimpleName();
// permission request codes need to be < 256
private static final int RC_HANDLE_CAMERA_PERM = 2;
public static final String BARCODE_OBJECT = "Barcode";
private CameraSource _cameraSource;
private CameraSourcePreview _preview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qrscan);
_preview = this.findViewById(R.id.preview);
// Check for the camera permission before accessing the camera. If the
// permission is not granted yet, request permission.
int rc = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (rc == PackageManager.PERMISSION_GRANTED) {
createCameraSource(true);
} else {
requestCameraPermission();
}
}
/**
* Handles the requesting of the camera permission. This includes
* showing a "Snackbar" message of why the permission is needed then
* sending the request.
*/
private void requestCameraPermission() {
Log.w(TAG, "Camera permission is not granted. Requesting permission");
final String[] permissions = new String[]{Manifest.permission.CAMERA};
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_CAMERA_PERM);
return;
}
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View view) {
ActivityCompat.requestPermissions(QrScanActivity.this, permissions,
RC_HANDLE_CAMERA_PERM);
}
};
}
/**
* Creates and starts the camera. Note that this uses a higher resolution in comparison
* to other detection examples to enable the barcode detector to detect small barcodes
* at long distances.
* <p>
* Suppressing InlinedApi since there is a check that the minimum version is met before using
* the constant.
*/
#SuppressLint("InlinedApi")
private void createCameraSource(boolean autoFocus) {
Context context = getApplicationContext();
// A barcode detector is created to track barcodes. An associated multi-processor instance
// is set to receive the barcode detection results, track the barcodes, and maintain
// graphics for each barcode on screen. The factory is used by the multi-processor to
// create a separate tracker instance for each barcode.
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context).setBarcodeFormats(Barcode.QR_CODE | Barcode.DATA_MATRIX | Barcode.AZTEC | Barcode.PDF417 | Barcode.CODE_39 |
Barcode.EAN_8 | Barcode.EAN_13 | Barcode.UPC_A | Barcode.UPC_E | Barcode.CODE_93 | Barcode.CODE_128 | Barcode.ITF | Barcode.CODABAR).build();
BarcodeTracker tracker = new BarcodeTracker(this);
barcodeDetector.setProcessor(
new FirstItemProcessor(barcodeDetector, tracker));
if (!barcodeDetector.isOperational()) {
// Note: The first time that an app using the barcode or face API is installed on a
// device, GMS will download a native libraries to the device in order to do detection.
// Usually this completes before the app is run for the first time. But if that
// download has not yet completed, then the above call will not detect any barcodes
// and/or faces.
//
// isOperational() can be used to check if the required native libraries are currently
// available. The detectors will automatically become operational once the library
// downloads complete on device.
Log.w(TAG, "Detector dependencies are not yet available.");
// Check for low storage. If there is low storage, the native library will not be
// downloaded, so detection will not become operational.
IntentFilter lowstorageFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
boolean hasLowStorage = registerReceiver(null, lowstorageFilter) != null;
if (hasLowStorage) {
Toast.makeText(this, "low storage", Toast.LENGTH_LONG).show();
Log.w(TAG, "low storage error");
}
}
// Creates and starts the camera. Note that this uses a higher resolution in comparison
// to other detection examples to enable the barcode detector to detect small barcodes
// at long distances.
CameraSource.Builder builder = new CameraSource.Builder(getApplicationContext(), barcodeDetector)
.setFacing(CameraSource.CAMERA_FACING_BACK)
//.setRequestedPreviewSize(getWindow().getDecorView().getHeight(), getWindow().getDecorView().getWidth())
.setRequestedFps(30.0f);
builder = builder.setAutoFocusEnabled(autoFocus);
_cameraSource = builder.build();
}
/**
* Restarts the camera.
*/
#Override
protected void onResume() {
super.onResume();
startCameraSource();
((TracksApplication) getApplication()).set_currentClass(getClass().getSimpleName());
}
/**
* Stops the camera.
*/
#Override
protected void onPause() {
super.onPause();
if (_preview != null) {
_preview.stop();
}
}
/**
* Releases the resources associated with the camera source, the associated detectors, and the
* rest of the processing pipeline.
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (_preview != null) {
_preview.release();
}
}
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {#link #requestPermissions(String[], int)}.
* <p>
* <strong>Note:</strong> It is possible that the permissions request interaction
* with the user is interrupted. In this case you will receive empty permissions
* and results arrays which should be treated as a cancellation.
* </p>
*
* #param requestCode The request code passed in {#link #requestPermissions(String[], int)}.
* #param permissions The requested permissions. Never null.
* #param grantResults The grant results for the corresponding permissions
* which is either {#link PackageManager#PERMISSION_GRANTED}
* or {#link PackageManager#PERMISSION_DENIED}. Never null.
* #see #requestPermissions(String[], int)
*/
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != RC_HANDLE_CAMERA_PERM) {
Log.d(TAG, "Got unexpected permission result: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Camera permission granted - initialize the camera source");
// we have permission, so create the camerasourc
createCameraSource(true);
return;
}
Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
" Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)"));
}
/**
* Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet
* (e.g., because onResume was called before the camera source was created), this will be called
* again when the camera source is created.
*/
private void startCameraSource() throws SecurityException {
if (_cameraSource != null) {
try {
_preview.start(_cameraSource);
} catch (IOException e) {
Log.e(TAG, "Unable to start camera source.", e);
_cameraSource.release();
_cameraSource = null;
}
}
}
#Override
public void onBarcodeDetected(Barcode barcode) {
Intent data = new Intent();
data.putExtra(BARCODE_OBJECT, barcode);
setResult(CommonStatusCodes.SUCCESS, data);
finish();
}
}
I don't know what is wrong, if someone have an idea...
The problem is "solved" , it appears to be a problem with datamatrix that I received.
Because I tried to scan those with multiple scanner like an IOS device , or a Google play store application , everything and they are not recognized too.
This error shows "java.lang.RuntimeException: Camera is being used after Camera.release() was called" when me switching camera. Is my way to release the camera is not the correct way? Can someone suggest me the best way to implement this?
This is my CameraActivity.java:
private Camera mCamera;
private CameraPreviewActivity mPreview;
private int cameraId = 0;
private static final int MY_PERMISSIONS_REQUEST_CAMERA = 1;
/**
* Check if this device has a camera
*/
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
/**
* A safe way to get an instance of the Camera object.
*/
// attempt to get a Camera instance
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open();
} catch (Exception e) {
System.out.println("Camera not working.");
}
return c; // returns null if camera is unavailable
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camerapreview_activity);
// Create our Preview view and set it as the content of our activity.
// Create an instance of Camera
switchCamera();
showCamera();
}
public void switchCamera() {
ToggleButton facingSwitch = (ToggleButton) findViewById(R.id.facingSwitch);
facingSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
onDestroy();
preview.removeAllViews();
if (isChecked) {
// The toggle is enabled
preview.addView(mPreview);
Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
} else {
// The toggle is disabled
preview.addView(mPreview);
Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
}
}
});
}
protected void onDestroy () {
super.onDestroy();
if (mCamera != null){
mCamera.stopPreview();
mCamera.release(); // release the camera for other applications
mCamera = null;
}
}
public void showCamera() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
Toast.makeText(this, "Camera permission is needed to show the camera preview", Toast.LENGTH_SHORT).show();
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST_CAMERA);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
else {
// Permission has already been granted
mCamera = getCameraInstance();
mPreview = new CameraPreviewActivity(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
}
// Here, thisActivity is the current activity
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_CAMERA: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Create our Preview view and set it as the content of our activity.
// permission was granted, yay! Do the
// contacts-related task you need to do.
showCamera();
} else {
Toast.makeText(this, "Permission was not granted", Toast.LENGTH_SHORT).show();
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
}
}
// other 'case' lines to check for other
// permissions this app might request.
And this is my CameraPreviewActivity.java
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreviewActivity(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
Camera.open(1) and Camera.open(0) return a camera instance, use it to replace mCamera. This does not explain the RuntimeException.
This exception happens because your CameraPreviewActivity keeps its separate reference to the Camera instance which you opened in CameraActivity.
The best practice would be to keep the reference only in the CameraPreviewActivity class, and remove the mCamera field from CameraActivity class.
I'm trying to create a new android application that is comprised of multiple mini-games. The launcher activity extends BaseGameActivity and has a sign-in button and a ListView containing all the possible games that can be played.
Inside of a mini-game activity (also extends BaseGameActivity), how can I get it to create a notification which will launch a specific Activity? Currently, when I call invitePlayersToGame, the invitation that gets sent is for the full application (Mini-Games) and not the individual game (specific dice game).
public void invitePlayersToGame(View pView) {
Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(getApiClient(), 1, 1);
intent.putExtra("gameName", "Patman Yahtzee");
startActivityForResult(intent, RC_SELECT_PLAYERS);
}
Is there a way to get the notification to generate with a specific message? Is there a way to get notification to open directly to the mini-game activity without going to the main launcher activity first?
Any help is appreciated. Thanks!
You can send sendReliableMessage for method handshaking.
First enter a room (quickgame or send invite).
public void openInvitationIntent() {
// launch the player selection screen
// minimum: 1 other player; maximum: 1 other players
Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(mGoogleApiClient, 1, 1);
startActivityForResult(intent, RC_SELECT_PLAYERS);
}
onConnected:
#Override
public void onConnected(Bundle connectionHint) {
// QuickGame
if (mGameMode == 1) {
Log.d(TAG, "onConnected() called. Sign in successful!");
Log.d(TAG, "Sign-in succeeded.");
startQuickGame();
// register listener so we are notified if we receive an invitation to play
// while we are in the game
if (connectionHint != null) {
Log.d(TAG, "onConnected: connection hint provided. Checking for invite.");
Invitation inv = connectionHint.getParcelable(Multiplayer.EXTRA_INVITATION);
if (inv != null && inv.getInvitationId() != null) {
// retrieve and cache the invitation ID
Log.d(TAG, "onConnected: connection hint has a room invite!");
acceptInviteToRoom(inv.getInvitationId());
return;
}
}
}
// Send request
else if (mGameMode == 0) {
// request code for the "select players" UI
// can be any number as long as it's unique
invitationInbox();
}
// request accepted
else {
mIncomingInvitationId = getIntent().getExtras().getString(AppConstants.RC_INVITATION_ID);
RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
roomConfigBuilder.setInvitationIdToAccept(mIncomingInvitationId);
Games.RealTimeMultiplayer.join(mGoogleApiClient, roomConfigBuilder.build());
// prevent screen from sleeping during handshake
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
After this, you can send model class (includes what you need).
private void broadcastMessage(ModelGameRecievedMessage broadcastedMessage, boolean isFinal) {
try {
if ( mParticipants != null && broadcastedMessage != null) {
byte[] bytes = Utils.serialize(broadcastedMessage);
// Send to every other participant.
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId)) {
continue;
}
if (p.getStatus() != Participant.STATUS_JOINED) {
continue;
}
if (mRoomId != null) {
if (isFinal) {
// final score notification must be sent via reliable broadcastedMessage
Games.RealTimeMultiplayer.sendReliableMessage(mGoogleApiClient, null, bytes,
mRoomId, p.getParticipantId());
} else {
// it's an interim score notification, so we can use unreliable
Games.RealTimeMultiplayer.sendUnreliableMessage(mGoogleApiClient, bytes,
mRoomId, p.getParticipantId());
}
}
}
Logy.l("broadcastedMessage.getMessageTypeId(); " + broadcastedMessage.getMessageTypeId());
Logy.l("broadcastedMessage.getMessage(); " + broadcastedMessage.getMessage());
}
} catch (IOException e) {
e.printStackTrace();
}
}
finally you can reach the data on other devices:
#Override
public void onRealTimeMessageReceived(RealTimeMessage rtm) {
byte[] bufy = rtm.getMessageData();
ModelGameRecievedMessage recievedMessage = null;
try {
recievedMessage = (ModelGameRecievedMessage) Utils.deserialize(bufy);
Logy.l("recievedMessage.getMessageTypeId(); " + recievedMessage.getMessageTypeId());
Logy.l("recievedMessage.getMessage(); " + recievedMessage.getMessage());
} catch (Exception e) {
Logy.e("Exception onRealTimeMessageReceived deserialize: " + e);
}
switch (recievedMessage.getMessageTypeId()) {
case AppConstants.RC_MULTI_START_TIMEMILIS_MULTIPLAYER:
....
I need a way to control the camera flash on an Android device while it is recording video. I'm making a strobe light app, and taking videos with a flashing strobe light would result in the ability to record objects that are moving at high speeds, like a fan blade.
The flash can only be enabled by starting a video preview and setting FLASH_MODE_TORCH in the camera's parameters. That would look like this:
Camera c = Camera.open();
Camera.Parameters p = c.getParameters();
p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
c.setParameters(p);
c.startPreview();
Once the preview has started, I can flip that parameter back and forth to turn the light on and off. This works well until I try to record a video. The trouble is that in order to give the camera to the MediaRecorder, I first have to unlock it.
MediaRecorder m = new MediaRecorder();
c.unlock(); // the killer
m.setCamera(c);
After that unlock, I can no longer change the camera parameters and therefore have no way to change the flash state.
I do not know if it is actually possible to do this since I'm not the best at java-hacking, but here is what I do know:
Camera.unlock() is a native method, so I can't really see the mechanism behind the way it locks me out
Camera.Parameter has a HashMap that contains all of its parameters
Camera.setParameters(Parameters) takes the HashMap, converts it to a string, and passes it to a native method
I can eliminate all the parameters but TORCH-MODE from the HashMap and the Camera will still accept it
So, I can still access the Camera, but it won't listen to anything I tell it. (Which is kind of the purpose of Camera.unlock())
Edit:
After examining the native code, I can see that in CameraService.cpp my calls to Camera.setParameters(Parameters) get rejected because my Process ID does not match the Process ID the camera service has on record. So it would appear that that is my hurdle.
Edit2:
It would appear that the MediaPlayerService is the primary service that takes control of the camera when a video is recording. I do not know if it is possible, but if I could somehow start that service in my own process, I should be able to skip the Camera.unlock() call.
Edit3:
One last option would be if I could somehow get a pointer to the CameraHardwareInterface. From the looks of it, this is a device specific interface and probably does not include the PID checks. The main problem with this though is that the only place that I can find a pointer to it is in CameraService, and CameraService isn't talking.
Edit4: (several months later)
At this point, I don't think it is possible to do what I originally wanted. I don't want to delete the question on the off chance that someone does answer it, but I'm not actively seeking an answer. (Though, receiving a valid answer would be awesome.)
I encountered a similar issue. The user should be able to change the flash mode during recording to meet their needs depending on the light situation. After some investigative research i came to the following solution:
I assume, that you've already set up a proper SurfaceView and a SurfaceHolder with its necessary callbacks. The first thing i did was providing this code (not declared variables are globals):
public void surfaceCreated(SurfaceHolder holder) {
try {
camera = Camera.open();
parameters = camera.getParameters();
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
camera.setPreviewDisplay(holder);
camera.startPreview();
recorder = new MediaRecorder();
} catch (IOException e) {
e.printStackTrace();
}
}
My next step was initializing and preparing the recorder:
private void initialize() {
camera.unlock();
recorder.setCamera(camera);
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
recorder.setVideoFrameRate(20);
recorder.setOutputFile(filePath);
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
finish();
} catch (IOException e) {
e.printStackTrace();
finish();
}
}
It's important to note, that camera.unlock() has to be called BEFORE the whole initialization process of the media recorder. That said also be aware of the proper order of each set property, otherwise you'll get an IllegalStateException when calling prepare() or start(). When it comes to recording, i do this. This will usually be triggered by a view element:
public void record(View view) {
if (recording) {
recorder.stop();
//TODO: do stuff....
recording = false;
} else {
recording = true;
initialize();
recorder.start();
}
}
So now, i finally can record properly. But what's with that flash? Last but not least, here comes the magic behind the scenes:
public void flash(View view) {
if(!recording) {
camera.lock();
}
parameters.setFlashMode(parameters.getFlashMode().equals(Parameters.FLASH_MODE_TORCH) ? Parameters.FLASH_MODE_OFF : Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
if(!recording) {
camera.unlock();
}
}
Everytime i call that method via an onClick action i can change the flash mode, even during recording. Just take care of properly locking the camera. Once the lock is aquired by the media recorder during recording, you don't have to lock/unlock the camera again. It doesn't even work. This was tested on a Samsung Galaxy S3 with Android-Version 4.1.2. Hope this approach helps.
After preparing media recorder, use camera.lock(), and then set whatever parameters you want to set to camera.
But before starting recording you need to call camera.unlock(), and after you stop media recorder you need to call camera.lock() to start preview.
Enjoy!!!
Try this.. hopefully it will work.. :)
private static Torch torch;
public Torch() {
super();
torch = this;
}
public static Torch getTorch() {
return torch;
}
private void getCamera() {
if (mCamera == null) {
try {
mCamera = Camera.open();
} catch (RuntimeException e) {
Log.e(TAG, "Camera.open() failed: " + e.getMessage());
}
}
}
public void toggleLight(View view) {
toggleLight();
}
private void toggleLight() {
if (lightOn) {
turnLightOff();
} else {
turnLightOn();
}
}
private void turnLightOn() {
if (!eulaAgreed) {
return;
}
if (mCamera == null) {
Toast.makeText(this, "Camera not found", Toast.LENGTH_LONG);
button.setBackgroundColor(COLOR_WHITE);
return;
}
lightOn = true;
Parameters parameters = mCamera.getParameters();
if (parameters == null) {
button.setBackgroundColor(COLOR_WHITE);
return;
}
List<String> flashModes = parameters.getSupportedFlashModes();
if (flashModes == null) {
button.setBackgroundColor(COLOR_WHITE);
return;
}
String flashMode = parameters.getFlashMode();
Log.i(TAG, "Flash mode: " + flashMode);
Log.i(TAG, "Flash modes: " + flashModes);
if (!Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
if (flashModes.contains(Parameters.FLASH_MODE_TORCH)) {
parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(parameters);
button.setBackgroundColor(COLOR_LIGHT);
startWakeLock();
} else {
Toast.makeText(this, "Flash mode (torch) not supported",
Toast.LENGTH_LONG);
button.setBackgroundColor(COLOR_WHITE);
Log.e(TAG, "FLASH_MODE_TORCH not supported");
}
}
}
private void turnLightOff() {
if (lightOn) {
button.setBackgroundColor(COLOR_DARK);
lightOn = false;
if (mCamera == null) {
return;
}
Parameters parameters = mCamera.getParameters();
if (parameters == null) {
return;
}
List<String> flashModes = parameters.getSupportedFlashModes();
String flashMode = parameters.getFlashMode();
if (flashModes == null) {
return;
}
Log.i(TAG, "Flash mode: " + flashMode);
Log.i(TAG, "Flash modes: " + flashModes);
if (!Parameters.FLASH_MODE_OFF.equals(flashMode)) {
if (flashModes.contains(Parameters.FLASH_MODE_OFF)) {
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameters);
stopWakeLock();
} else {
Log.e(TAG, "FLASH_MODE_OFF not supported");
}
}
}
}
private void startPreview() {
if (!previewOn && mCamera != null) {
mCamera.startPreview();
previewOn = true;
}
}
private void stopPreview() {
if (previewOn && mCamera != null) {
mCamera.stopPreview();
previewOn = false;
}
}
private void startWakeLock() {
if (wakeLock == null) {
Log.d(TAG, "wakeLock is null, getting a new WakeLock");
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
Log.d(TAG, "PowerManager acquired");
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
Log.d(TAG, "WakeLock set");
}
wakeLock.acquire();
Log.d(TAG, "WakeLock acquired");
}
private void stopWakeLock() {
if (wakeLock != null) {
wakeLock.release();
Log.d(TAG, "WakeLock released");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Eula.show(this)) {
eulaAgreed = true;
}
setContentView(R.layout.main);
button = findViewById(R.id.button);
surfaceView = (SurfaceView) this.findViewById(R.id.surfaceview);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
disablePhoneSleep();
Log.i(TAG, "onCreate");
}
To access the device camera, you must declare the CAMERA permission in your Android Manifest. Also be sure to include the <uses-feature> manifest element to declare camera features used by your application. For example, if you use the camera and auto-focus feature, your Manifest should include the following:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
A sample that checks for torch support might look something like this:
//Create camera and parameter objects
private Camera mCamera;
private Camera.Parameters mParameters;
private boolean mbTorchEnabled = false;
//... later in a click handler or other location, assuming that the mCamera object has already been instantiated with Camera.open()
mParameters = mCamera.getParameters();
//Get supported flash modes
List flashModes = mParameters.getSupportedFlashModes ();
//Make sure that torch mode is supported
//EDIT - wrong and dangerous to check for torch support this way
//if(flashModes != null && flashModes.contains("torch")){
if(flashModes != null && flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)){
if(mbTorchEnabled){
//Set the flash parameter to off
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
}
else{
//Set the flash parameter to use the torch
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
}
//Commit the camera parameters
mCamera.setParameters(mParameters);
mbTorchEnabled = !mbTorchEnabled;
}
To turn the torch on, you simply set the camera parameter Camera.Parameters.FLASH_MODE_TORCH
Camera mCamera;
Camera.Parameters mParameters;
//Get a reference to the camera/parameters
mCamera = Camera.open();
mParameters = mCamera.getParameters();
//Set the torch parameter
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
//Comit camera parameters
mCamera.setParameters(mParameters);
To turn the torch off, set Camera.Parameters.FLASH_MODE_OFF