I am using camera to take pictures in the foreground service, which can be used long term like 5 to 10 hours. The problem is when another app starts using camera while the foreground service is running it stop taking pictures and user have to manually restart the service.
I am taking pictures in a loop ( I initializes the camera instance takes a picture then releases the camera and repeat). The solution I see here, is that at the start of each loop iteration I should add a check to see if the camera is currently in use.
How exactly can I check if the camera is in use by any other application or process on device? or is there a better approach to my problem?
If you are using camera2 api, checkout CameraManager.AvailabilityCallback callback.
it will inform you via callback when a cameraID is available/unavailable.
This worked for me!!
if returns true, means camera is free
public boolean isCameraFree() {
Camera camera = null;
try {
camera = Camera.open();
} catch (RuntimeException e) {
return false;
} finally {
if (camera != null) camera.release();
}
return true;
}
Related
My first experience with background video recording on Android was with JavaCV FFMpegRecoder. It's easy to implement, just create camera instance in activity, set PreviewCallback listener in the service, on onPreviewFrame just send byte to FFMpegRecorder and don't destroy (disconnect) camera in onPause or onStop of course
But FFMpegRecorder isn't that good (cpu, memory usage)
So I found INDExOS m4m library (by Intel): https://github.com/INDExOS/media-for-mobile
It has CameraCapturerActivity.java - https://github.com/INDExOS/media-for-mobile/blob/master/samples/src/main/java/org/m4m/samples/CameraCapturerActivity.java
Seems it really doesn't eat many resources
I decided to try recording in background mode, I just simply commented its onPause method where stop recording and preview methods are executed, but it just doesn't record anything (freezes on the last frame) until I return to activity
When I set PreviewCallback listener to this class, onPreviewFrame sends byte in background ok, seems onFrameAvailable of SurfaceTexture related to delivering frames in m4m library are stopped when onPause is called from Activity
library has two onFrameAvailable listeners:
first in PreviewRender.java - https://github.com/INDExOS/media-for-mobile/blob/master/android/src/main/java/org/m4m/android/PreviewRender.java#L241, seems class contains everything that related to displaying frames in view class (so should not be important for recording video)
second in CameraSource.java - https://github.com/INDExOS/media-for-mobile/blob/master/android/src/main/java/org/m4m/android/CameraSource.java#L222
seems this is the main class that gets frames, and I guess it is used for video recording
But also it seems those classes are still related in quite things
For example if I comment createPreview method in CameraCapturerActivity.java
private void createPreview() {
surfaceView = new GLSurfaceView(getApplicationContext());
surfaceView.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR);
((RelativeLayout) findViewById(R.id.camera_layout)).addView(surfaceView, 0);
preview = capture.createPreview(surfaceView, camera);
preview.setFillMode(fillMode);
if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
capture.setOrientation(90);
} else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
capture.setOrientation(0);
}
preview.start();
}
app will run ok, of course I won't see frames, but recording won't be working when trying to press the record button, so recording doesn't work if preview wasn't created
So I need help to understand how all of this works, how could I pause preview but continue recording in background when I leave activity and resume preview when returning to activity again. I didn't work with SurfaceTexture, GLSurfaceView, only worked with ordinary SurfaceView and its holder callbacks onSurfaceCreated, onSurfaceChanged,..
I just don't see in the project something similar to onSurfaceDestroy which would stop recording when user leaves activity
I see OpenGl API, textures are also used in Grafika project https://github.com/google/grafika
So I believe there are people who worked with something like this and could know how SurfaceTexture and its callbacks (onFrameAvailable,..) works
Of course many things are related to m4m library code design itself, but still hard to understand something when you didn't work with all of this (opengl, surfacetexture,...)
UPDATE
Now I know a little about EGLContext, that we have to set it to a specific source (for preview or for recording - encoder)
I succeeded to make Grafika recording example working in background https://github.com/google/grafika/blob/master/src/com/android/grafika/ContinuousCaptureActivity.java
In that sample class I commented everything in onPause, removed mDisplaySurface and did other things
And onFrameAvailable looks like this now:
#Override // SurfaceTexture.OnFrameAvailableListener; runs on arbitrary thread
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Log.d(TAG, "frame available");
if (mEglCore == null) {
return;
}
mEncoderSurface.makeCurrent();
mCameraTexture.updateTexImage();
mCameraTexture.getTransformMatrix(mTmpMatrix);
if (!mFileSaveInProgress) {
GLES20.glViewport(0, 0, VIDEO_WIDTH, VIDEO_HEIGHT);
mFullFrameBlit.drawFrame(mTextureId, mTmpMatrix);
drawExtra(mFrameNum, VIDEO_WIDTH, VIDEO_HEIGHT);
mCircEncoder.frameAvailableSoon();
mEncoderSurface.setPresentationTime(mCameraTexture.getTimestamp());
mEncoderSurface.swapBuffers();
}
mFrameNum++;
//mHandler.sendEmptyMessage(MainHandler.MSG_FRAME_AVAILABLE);
}
So now when I press home button, it still records frames to a file I can see it later
Now I need to get back to m4m library, cause it record audio and has utils for frame processing
In Grafika, all the videos are recorded in internal storage.
Try to change the output path of video to see it in other player. Something like that:
File outputFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
boolean isPresent = true;
if (!outputFile.exists()) {
isPresent = outputFile.mkdir();
}
if (isPresent) {
outputFile = new File(outputFile.getAbsolutePath(),"camera-test.mp4");
} else {
// Failure
}
You can change CameraCaptureActivity (Show + camera capture) example to perform what you want. Comment OnPause method and change openCamera like that:
private void openCamera(int desiredWidth, int desiredHeight) {
if (mCamera != null) {
return;
}
I am having a headache over the Camera API 1 for android. After reading all of the Internet content, I made some sample app that works OK. It creates a service, which then is used to operate with the camera in the background, so there is no preview or activity enabled. To achieve this I use a dummy SurfaceHolder, like this:
protected class MySurfaceHolder implements SurfaceHolder {
private final Surface surface;
private final SurfaceTexture surfaceTexture;
public MySurfaceHolder () {
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
if (textures.length > 0) {
this.surfaceTexture = new SurfaceTexture(textures[0]);
this.surface = new Surface(this.surfaceTexture);
} else {
this.surface = null;
this.surfaceTexture = null;
}
}
[...]
}
and then I use it like this
// simplified version of my code
try {
initializeCamera(); // open camera and set Camera.Parameters
camera.setPreviewDisplay(new MySurfaceHolder());
camera.startPreview();
camera.unlock();
initializeMediaRecorder(); // create MediaRecorder, set video/audio parameters
mediaRecorder.prepare();
mediaRecorder.start();
// wait until recording finish and exit
} finally {
stopRecording();
}
the Camera and MediaRecorder initialization methods are just like the documentation states they should be (and they work).
Everything works and operates as it should. Almost everything - sometimes, under unknown circumstances the MediaRecorder creates empty files, like 32kB containing only headers and info about the video - no frames. The longer I record like this, the bigger is the file (few kB every few seconds). After 1 minute, the file weights about 80kB. Funny thing is I know that the camera is working and capturing frames (I debugged it a little showing preview frames), but the frames are not written into the output file.
Also when it happens I am not able to record in FHD (1920x1080) - I get the "start failed" message - at this time camera is not capturing frames. The same thing could happen when I use wrong (not supported) video size. I suppose in this case the message is thrown at the mediaRecorder.start(); line, and stopRecording(); is invoked but I am not sure.
After some time or after unknown action the problem is suddenly gone (I don't know when, I don't know how). It happens for sure on Android 5.1, but may happen on other versions as well.
Could this bug be related to my custom surface code?
What could cause the MediaRecorder to not write frames into a file?
Why I am not able to record in FHD, but in the same time I am able to record in HD (1280x720)?
Is there any alternative for MediaRecorder, so I can avoid these bugs?
May it happen when another app is trying to get Camera object, thus distrupting current recording? If so, how to regain access to the Camera object (I apparently am not able to do this now on some devices).
EDIT:
I think I might have a clue. I am calling
camera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
// ... get current frame
}
camera.startPreview();
to get preview frame of current recording. It appears that the bug occurs when I am using this method to get preview frame (at random times). It seems flawed, because not all devices react to this thing properly (sometimes there is no preview frame...). Is there any other, better method of handling current preview frame without the real surface?
I want to record a video call made on skype on android phone. But when the call gets connected i start my app which record the video. But it troughs an error (My app cant start recording) "java.lang.RuntimeException: Fail to connect to camera service"
The camera can only be used by one application at a time.
As per the open() documentation:
Creates a new Camera object to access a particular hardware camera. If the same camera is opened by other applications, this will throw a RuntimeException.
http://developer.android.com/guide/topics/media/camera.html states the following:
Accessing cameras
If you have determined that the device on which your application is running has a camera, you must request to access it by getting an instance of Camera (unless you are using an intent to access the camera).
To access the primary camera, use the Camera.open() method and be sure
to catch any exceptions, as shown in the code below:
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
// Camera is not available (in use or does not exist)
So, simply said, your answer is no.
I have a small app I have been working on that uses the front camera. The way I have been obtaining use of the front camera seems to work on most phones, but users have been reporting trouble on the S3 and various other new devices. The way I have been accessing the front camera is like so:
// Find the ID of the front camera
CameraInfo cameraInfo = new CameraInfo();
for(int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if(cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
defaultCameraId = i;
mCameraFound = true;
}
}
if(!mCameraFound)
displayDialog(8);
From some of the error reporting I've added into the app, I've noticed the S3 actually finds the front camera, but users report it only shows a blank screen? I have only been able to test on the devices I have (GNex and N7). I was hoping someone here may have some experience with this or may be able to help me solve this issue. If you want to try the app out on your S3, check the link below. Thanks in advance.
https://play.google.com/store/apps/details?id=com.wckd_dev.mirror
EDIT: I created a MirrorView object which contains a TextureView used for the preview. The MirrorView object implements a SurfaceTextureListener. Within the onSurfaceTextureAvailable() method is where the preview is started. I also created a method for restarting the preview after the app has gone from hidden back to visible.
So this is called when the app is first started:
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
try {
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
requestLayout();
mCamera.setParameters(parameters);
mCamera.setPreviewTexture(surface);
mCamera.startPreview();
}
}
catch(RuntimeException e) {
// Log.e(TAG, "RuntimeException caused by setPreviewTexture()", exception);
}
catch (IOException e) {
// Log.e(TAG, "IOException caused by setPreviewTexture()", exception);
}
}
The restartPreview call is to an identical (but separate) method. From some of the debug data I've been collecting through users, I've noticed that the app finds two camera on the S III and selects the id matching CAMERA_FACING_FRONT. Also, this issue doesn't seem to be happening on all S III. I have users who have feedback reporting as much. The latest report from a user experiencing this issue was an AT&T S III user. Any help would be appreciated!
Got some face time with an S3 tonight that was experiencing this issue with my app. Here what was going on. The TextureView relies on 2d hardware acceleration which is supposed to on by default (from what I understood) on 4.0+ devices. It wasn't turning on (for my app at least) on his phone. The fix was as simple as adding a single line in the manifest (under application).
android:hardwareAcceleration = "true"
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Android Camera will not work. startPreview fails
I am trying to set a camera preview in a custom SurfaceView but I get an exception each time I execute the initialization method.
Below is the code for camera preview initialization:
private void init(Context context)
{
setFocusable(true);
mRecording = false;
fileRW = new FileReaderWriter();
frameCount = 0;
if(mCamera == null)
{
mCamera = Camera.open();
}
Parameters parameters = mCamera.getParameters();
parameters.setPictureFormat(PixelFormat.JPEG);
mCamera.setParameters(parameters);
try {
mCamera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.startPreview();
}
the line mCamera.setPreviewDisplay(surfaceHolder); throws an exception (setPreviewDisplay failed) each time I try to execute the method.
Does anyone know what could be wrong? I would really appreciate any of your help.
Thanks!`
I completely agree with Jon Bright
I couldn't figure out what was going on for a week, I ignored the setType on the surface holder because the SDK said it was deprecated, ie.
"This method is deprecated. this is ignored, this value is set automatically when needed."
But if you don't do that, it will crash on setPreview. This is running 1.5 SDK (I need it to be backwards compatible to that) on a Galaxy S with 2.1. So make sure you set the type. Not quite as automatic as the documentation makes it sound.
The best place to call setPreviewDisplay() is in surfaceChanged() If the surface is just created, surfaceChanged will be called at least once and you can startPreview() and setPreviewDisplay there. If the surface changes and the preview already starts, you can stopPreview/setPreviewDisplay/startPreview there. Even if your app does not change the size of the surface, the framework may still unexpectedly call surfaceChanged() when the app starts or exits due to orientation changes. So your app really needs to handle surfaceChanged properly. You can trace the source code of camera application in Android for reference.
The code snippet in another answer works if surfaceChanged() is only called once in the app lifecycle.