I am developing a camera app and I will use front and back camera. I am using TextureView like this:
<TextureView
android:id="#+id/textureView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
/>
and code behind:
renderer = new CameraRenderer(this, isFrontCamera);
textureView.setSurfaceTextureListener(renderer);
CameraRenderer class implemet SurfaceTextureListener
public class CameraRenderer implements Runnable, TextureView.SurfaceTextureListener{...
and I set camera in onSurfaceTextureAvailable method like this:
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
if (renderThread != null && renderThread.isAlive()) {
renderThread.interrupt();
}
renderThread = new Thread(this);
surfaceTexture = surface;
gwidth = width;
gheight = height;
// Open camera
int cameraId;
if(isFrontCamera)
cameraId = getFrontCamera();
else
cameraId = getBackCamera();
camera = Camera.open(cameraId);
renderThread.start();
}
Problem:
When I want to change camera and click button I am creating a new CameraRenderer instance and set to textureView
textureView.setSurfaceTextureListener(renderer);
but onSurfaceTextureDestroyed method in CameraRenderer class never called, also onSurfaceTextureAvailable method is not called too.
I can call onSurfaceTextureDestroyed method myself but I can't set second camera preview to textureView because onSurfaceTextureAvailable method is not called second time.
Here is my onSurfaceTextureDestroyed method:
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (camera != null) {
camera.stopPreview();
camera.release();
}
if (renderThread != null && renderThread.isAlive()) {
renderThread.interrupt();
}
CameraFilter.release();
return true;
}
Last note: I tried set listener as null but not working.
So How can I re-set textureView Listener. Thanks in advance!
Related
The idea is simple: My App should create a camera object, show a preview and take a picture (without pressing any button) and close the camera afterwards. Unfortunately it is not even taking any pictures. (Means: My callback code which should be executed after the picture is taken is never reached.)
I'm using a little wrapper class for the android camera api. My activity code looks like this:
camera = new SurveillanceCamera(this, layoutForPreview);
camera.start();
camera.takePicture();
The wrapper class:
public SurveillanceCamera(Context context, LinearLayout previewLayout) {
cameraInstance = Camera.open(findBackFacingCamera());
pictureCallback = getPictureCallback();
cameraPreview = new CameraPreview(context, cameraInstance);
this.previewLayout = previewLayout;
this.previewLayout.addView(cameraPreview);
cameraInstance.startPreview();
}
public void takePicture() {
//this code is reached
cameraInstance.takePicture(null, null, pictureCallback);
}
public void start() {
if (cameraInstance == null) {
cameraInstance = Camera.open(findBackFacingCamera());
pictureCallback = getPictureCallback();
cameraPreview.setCamera(cameraInstance);
cameraInstance.startPreview();
}
}
private PictureCallback getPictureCallback() {
PictureCallback picture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.e(TAG, "Jey picture was taken...");
// refresh camera to continue preview
cameraPreview.refreshCamera(cameraInstance);
}
};
return picture;
}
private int findBackFacingCamera() {
int cameraId = -1;
// Search for the back facing camera
// get the number of cameras
int numberOfCameras = Camera.getNumberOfCameras();
// for every camera check
for (int i = 0; i < numberOfCameras; i++) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
cameraId = i;
break;
}
}
return cameraId;
}
There are no exceptions thrown in the code, but the Log.e() in onPictureTaken is not shown. It seems that the camera preview is displayed but nothing happens. Is it possible, that the preview is not fully loaded at the moment when camera.takePicture() is executed? Any suggestions?
Your suspicion is most likely justified. I don't see when setPreviewDisplay() is called. But this call should complete for takePicture() to succeed.
It is impossible to simply call
camera = new SurveillanceCamera(this, layoutForPreview);
camera.start();
camera.takePicture();
because to complete a setPreviewDisplay() call, you need a fully initialized Holder instance, but you only add the surface view in constructor.
It would be best to register a SurfaceHolder.Callback and call takePicture() from the surfaceChanged() callback.
My app contains three fragments. I need to keep the user experience fluid so I'm using a the setOffscreenPageLimit() method to keep them all alive in memory.
Problem: when I start a new activity (even empty), It loads slowly. Same when I finish it.
I know this is coming from my Camera Preview fragment because when I comment out the init of the camera, everything runs very smoothly.
Here is how I initialize my camera preview on the OnResume method:
mCamera = GetCameraInstance(currentCameraId);
//-- Set the SurfaceView
preview = (SurfaceView) view.findViewById(R.id.camera_preview);
mSurfaceHolder = preview.getHolder();
mSurfaceHolder.addCallback(this);
if (mCamera != null) {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
}
When the new activity is started, the surfaceDestroyed method is called which destroys the Camera preview. When the new activity is terminated, the app recreate a camera view again.
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
The time spent on stopPreview() and release() method as seen on traceview. It takes around 700ms to destroy the camera preview.
Screenshot of Traceview
Based on #CommonsWare suggestion, here is how I did it.
First, I placed my mCamera variable in the Application class
public Camera mCamera;
Then, I placed the AsyncTask in my fragment
private class ControlCameraTask extends AsyncTask<Integer, Void, Void> {
protected Void doInBackground(Integer... urls) {
//--
if (app.mCamera != null) {
app.mCamera.stopPreview();
app.mCamera.release();
app.mCamera = null;
} else {
app.mCamera = GetCameraInstance(currentCameraId);
app.mCamera.setPreviewDisplay(app.mSurfaceHolder);
app.mCamera.startPreview();
}
return null;
}
}
Finally, I just call my AsyncTask on onResumeand onPausemethods
#Override
public void onResume() {
super.onResume();
new ControlCameraTask().execute(1);
}
#Override
public void onPause() {
super.onPause();
new ControlCameraTask().execute(1);
}
PS: I removed the trycatches for the code to be easily readable.
I have a problem. After initializing the camera for a preview and bringing another app into focus, then back to my app: the preview shows up black. If I continue to take a picture, it takes a picture of where I point the camera normally.
Am I doing something wrong on the OnResume() override? Relative code is below:
public void ReleaseCamera()
{
if (myCamera != null)
{
myCamera.Release();
myCamera = null;
}
}
protected override void OnPause()
{
base.OnPause();
if (myButtonState == ButtonState.CameraActive)
ReleaseCamera();
}
protected override void OnResume()
{
base.OnResume();
if (myButtonState == ButtonState.CameraActive)
InitializeCamera();
}
private void InitializeCamera()
{
SurfaceView mySurfaceView = FindViewById<SurfaceView>(Resource.Id.surfaceView1);
myCamera = Android.Hardware.Camera.Open(cameraNumber);
Android.Hardware.Camera.Parameters p = myCamera.GetParameters();
myCamera.SetDisplayOrientation(90); // Portrait
myCamera.SetPreviewDisplay(mySurfaceView.Holder);
myCamera.StartPreview();
}
Thank you for your help. :)
onResume() gets called too early. You don't have the surface holder ready at this stage. You can try to introduce onPostResume() handler in your Activity, and/or handle the SurfaceHolder.Callback.surfaceChanged() event.
I am trying to create a surface view for a camera so it renders on the surface whenever is in the view of the camera. At the moment all I can see on my camera view is a black screen view. I have tried to look on Google and here but so far I haven't found what I am looking for. Anyone can suggest me some idea.
I have written a class that can help you.
public class Preview_can_work extends Activity {
private SurfaceView surface_view;
private Camera mCamera;
SurfaceHolder.Callback sh_ob = null;
SurfaceHolder surface_holder = null;
SurfaceHolder.Callback sh_callback = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
surface_view = new SurfaceView(getApplicationContext());
addContentView(surface_view, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
if (surface_holder == null) {
surface_holder = surface_view.getHolder();
}
sh_callback = my_callback();
surface_holder.addCallback(sh_callback);
}
SurfaceHolder.Callback my_callback() {
SurfaceHolder.Callback ob1 = new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mCamera.startPreview();
}
};
return ob1;
}
}
in your manifest file copy this code for camera permission
<uses-permission android:name="android.permission.CAMERA"/>
Explanation:
SurfaceView is a type of View which contains a SurfaceHolder. SurfaceHolder holds the surface on which we can display our media (generally frames).
mCamera is a Camera object which will contains the camera instance.
When you want to hold default Camera instance then you can simply call Camera.open();
Camera mCamera = Camera.open();
Now you have an open camera or you are having default camera instance. Now you need to capture frames from the camera and display it on a surface. But you cannot display it without any
surface. Here the surfaceView provides surfaceHolder and surfaceHolder provides surface to display camera frames. Now when surface will be created three callback functions will be
called.
1. public void surfaceCreated(SurfaceHolder holder)
2. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
3. public void surfaceDestroyed(SurfaceHolder holder)
Note:- Surface will be destroyed when your application will go on pause.
surfaceCreated:
surfaceCreated is a callback function which will be called when your surface will be created. In this, you can open your camera and set other attributes.
surfaceChanged:
This will be called atleast one time when your surface will be created. After that it will be called whenever your surface will change(In device rotation). Here you can
start your preview because your surface have already created.
surfaceDestroyed:
This will be called every time when your surface will destroy. Now if you dont have surface then where you can display you camera frames so I have released camera by using
mCamera.release(). This is very important because if your activity will be on pause and any other activity tries to open camera then it will not able to open it as you have
already open camera. Camera is a shared resource so one time only one application can use it. So remember one thing whenever you open a camera then always release it.
stopPreview:
When you start preview then your camera starts capturing your frames and display it on a surface. Now if your surface have destroyed then you need to stop capturing frames
from camera so you have to call mCamera.stopPreview.
Make shure you added the permission :
<uses-permission android:name="android.permission.CAMERA"/>
Also these window properties:
getWindow().setFormat(PixelFormat.TRANSLUCENT);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Post some code if that doesn't work in order to help you
I am developing a custom camera application.Given below is my Activity class.
public class MyCustomCam extends Activity {
private Camera mCamera;
private CameraPreview mPreview;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mCamera = getCameraInstance();
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
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}
}
}
Given below is my main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" />
<Button android:id="#+id/button_capture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/></LinearLayout>
And this is my CameraPreview class
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(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("", "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("", "Error starting camera preview: " + e.getMessage());
}
}
}
Here
mCamera.setPreviewDisplay(holder);
code is throwing null pointer exception...i cant fix that.please tell me why it is throwing exception and how can i fix that?
Release the camera in your surfaceDestroyed function
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
and make sure you add camera permission in your manifest file.
<uses-permission android:name="android.permission.CAMERA" />
I was having the same bug.
I used this Q&A to help solve my issue. Specifically, reference the adding of the function getBestPreviewSize(...) invoked prior to params.setPreviewSize(...) being set in surfaceChanged.
Android - cam.setPreviewDisplay(holder) running into IOError
As a side note (because this was my next bug), if you start doing any more customization of the Layout (e.g. removing the title bar), any "requests" to the UI to make changes should be made prior to setContentView(...), noted in the link below.
requestFeature() must be called before adding content
Most probably your problem would be that your emulator's camera settings. Go to AVD manager, edit your emulator and set your camera , may be to emulated. restart the emulator and launch your app.