[ACCEPTED]-Taking picture from camera without preview-android-service
it is really weird that camera on android 14 platform can't stream video until it given 13 valid preview surface. it seems that the 12 architects of the platform was not thinking 11 about 3rd party video streaming applications 10 at all. even for augmented reality case 9 the picture can be presented as some kind 8 of visual substitution, not real time camera 7 stream.
anyway, you can simply resize preview surface to 1x1 pixels and put it 6 somewhere in the corner of the widget (visual 5 element). please pay attention - resize 4 preview surface, not camera frame size.
of 3 course such trick does not eliminate unwanted 2 data streaming (for preview) which consumes 1 some system resources and battery.
I found the answer to this in the Android Camera Docs.
Note: It 9 is possible to use
MediaRecorder
without creating a camera preview 8 first and skip the first few steps of this 7 process. However, since users typically 6 prefer to see a preview before starting 5 a recording, that process is not discussed 4 here.
You can find the step by step instructions 3 at the link above. After the instructions, it 2 will state the quote that I have provided 1 above.
Actually it is possible, but you have to 3 fake the preview with a dummy SurfaceView
SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);
Update 9/21/11: 2 Apparently this does not work for every 1 Android device.
Taking the Photo
Get this working first before trying to 5 hide the preview.
- Correctly set up the preview
- Use a
SurfaceView
(pre-Android-4.0 compatibility) orSurfaceTexture
(Android 4+, can be made transparent) - Set and initialise it before taking the photo
- Wait for the
SurfaceView
'sSurfaceHolder
(viagetHolder()
) to reportsurfaceCreated()
or theTextureView
to reportonSurfaceTextureAvailable
to itsSurfaceTextureListener
before setting and initialising the preview.
- Use a
- Ensure the preview is visible:
- Add it to the
WindowManager
- Ensure its layout size is at least 1x1 pixels (you might want to start by making it
MATCH_PARENT
xMATCH_PARENT
for testing) - Ensure its visibility is
View.VISIBLE
(which seems to be the default if you don't specify it) - Ensure you use the
FLAG_HARDWARE_ACCELERATED
in theLayoutParams
if it's aTextureView
.
- Add it to the
- Use
takePicture
's JPEG callback since the documentation says the other callbacks aren't supported on all devices
Troubleshooting
- If
surfaceCreated
/onSurfaceTextureAvailable
doesn't get called, theSurfaceView
/TextureView
probably isn't being displayed. - If
takePicture
fails, first ensure the preview is working correctly. You can remove yourtakePicture
call and let the preview run to see if it displays on the screen. - If the picture is darker than it should be, you might need to delay for about a second before calling
takePicture
so that the camera has time to adjust its exposure once the preview has started.
Hiding the Preview
Make the preview
View
1x1 size 4 to minimise its visibility (or try 8x16 for possibly more reliability)new WindowManager.LayoutParams(1, 1, /*...*/)
Move the preview 3 out of the centre to reduce its noticeability:
new WindowManager.LayoutParams(width, height, Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
Make 2 the preview transparent (only works for 1
TextureView
)WindowManager.LayoutParams params = new WindowManager.LayoutParams( width, height, /*...*/ PixelFormat.TRANSPARENT); params.alpha = 0;
Working Example (tested on Sony Xperia M, Android 4.3)
/** Takes a single photo on service start. */
public class PhotoTakingService extends Service {
@Override
public void onCreate() {
super.onCreate();
takePhoto(this);
}
@SuppressWarnings("deprecation")
private static void takePhoto(final Context context) {
final SurfaceView preview = new SurfaceView(context);
SurfaceHolder holder = preview.getHolder();
// deprecated setting, but required on Android versions prior to 3.0
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new Callback() {
@Override
//The preview must happen at or after this point or takePicture fails
public void surfaceCreated(SurfaceHolder holder) {
showMessage("Surface created");
Camera camera = null;
try {
camera = Camera.open();
showMessage("Opened camera");
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
throw new RuntimeException(e);
}
camera.startPreview();
showMessage("Started preview");
camera.takePicture(null, null, new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
showMessage("Took picture");
camera.release();
}
});
} catch (Exception e) {
if (camera != null)
camera.release();
throw new RuntimeException(e);
}
}
@Override public void surfaceDestroyed(SurfaceHolder holder) {}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
});
WindowManager wm = (WindowManager)context
.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
1, 1, //Must be at least 1x1
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
0,
//Don't know if this is a safe default
PixelFormat.UNKNOWN);
//Don't set the preview visibility to GONE or INVISIBLE
wm.addView(preview, params);
}
private static void showMessage(String message) {
Log.i("Camera", message);
}
@Override public IBinder onBind(Intent intent) { return null; }
}
On Android 4.0 and above (API level >= 14), you 14 can use TextureView to preview the camera stream and 13 make it invisible so as to not show it to 12 the user. Here's how:
First create a class 11 to implement a SurfaceTextureListener that 10 will get the create/update callbacks for 9 the preview surface. This class also takes 8 a camera object as input, so that it can 7 call the camera's startPreview function 6 as soon as the surface is created:
public class CamPreview extends TextureView implements SurfaceTextureListener {
private Camera mCamera;
public CamPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
try{
mCamera.setPreviewTexture(surface);
} catch (IOException t) {}
mCamera.startPreview();
this.setVisibility(INVISIBLE); // Make the surface invisible as soon as it is created
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Put code here to handle texture size change if you want to
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Update your view here!
}
}
You'll 5 also need to implement a callback class 4 to process the preview data:
public class CamCallback implements Camera.PreviewCallback{
public void onPreviewFrame(byte[] data, Camera camera){
// Process the camera data here
}
}
Use the above 3 CamPreview and CamCallback classes to setup 2 the camera in your activity's onCreate() or 1 similar startup function:
// Setup the camera and the preview object
Camera mCamera = Camera.open(0);
CamPreview camPreview = new CamPreview(Context,mCamera);
camPreview.setSurfaceTextureListener(camPreview);
// Connect the preview object to a FrameLayout in your UI
// You'll have to create a FrameLayout object in your UI to place this preview in
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraView);
preview.addView(camPreview);
// Attach a callback for preview
CamCallback camCallback = new CamCallback();
mCamera.setPreviewCallback(camCallback);
There is a way of doing this but it's somewhat 8 tricky. what should be done, is attach a 7 surfaceholder to the window manager from 6 the service
WindowManager wm = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
wm.addView(surfaceview, params);
and then set
surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);
where mHolder is 5 the holder you get from the surface view.
this 4 way, you can play with the surfaceview's 3 alpha, make it completly transparent, but 2 the camera will still get frames.
that's 1 how i do it. hope it helps :)
We solved this problem by using a dummy 9 SurfaceView (not added to actual GUI) in 8 versions below 3.0 (or let's say 4.0 as 7 a camera service on a tablet does not really 6 make sense). In versions >= 4.0 this worked 5 in the emulator only ;( The use of SurfaceTexture 4 (and setSurfaceTexture()) instead of SurfaceView 3 (and setSurfaceView()) worked here. At least 2 this works on Nexus S.
I think this really 1 is a shortcoming of the Android framework.
In the "Working Example by Sam" (Thank you 4 Sam... )
if at istruction "wm.addView(preview, params);"
obtain 3 exception "Unable to add window android.view.ViewRoot 2 -- permission denied for this window type"
resolve 1 by using this permission in AndroidManifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
You can try this working code, This service 5 click front picture, if you want to capture 4 back camera picture then uncomment back 3 camera in code and comment front camera.
Note 2 :- Allow Camera and Storage permission to 1 App And startService from Activity or anywhere.
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
CapturePhoto();
}
private void CapturePhoto() {
Log.d("kkkk","Preparing to take photo");
Camera camera = null;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
int frontCamera = 1;
//int backCamera=0;
Camera.getCameraInfo(frontCamera, cameraInfo);
try {
camera = Camera.open(frontCamera);
} catch (RuntimeException e) {
Log.d("kkkk","Camera not available: " + 1);
camera = null;
//e.printStackTrace();
}
try {
if (null == camera) {
Log.d("kkkk","Could not get camera instance");
} else {
Log.d("kkkk","Got the camera, creating the dummy surface texture");
try {
camera.setPreviewTexture(new SurfaceTexture(0));
camera.startPreview();
} catch (Exception e) {
Log.d("kkkk","Could not set the surface preview texture");
e.printStackTrace();
}
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFileDir=new File("/sdcard/CaptureByService");
if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
pictureFileDir.mkdirs();
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "ServiceClickedPic_" + "_" + date + ".jpg";
String filename = pictureFileDir.getPath() + File.separator + photoFile;
File mainPicture = new File(filename);
try {
FileOutputStream fos = new FileOutputStream(mainPicture);
fos.write(data);
fos.close();
Log.d("kkkk","image saved");
} catch (Exception error) {
Log.d("kkkk","Image could not be saved");
}
camera.release();
}
});
}
} catch (Exception e) {
camera.release();
}
}
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.