Работа с камерой без взаимодействия с пользователем.

Ответить
Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Работа с камерой без взаимодействия с пользователем.

Сообщение Mikhail_dev » 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 не работает по некоторым причинам. У кого есть потребность в грамотной работе примера на этом устройстве, пишите в ЛС.
Последний раз редактировалось Mikhail_dev 12 июл 2013, 19:34, всего редактировалось 6 раз.

YuriK
Сообщения: 29
Зарегистрирован: 13 апр 2013, 12:57
Откуда: Санкт-Петербург

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение YuriK » 04 май 2013, 02:18

Итоги первых тестов: на старом девайсе с 2.2 и одной задней камерой сработало без проблем. (во втором блоке правда маленькая опечатка, не Hidden, а HiddenCamera конечно в первой строке, но это мелочь)

На Нексусе 7 пока не работает. Сначала спотыкалось о строку camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
Фишка в том, что хотя она там и фронтальная, но ее id там равно 0, а CameraInfo.CAMERA_FACING_FRONT это 1
После замены этого дела на camera[1] = Camera.open(0); этот этап стал проходить.
Но теперь застревает где-то после saveImage, при этом все try проходит, просто не сохраняет ничего и валит сервис камеры так, что девайс приходится перезагружать. Вот лог этой ситуации:

Код: Выделить всё

05-04 02:41:24.740: INFO/CameraClient(128): Opening camera 0
05-04 02:41:24.740: INFO/NvOmxCamera(128): HAL_camera_device_open: open camera 0
05-04 02:41:24.740: ERROR/(128): ioctl SetWhiteBalance failed: -1
05-04 02:41:24.740: ERROR/NvOmxCameraSettingsParser(128): Param type 70 not supported
05-04 02:41:24.740: INFO/NvOmxCamera(128): HAL_camera_device_open: opened camera 0 (0x40f693d0)
05-04 02:41:24.740: DEBUG/HC(3158): SaveImage() started
05-04 02:41:24.750: DEBUG/Camera(3158): app passed NULL surface
05-04 02:41:24.780: DEBUG/NvOsDebugPrintf(128): NvMMLiteBlockCreate : Block : BlockType = 1
05-04 02:41:24.780: DEBUG/NvOsDebugPrintf(128): NvMMLiteJPEGEncGetBufferRequirements : BufferSize 1843200
05-04 02:41:24.810: DEBUG/(128): Camera fd open as: 130
05-04 02:41:25.260: WARN/System.err(1543): LOG: Warning Unknown dock level ignored.
05-04 02:41:25.290: DEBUG/NvOsDebugPrintf(128): Image Physically rotated in DZ
05-04 02:41:25.290: DEBUG/NvOsDebugPrintf(128): NvMMExif_Orientation_0_Degrees= 1
05-04 02:41:25.320: ERROR/NvOmxCamera(128): OMX_ERRORTYPE android::NvOmxCamera::getCameraStereoMode(NvxComponent*, NvOmxCameraUserStereoMode&): Error: invalid NVX mode 0.
05-04 02:41:25.320: ERROR/NvOmxCamera(128): OMX_ERRORTYPE android::NvOmxCamera::getCameraStereoModeAndCaptureInfo(NvxComponent*, NvOmxCameraUserStereoMode&, NVX_STEREOCAPTUREINFO&): getCameraStereoMode failed with 0x00000000
05-04 02:41:25.320: DEBUG/NvOsDebugPrintf(128): NvMMLiteJPEGEncSetAttribute: Incorrect value 0 for stereo capture type
05-04 02:41:25.320: ERROR/NvOmxCameraSettings(128): OMX_ERRORTYPE android::programStereoInfo(OMX_HANDLETYPE, const NVX_STEREOCAPTUREINFO&, android::NvxWrappers*): pNvxWrappers->OMX_SetConfigIL failed with 0x80001005
Вот здесь я видел что человек решает проблемы на Нексусе вызывая release() сразу после open, хотя не очень пока представляю как мы можем использовать это в нашем случае.
Тем не менее, просто для эксперимента попробовал освободить камеру сразу после autoFocus в saveImage()

Код: Выделить всё

camera.autoFocus(this);
camera.stopPreview(); // my
camera.release(); // my
Эффектом этого стало, что камера перестала убиваться, но снимка я так и не получил, а лог был такой:

Код: Выделить всё

05-04 02:29:28.590: INFO/CameraClient(128): Opening camera 0
05-04 02:29:28.590: INFO/NvOmxCamera(128): HAL_camera_device_open: open camera 0
05-04 02:29:28.610: ERROR/(128): ioctl SetWhiteBalance failed: -1
05-04 02:29:28.610: ERROR/NvOmxCameraSettingsParser(128): Param type 70 not supported
05-04 02:29:28.610: INFO/NvOmxCamera(128): HAL_camera_device_open: opened camera 0 (0x425b0f10)
05-04 02:29:28.610: DEBUG/HC(2501): SaveImage() started
05-04 02:29:28.620: DEBUG/Camera(2501): app passed NULL surface
05-04 02:29:28.640: DEBUG/NvOsDebugPrintf(128): NvMMLiteBlockCreate : Block : BlockType = 1
05-04 02:29:28.640: DEBUG/NvOsDebugPrintf(128): NvMMLiteJPEGEncGetBufferRequirements : BufferSize 1843200
05-04 02:29:28.670: DEBUG/(128): Camera fd open as: 130
05-04 02:29:29.150: DEBUG/NvOsDebugPrintf(128): Image Physically rotated in DZ
05-04 02:29:29.150: DEBUG/NvOsDebugPrintf(128): NvMMExif_Orientation_0_Degrees= 1
05-04 02:29:29.250: DEBUG/(128): Camera fd close (MI1040)
05-04 02:29:29.270: INFO/CameraClient(128): Destroying camera 0
Может это поможет кому-то найти решение, не знаю.
Вот еще один вопрос по теме камеры на Nexus7 на SOF, у меня есть ощущение, что в самом нижнем ответе, есть что-то полезное, и это связано со строкой в логе "DEBUG/Camera(2501): app passed NULL surface" но до конца его решение прорубить не могу и пока сдаюсь, голова уже не работает)

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение Mikhail_dev » 04 май 2013, 10:04

Я тут по коду глянул своему, в нём нету привязки
На Нексусе 7 пока не работает. Сначала спотыкалось о строку camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
Фишка в том, что хотя она там и фронтальная, но ее id там равно 0, а CameraInfo.CAMERA_FACING_FRONT это 1
к этому делу. тут неважно, какая она по счету идет. брось лог с первоначальной ошибкой, посмотрим на что она ругается.
(во втором блоке правда маленькая опечатка, не Hidden, а HiddenCamera конечно в первой строке, но это мелочь)
ага, пофиксил. Спасибо.

YuriK
Сообщения: 29
Зарегистрирован: 13 апр 2013, 12:57
Откуда: Санкт-Петербург

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение YuriK » 04 май 2013, 10:38

no-- писал(а):Я тут по коду глянул своему, в нём нету привязки
На Нексусе 7 пока не работает. Сначала спотыкалось о строку camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);Фишка в том, что хотя она там и фронтальная, но ее id там равно 0, а CameraInfo.CAMERA_FACING_FRONT это 1
к этому делу. тут неважно, какая она по счету идет. брось лог с первоначальной ошибкой, посмотрим на что она ругается.
Ну почему же нет. Как раз по твоему коду вполне видно, что при проверке камер в takePhotoFromAllCameras, привязка как раз есть.
Ты сначала правильно выясняешь, что cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT а потом это ведет к тому, что программа пытается открыть камеру по этому же флагу (Camera.open(CameraInfo.CAMERA_FACING_FRONT)) и это уже ведет к ошибке и выбрасывает исключение и Тост: "Неполадки с камерой. Повторите попытку...".
Вот лог:

Код: Выделить всё

05-04 11:25:12.860: DEBUG/HC(8231): takePhotoFromAllCameras count=1
05-04 11:25:12.870: DEBUG/HC(8231): cameraInfo.facing =1
05-04 11:25:12.870: ERROR/CameraService(128): CameraService::connect X (pid 8231) rejected (invalid cameraId 1).
05-04 11:25:12.870: WARN/System.err(8231): java.lang.RuntimeException: Fail to connect to camera service
05-04 11:25:12.870: WARN/System.err(8231): at android.hardware.Camera.native_setup(Native Method)
05-04 11:25:12.870: WARN/System.err(8231): at android.hardware.Camera.<init>(Camera.java:340)
05-04 11:25:12.870: WARN/System.err(8231): at android.hardware.Camera.open(Camera.java:302)
05-04 11:25:12.870: WARN/System.err(8231): at com.example.HiddenCam.HiddenCamera.takePhotoFromAllCameras(HiddenCamera.java:104)
05-04 11:25:12.870: WARN/System.err(8231): at com.example.HiddenCam.HiddenCamera.takePhoto(HiddenCamera.java:57)
05-04 11:25:12.870: WARN/System.err(8231): at com.example.HiddenCam.StartActivity$1.onClick(StartActivity.java:23)
05-04 11:25:12.870: WARN/System.err(8231): at android.view.View.performClick(View.java:4204)
05-04 11:25:12.870: WARN/System.err(8231): at android.view.View$PerformClick.run(View.java:17355)
05-04 11:25:12.870: WARN/System.err(8231): at android.os.Handler.handleCallback(Handler.java:725)
05-04 11:25:12.870: WARN/System.err(8231): at android.os.Handler.dispatchMessage(Handler.java:92)
05-04 11:25:12.870: WARN/System.err(8231): at android.os.Looper.loop(Looper.java:137)
05-04 11:25:12.870: WARN/System.err(8231): at android.app.ActivityThread.main(ActivityThread.java:5041)
05-04 11:25:12.870: WARN/System.err(8231): at java.lang.reflect.Method.invokeNative(Native Method)
05-04 11:25:12.870: WARN/System.err(8231): at java.lang.reflect.Method.invoke(Method.java:511)
05-04 11:25:12.870: WARN/System.err(8231): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
05-04 11:25:12.870: WARN/System.err(8231): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
05-04 11:25:12.870: WARN/System.err(8231): at dalvik.system.NativeStart.main(Native Method)
У тебя всё правильно и логично, нелогично в Nexus 7 :)
И с этим то мне всё ясно, нужно просто вносить в логику проверки специальные условия для N7, а вот что там творится дальше в конце saveImage, а если точнее, то при вызове конкретно camera.takePicture(...) - непонятно.

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение Mikhail_dev » 04 май 2013, 11:20

Имел ввиду, что в массиве есть привязка, но это не играет роли. Если камеры нет, то данный номер массива будет пропущен. подскажи еще что за строка
com.example.HiddenCam.HiddenCamera.takePhotoFromAllCameras(HiddenCamera.java:104)
? А то нумерация у нас не совпадает.

YuriK
Сообщения: 29
Зарегистрирован: 13 апр 2013, 12:57
Откуда: Санкт-Петербург

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение YuriK » 04 май 2013, 15:24

no-- писал(а):...подскажи еще что за строка
com.example.HiddenCam.HiddenCamera.takePhotoFromAllCameras(HiddenCamera.java:104)
Это как раз та самая строка, о которой шла речь:

Код: Выделить всё

camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение Mikhail_dev » 04 май 2013, 15:45

Я так понимаю что это баг самого нексуса.
Вот в коде CameraService по поиску строки "rejected (invalid cameraId" я нашел такой метод.
[syntax=java]sp<ICamera> CameraService::connect(
const sp<ICameraClient>& cameraClient, int cameraId) {

int callingPid = getCallingPid();

[...]

if (cameraId < 0 || cameraId >= mNumberOfCameras) {
LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
callingPid, cameraId);
return NULL;
}[/syntax]
Значит нексус выдаёт неадекватный ID камеры. Либо как пишет человек тут, переменная mNumberOfCameras, которая как раз стоит в условии, имеет похоже значение 0. Поэтому думаю и происходит выход за пределы условия, с индексом 1. Как он пишет, камера похоже не зарегистрировалась.
Попробуй посмотреть сколько камер всего доступно. У меня пока нету под рукой андроида с эклипсом. Я смотрю что там должен быть в классе метод getNumberOfCameras. Он как раз и инициализирует ту переменную. Посмотри что он выдаст.

YuriK
Сообщения: 29
Зарегистрирован: 13 апр 2013, 12:57
Откуда: Санкт-Петербург

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение YuriK » 04 май 2013, 16:31

no-- писал(а):Попробуй посмотреть сколько камер всего доступно. У меня пока нету под рукой андроида с эклипсом. Я смотрю что там должен быть в классе метод getNumberOfCameras. Он как раз и инициализирует ту переменную. Посмотри что он выдаст.
Уже смотрел. 1 камера, как и ожидается.
У меня в одном из логов первой шла строчка

Код: Выделить всё

05-04 11:25:12.860: DEBUG/HC(8231): takePhotoFromAllCameras count=1
это я как раз результат getNumberOfCameras проверял.

что "нексус выдаёт неадекватный ID камеры", так я собственно о том же. если точнее, то он принимает неадекватный ID камеры в метод open(id)

Аватара пользователя
Mikhail_dev
Сообщения: 2386
Зарегистрирован: 09 янв 2012, 14:45
Откуда: Самара

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение Mikhail_dev » 04 май 2013, 22:57

Теперь понял про что ты мне так давно говоришь =)
Попробуй поменять метод
[syntax=java]
@TargetApi(10)
private void openFacingFrontCamera() {
try {
camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
} catch (Exception e) {
camera[1] = Camera.open(0);
}
}
[/syntax]
и там, где открывается фронтальная камера, открывать через этот метод. думаю шаманство с release тут ни к чему. Думаю возможна будет ошибка, потому как возможно камера после ошибки будет занята, тогда придется наверно копаться в packagemanager и узнавать, что за телефон.

shephord
Сообщения: 4
Зарегистрирован: 24 май 2021, 08:10

Re: Работа с камерой без взаимодействием с пользователем.

Сообщение shephord » 24 май 2021, 08:26

YuriK писал(а):
04 май 2013, 10:38
no-- писал(а):Я тут по коду глянул своему, в нём нету привязки
На Нексусе 7 пока не работает. Сначала спотыкалось о строку camera[1] = Camera.open(CameraInfo.CAMERA_FACING_FRONT);Фишка в том, что хотя она там и фронтальная, но ее id там равно 0, а CameraInfo.CAMERA_FACING_FRONT это 1
к этому делу. тут неважно, какая она по счету идет. брось лог с первоначальной ошибкой, посмотрим на что она ругается.
Ну почему же e chat нет. Как раз по твоему коду вполне видно, что при проверке камер в takePhotoFromAllCameras, привязка как раз есть.
Ты сначала правильно выясняешь, что cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT а потом это ведет к тому, что программа пытается открыть камеру по этому же флагу (Camera.open(CameraInfo.CAMERA_FACING_FRONT)) и это omegle chat уже ведет к ошибке и выбрасывает исключение и Тост: "Неполадки с камерой. Повторите попытку...".
Вот лог:

Код: Выделить всё

05-04 11:25:12.860: DEBUG/HC(8231): takePhotoFromAllCameras count=1
05-04 11:25:12.870: DEBUG/HC(8231): cameraInfo.facing =1
05-04 11:25:12.870: ERROR/CameraService(128): CameraService::connect X (pid 8231) rejected (invalid cameraId 1).
05-04 11:25:12.870: WARN/System.err(8231): java.lang.RuntimeException: Fail to connect to camera service
05-04 11:25:12.870: WARN/System.err(8231): at android.hardware.Camera.native_setup(Native Method)
05-04 11:25:12.870: WARN/System.err(8231): at android.hardware.Camera.<init>(Camera.java:340)
05-04 11:25:12.870: WARN/System.err(8231): at android.hardware.Camera.open(Camera.java:302)
05-04 11:25:12.870: WARN/System.err(8231): at com.example.HiddenCam.HiddenCamera.takePhotoFromAllCameras(HiddenCamera.java:104)
05-04 11:25:12.870: WARN/System.err(8231): at com.example.HiddenCam.HiddenCamera.takePhoto(HiddenCamera.java:57)
05-04 11:25:12.870: WARN/System.err(8231): at com.example.HiddenCam.StartActivity$1.onClick(StartActivity.java:23)
05-04 11:25:12.870: WARN/System.err(8231): at android.view.View.performClick(View.java:4204)
05-04 11:25:12.870: WARN/System.err(8231): at android.view.View$PerformClick.run(View.java:17355)
05-04 11:25:12.870: WARN/System.err(8231): at android.os.Handler.handleCallback(Handler.java:725)
05-04 11:25:12.870: WARN/System.err(8231): at android.os.Handler.dispatchMessage(Handler.java:92)
05-04 11:25:12.870: WARN/System.err(8231): at android.os.Looper.loop(Looper.java:137)
05-04 11:25:12.870: WARN/System.err(8231): at android.app.ActivityThread.main(ActivityThread.java:5041)
05-04 11:25:12.870: WARN/System.err(8231): at java.lang.reflect.Method.invokeNative(Native Method)
05-04 11:25:12.870: WARN/System.err(8231): at java.lang.reflect.Method.invoke(Method.java:511)
05-04 11:25:12.870: WARN/System.err(8231): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
05-04 11:25:12.870: WARN/System.err(8231): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
05-04 11:25:12.870: WARN/System.err(8231): at dalvik.system.NativeStart.main(Native Method)
У тебя всё правильно и логично, нелогично в Nexus 7 :)
И с этим то мне всё ясно, нужно просто вносить в логику проверки специальные условия для N7, а вот что там творится дальше в конце saveImage, а если точнее, то при вызове конкретно camera.takePicture(...) - непонятно.
Yes, you are right. There is no issue with code.
I also think that you getting this cause because of your device camera.
So please try again.

mamba
Сообщения: 2
Зарегистрирован: 12 июл 2021, 11:00

Re: Работа с камерой без взаимодействия с пользователем.

Сообщение mamba » 12 июл 2021, 11:04

Mikhail_dev писал(а):
03 май 2013, 20:19
Внимание! Много текста! Но такого простого...
Здесь представлена наработка получения фотографии со всех камер устройства, а также её сохранение, без взаимодействия с пользователем.
Решил наработку вынести в отдельную тему, чтобы она не потерялась. Будет замечательно, если кто-то поможет её допилить до более лучшего состояния.
Как работает:
1. Если API<10 , то работа ведется с одной камерой (ибо до 10 API, нельзя работать с несколькими камерами). Если >10, то проверяется, какие камеры доступны и выставляются boolean флаги. chatiw
2. Перед сохранением вызывается метод prepareCameraParameters. Он подготавливает камеру к необходимым параметрам, а именно:
2.1 проверяет угол поворота камеры и выставляет правильную ориентацию. Там, как многие замечали, есть проблемы с тем, что фотография получается повёрнутой. Это фиксится данным методом (правда есть нюансы) camzap.
2.2 получает для неё наилучший размер по выбранным параметрам (метод getBestSize(int width, int height, Camera.Parameters parameters)). Допустим, я отправляю 1300, 1300, parameters, он найдет для камеры всевозможные click speed test разрешения, которые доступны. Если есть разрешения выше 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 не работает по некоторым причинам. У кого есть потребность в грамотной работе примера на этом устройстве, пишите в ЛС.
Последствия основных тестов: на старом гаджете с 2.2 и тыльной камерой все работало без проблем. (в следующем поле на самом деле небольшая ошибка, не замаскированная, а скрытая камера, конечно, в основной строке, но это мелочь)

Ответить