Работа с камерой без взаимодействия с пользователем.
Добавлено: 03 май 2013, 20:19
Внимание! Много текста! Но такого простого...
Здесь представлена наработка получения фотографии со всех камер устройства, а также её сохранение, без взаимодействия с пользователем.
Решил наработку вынести в отдельную тему, чтобы она не потерялась. Будет замечательно, если кто-то поможет её допилить до более лучшего состояния.
Как работает:
1. Если API<10 , то работа ведется с одной камерой (ибо до 10 API, нельзя работать с несколькими камерами). Если >10, то проверяется, какие камеры доступны и выставляются boolean флаги.
2. Перед сохранением вызывается метод prepareCameraParameters. Он подготавливает камеру к необходимым параметрам, а именно:
2.1 проверяет угол поворота камеры и выставляет правильную ориентацию. Там, как многие замечали, есть проблемы с тем, что фотография получается повёрнутой. Это фиксится данным методом (правда есть нюансы).
2.2 получает для неё наилучший размер по выбранным параметрам (метод getBestSize(int width, int height, Camera.Parameters parameters)). Допустим, я отправляю 1300, 1300, parameters, он найдет для камеры всевозможные разрешения, которые доступны. Если есть разрешения выше 1300x1300, он возьмет выбранное (1300). Если ниже, то просто возьмет наилучшее. Еще замечу, что чем меньше разрешение, тем меньше получается смазанность. Помните, что в основном 1024х768 вполне хватает. Если в каком-нибудь китайце стоит 40 мегапиксельная камера, то получите очень сильно смазанную фотографию.
3. Теперь главное. Делается снимок первой камеры, после чего мы попадаем в метод onPictureTaken. Тут мы отправляем нашу фотографию на сохранение, в асинхронном запросе, закрываем камеру и делаем снимок в этом же методе на сохранение со второй камеры, если она доступна!. Почему так через пятое место... Создание фотографии - это асинхронный метод. Если мы сразу сфотографируем с первой, а после со второй, то устройству снесет голову, т.к. оно не может работать с двумя камерами одновременно. А это значит, что нам надо ждать, пока отработает первая камера. Это как раз таки данный метод. Поэтому я тут и вызываю создание фотографии второй камеры.
Еще пару моментов.
1. Внутренний класс Image. Так уж получилось, что надо было в асинхронный запрос передать саму фотографию и текущее время. Пришлось сделать отдельный класс для этого. Текущее время - это название фотографии.
2. В коде есть куски других наших классов, а именно Logger и Settings. Логгер - наш собственный логгер ошибок. Уберите просто этот код и запишите в свой логгер. Settings - мы оттуда получаем путь дирректории, куда сохранять. Удалите и напишите свой.
[syntax=java]
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
public class HiddenCamera implements SurfaceHolder.Callback,
Camera.PictureCallback, Camera.AutoFocusCallback {
private Context context;
private SurfaceView surfaceView;
private Camera camera[] = null;
private boolean isCameraFacingBack = false;
private boolean isCameraFacingFront = false;
public HiddenCamera(Context context) {
this.context = context;
camera = new Camera[2];
}
public boolean takePhoto() {
if (Build.VERSION.SDK_INT < 10) {
camera[0] = null;
try {
camera[0] = Camera.open();
} catch (Exception cameraException) {
cameraException.printStackTrace();
Toast.makeText(context, "Неполадки с камерой. Повторите попытку спустя несколько секунд, "
+ "либо перезагрузите устройство",Toast.LENGTH_LONG).show();
return false;
}
saveImage(camera[0]);
} else {
takePhotoFromAllCameras();
}
return true;
}
/*
* Получение всех возможных камер на устройстве.
*/
@TargetApi(10)
private void takePhotoFromAllCameras() {
CameraInfo cameraInfo = new CameraInfo();
int camCount = Camera.getNumberOfCameras();
for ( int camIdx = 0; camIdx < camCount; camIdx++ ) {
camera[camIdx] = null;
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
isCameraFacingBack = true;
}
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
isCameraFacingFront = true;
}
}
try {
//Если камера есть и доступна, то мы вызываем у ОДНОЙ ВСЕГО ЛИШЬ камеры запрос на сохранение фотографии
//и указываем что камера уже отработала, либо просто недоступна. Другой вызов камеры делается
//непосредственно в методе onPictureTaken, чтобы не было конфликтов
if (isCameraFacingBack) {
camera[0] = Camera.open(CameraInfo.CAMERA_FACING_BACK);
saveImage(camera[0]);
isCameraFacingBack = false;
} else if (isCameraFacingFront) {
camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
saveImage(camera[1]);
isCameraFacingFront = false;
}
} catch (Exception cameraException) {
cameraException.printStackTrace();
Toast.makeText(context, "Неполадки с камерой. Повторите попытку спустя несколько секунд, "
+ "либо перезагрузите устройство",Toast.LENGTH_LONG).show();
}
}
private boolean saveImage(Camera camera) {
//Если камеры не существует, то вернется null. Мы пропустим отсутствующую камеру и перейдем к списку других доступных
if (camera==null) {
Logger.log(Logger.ERROR, "HiddenCamera", "some camera returned null", true);
return false;
}
surfaceView = new SurfaceView(context);
try {
prepareCameraParameters(camera);
camera.setPreviewDisplay(surfaceView.getHolder());
} catch (Exception e) {
closeCamera(camera);
Logger.log(Logger.ERROR, "HiddenCamera", "HiddenCamera error", true);
e.printStackTrace();
return false;
}
camera.startPreview();
camera.autoFocus(this);
return true;
}
private void prepareCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
parameters.set("orientation", "portrait");
parameters.set("rotation", 90);
}
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
parameters.set("orientation", "landscape");
parameters.set("rotation", 0);
}
Camera.Size result = getBestSize(1300,1300,parameters);
parameters.setPictureSize(result.width,result.height);
camera.setParameters(parameters);
}
@Override
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera) {
Image image = new Image(System.currentTimeMillis(), paramArrayOfByte);
new SaveInBackground().execute(image);
closeCamera(paramCamera);
if (isCameraFacingFront) {
openFacingFrontCamera();
saveImage(camera[1]);
isCameraFacingFront = false;
}
}
@TargetApi(10)
private void openFacingFrontCamera() {
camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
}
/**
* Метод вызывается при вызове метода camera.autofocus
*/
@Override
public void onAutoFocus(boolean paramBoolean, Camera paramCamera) {
// сохраняем картинку независимо от того, смогли ли мы сфокусироваться или нет
Camera camera = paramCamera;
camera.takePicture(null, null, null, this);
}
/**
* Метод нахождения наилучшего поддерживаемого изображения для данного устройства, по заданным критериям.
* Заданные критерии являются границами максимального получаемого изображения
* @param width
* @param height
* @param parameters
* @return
*/
private Camera.Size getBestSize(int width, int height, Camera.Parameters parameters) {
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPictureSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result = size;
} else {
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
return (result);
}
private void closeCamera(Camera paramCamera) {
if (paramCamera != null) {
paramCamera.setPreviewCallback(null);
paramCamera.stopPreview();
paramCamera.release();
paramCamera = null;
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
@Override
public void surfaceCreated(SurfaceHolder arg0) {}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {}
class SaveInBackground extends AsyncTask<Image, Void, Void> {
@Override
protected Void doInBackground(Image... images) {
FileOutputStream os = null;
for (Image image : images) {
try {
os = new FileOutputStream(String.format(Settings.getAppDir() + "/" + "%d.jpg", image.nameInCurrentTimeMillis));
os.write(image.byteImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
} catch (NullPointerException e3) {
e3.printStackTrace();
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
}
}
return null;
}
}
private class Image {
private byte[] byteImage;
private long nameInCurrentTimeMillis;
Image(long nameInCurrentTimeMillis, byte[] image) {
this.nameInCurrentTimeMillis = nameInCurrentTimeMillis;
this.byteImage = image;
}
}
}
[/syntax]
Сохранение
[syntax=java]
HiddenCamera hiddenCamera = new HiddenCamera(context);
hiddenCamera.takePhoto();
[/syntax]
edit. На Nexus 7 не работает по некоторым причинам. У кого есть потребность в грамотной работе примера на этом устройстве, пишите в ЛС.
Здесь представлена наработка получения фотографии со всех камер устройства, а также её сохранение, без взаимодействия с пользователем.
Решил наработку вынести в отдельную тему, чтобы она не потерялась. Будет замечательно, если кто-то поможет её допилить до более лучшего состояния.
Как работает:
1. Если API<10 , то работа ведется с одной камерой (ибо до 10 API, нельзя работать с несколькими камерами). Если >10, то проверяется, какие камеры доступны и выставляются boolean флаги.
2. Перед сохранением вызывается метод prepareCameraParameters. Он подготавливает камеру к необходимым параметрам, а именно:
2.1 проверяет угол поворота камеры и выставляет правильную ориентацию. Там, как многие замечали, есть проблемы с тем, что фотография получается повёрнутой. Это фиксится данным методом (правда есть нюансы).
2.2 получает для неё наилучший размер по выбранным параметрам (метод getBestSize(int width, int height, Camera.Parameters parameters)). Допустим, я отправляю 1300, 1300, parameters, он найдет для камеры всевозможные разрешения, которые доступны. Если есть разрешения выше 1300x1300, он возьмет выбранное (1300). Если ниже, то просто возьмет наилучшее. Еще замечу, что чем меньше разрешение, тем меньше получается смазанность. Помните, что в основном 1024х768 вполне хватает. Если в каком-нибудь китайце стоит 40 мегапиксельная камера, то получите очень сильно смазанную фотографию.
3. Теперь главное. Делается снимок первой камеры, после чего мы попадаем в метод onPictureTaken. Тут мы отправляем нашу фотографию на сохранение, в асинхронном запросе, закрываем камеру и делаем снимок в этом же методе на сохранение со второй камеры, если она доступна!. Почему так через пятое место... Создание фотографии - это асинхронный метод. Если мы сразу сфотографируем с первой, а после со второй, то устройству снесет голову, т.к. оно не может работать с двумя камерами одновременно. А это значит, что нам надо ждать, пока отработает первая камера. Это как раз таки данный метод. Поэтому я тут и вызываю создание фотографии второй камеры.
Еще пару моментов.
1. Внутренний класс Image. Так уж получилось, что надо было в асинхронный запрос передать саму фотографию и текущее время. Пришлось сделать отдельный класс для этого. Текущее время - это название фотографии.
2. В коде есть куски других наших классов, а именно Logger и Settings. Логгер - наш собственный логгер ошибок. Уберите просто этот код и запишите в свой логгер. Settings - мы оттуда получаем путь дирректории, куда сохранять. Удалите и напишите свой.
[syntax=java]
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
public class HiddenCamera implements SurfaceHolder.Callback,
Camera.PictureCallback, Camera.AutoFocusCallback {
private Context context;
private SurfaceView surfaceView;
private Camera camera[] = null;
private boolean isCameraFacingBack = false;
private boolean isCameraFacingFront = false;
public HiddenCamera(Context context) {
this.context = context;
camera = new Camera[2];
}
public boolean takePhoto() {
if (Build.VERSION.SDK_INT < 10) {
camera[0] = null;
try {
camera[0] = Camera.open();
} catch (Exception cameraException) {
cameraException.printStackTrace();
Toast.makeText(context, "Неполадки с камерой. Повторите попытку спустя несколько секунд, "
+ "либо перезагрузите устройство",Toast.LENGTH_LONG).show();
return false;
}
saveImage(camera[0]);
} else {
takePhotoFromAllCameras();
}
return true;
}
/*
* Получение всех возможных камер на устройстве.
*/
@TargetApi(10)
private void takePhotoFromAllCameras() {
CameraInfo cameraInfo = new CameraInfo();
int camCount = Camera.getNumberOfCameras();
for ( int camIdx = 0; camIdx < camCount; camIdx++ ) {
camera[camIdx] = null;
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
isCameraFacingBack = true;
}
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
isCameraFacingFront = true;
}
}
try {
//Если камера есть и доступна, то мы вызываем у ОДНОЙ ВСЕГО ЛИШЬ камеры запрос на сохранение фотографии
//и указываем что камера уже отработала, либо просто недоступна. Другой вызов камеры делается
//непосредственно в методе onPictureTaken, чтобы не было конфликтов
if (isCameraFacingBack) {
camera[0] = Camera.open(CameraInfo.CAMERA_FACING_BACK);
saveImage(camera[0]);
isCameraFacingBack = false;
} else if (isCameraFacingFront) {
camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
saveImage(camera[1]);
isCameraFacingFront = false;
}
} catch (Exception cameraException) {
cameraException.printStackTrace();
Toast.makeText(context, "Неполадки с камерой. Повторите попытку спустя несколько секунд, "
+ "либо перезагрузите устройство",Toast.LENGTH_LONG).show();
}
}
private boolean saveImage(Camera camera) {
//Если камеры не существует, то вернется null. Мы пропустим отсутствующую камеру и перейдем к списку других доступных
if (camera==null) {
Logger.log(Logger.ERROR, "HiddenCamera", "some camera returned null", true);
return false;
}
surfaceView = new SurfaceView(context);
try {
prepareCameraParameters(camera);
camera.setPreviewDisplay(surfaceView.getHolder());
} catch (Exception e) {
closeCamera(camera);
Logger.log(Logger.ERROR, "HiddenCamera", "HiddenCamera error", true);
e.printStackTrace();
return false;
}
camera.startPreview();
camera.autoFocus(this);
return true;
}
private void prepareCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
parameters.set("orientation", "portrait");
parameters.set("rotation", 90);
}
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
parameters.set("orientation", "landscape");
parameters.set("rotation", 0);
}
Camera.Size result = getBestSize(1300,1300,parameters);
parameters.setPictureSize(result.width,result.height);
camera.setParameters(parameters);
}
@Override
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera) {
Image image = new Image(System.currentTimeMillis(), paramArrayOfByte);
new SaveInBackground().execute(image);
closeCamera(paramCamera);
if (isCameraFacingFront) {
openFacingFrontCamera();
saveImage(camera[1]);
isCameraFacingFront = false;
}
}
@TargetApi(10)
private void openFacingFrontCamera() {
camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
}
/**
* Метод вызывается при вызове метода camera.autofocus
*/
@Override
public void onAutoFocus(boolean paramBoolean, Camera paramCamera) {
// сохраняем картинку независимо от того, смогли ли мы сфокусироваться или нет
Camera camera = paramCamera;
camera.takePicture(null, null, null, this);
}
/**
* Метод нахождения наилучшего поддерживаемого изображения для данного устройства, по заданным критериям.
* Заданные критерии являются границами максимального получаемого изображения
* @param width
* @param height
* @param parameters
* @return
*/
private Camera.Size getBestSize(int width, int height, Camera.Parameters parameters) {
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPictureSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result = size;
} else {
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
return (result);
}
private void closeCamera(Camera paramCamera) {
if (paramCamera != null) {
paramCamera.setPreviewCallback(null);
paramCamera.stopPreview();
paramCamera.release();
paramCamera = null;
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
@Override
public void surfaceCreated(SurfaceHolder arg0) {}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {}
class SaveInBackground extends AsyncTask<Image, Void, Void> {
@Override
protected Void doInBackground(Image... images) {
FileOutputStream os = null;
for (Image image : images) {
try {
os = new FileOutputStream(String.format(Settings.getAppDir() + "/" + "%d.jpg", image.nameInCurrentTimeMillis));
os.write(image.byteImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
} catch (NullPointerException e3) {
e3.printStackTrace();
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
}
}
return null;
}
}
private class Image {
private byte[] byteImage;
private long nameInCurrentTimeMillis;
Image(long nameInCurrentTimeMillis, byte[] image) {
this.nameInCurrentTimeMillis = nameInCurrentTimeMillis;
this.byteImage = image;
}
}
}
[/syntax]
Сохранение
[syntax=java]
HiddenCamera hiddenCamera = new HiddenCamera(context);
hiddenCamera.takePhoto();
[/syntax]
edit. На Nexus 7 не работает по некоторым причинам. У кого есть потребность в грамотной работе примера на этом устройстве, пишите в ЛС.