Съемка с камеры без предварительного просмотра
Я пишу приложение для Android 1.5, которое запускается сразу после загрузки. Это же Service и следует сделать снимок без предварительного просмотра. Это приложение будет регистрировать плотность света в некоторых районах, что. Я смог сделать снимок, но снимок был черным.
после исследования в течение длительного времени, я наткнулся на баг нить об этом. Если вы не создадите предварительный просмотр, изображение будет черным, так как Android-камера нуждается в предварительном просмотре для настройки экспозиции и фокусировки. Я создал SurfaceView и слушатель, но onSurfaceCreated() событие не уволили.
Я думаю, причина в том, что поверхность не создается визуально. Я также видел некоторые примеры вызова камеры статически с MediaStore.CAPTURE_OR_SOMETHING который делает снимок и сохраняет в нужной папке с двумя строками кода, но он не делает снимок тоже.
мне нужно использовать IPC и bindService() чтобы вызвать эту функцию? Или есть альтернативный способ добиться этого?
9 ответов:
Это действительно странно, что камера на платформе android не может передавать видео, пока он не дал действительную поверхность предварительного просмотра. похоже, что архитекторы платформы вообще не думали о сторонних приложениях для потоковой передачи видео. даже для случая дополненной реальности изображение может быть представлено как своего рода визуальная замена, а не поток камеры в реальном времени.
в любом случае, вы можете просто изменить размер поверхности предварительного просмотра до 1x1 пикселей и положить его где-то в углу виджет (визуальный элемент). пожалуйста, обратите внимание-изменить размер поверхности предварительного просмотра, а не размер кадра камеры.
конечно, такой трюк не устраняет нежелательную потоковую передачу данных (для предварительного просмотра), которая потребляет некоторые системные ресурсы и батарею.
Я нашел ответ на это Android Camera Docs.
Примечание: Можно использовать
MediaRecorderбез создания камеры сначала просмотрите и пропустите первые несколько шагов этого процесса. Однако, поскольку пользователи обычно предпочитают видеть предварительный просмотр перед началом запись, этот процесс здесь не обсуждается.вы можете найти пошаговые инструкции по ссылке выше. После инструкций он будет указывать цитату, которая Я приводил выше.
на самом деле это возможно, но вы должны подделать предварительный просмотр с помощью фиктивного SurfaceView
SurfaceView view = new SurfaceView(this); c.setPreviewDisplay(view.getHolder()); c.startPreview(); c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);более подробная информация найдена в my блоге.
обновление 9/21/11: по-видимому, это не работает для каждого устройства Android.
с фотографией
получить эту работу, прежде чем пытаться скрыть предварительный просмотр.
- правильно настроить предварительный просмотр
- использовать
SurfaceView(совместимость до Android-4.0) илиSurfaceTexture(Android 4+, можно сделать прозрачным)- установить и инициализировать его, прежде чем принимать фото
- ждать
SurfaceView' sSurfaceHolder(черезgetHolder()) отчетsurfaceCreated()илиTextureViewв отчетеonSurfaceTextureAvailableв своемSurfaceTextureListenerперед установкой и инициализация предварительного просмотра.- убедитесь, что предварительный просмотр виден:
- добавить его в
WindowManager- убедитесь, что его размер макета составляет не менее 1x1 пикселей (вы можете начать, сделав его
MATCH_PARENTxMATCH_PARENTдля тестирования)- убедитесь, что его видимость
View.VISIBLE(который, кажется, по умолчанию, если вы не укажете его)- убедитесь, что вы используете
FLAG_HARDWARE_ACCELERATEDнаLayoutParamsесли этоTextureView.- использовать
takePicture'S JPEG обратный вызов, так как документация говорит, что другие обратные вызовы не поддерживаются на всех устройствахустранение неисправностей
- если
surfaceCreated/onSurfaceTextureAvailableне называется, тоSurfaceView/TextureViewвероятно, не отображается.- если
takePictureсбой, сначала убедитесь, что предварительный просмотр работает правильно. Вы можете удалить свойtakePictureвызовите и запустите предварительный просмотр, чтобы увидеть, отображается ли он на экране.- если изображение темнее, чем должно быть, вам может потребоваться задержка примерно на секунду перед вызовом
takePictureчтобы у камеры было время настроить экспозицию после начала предварительного просмотра.скрытие просмотра
сделать предварительный просмотр
View1x1 размер, чтобы свести к минимуму его видимость (или попробуйте 8x16 для возможно большей надежности)new WindowManager.LayoutParams(1, 1, /*...*/)переместить предварительный просмотр из центра, чтобы уменьшить его заметность:
new WindowManager.LayoutParams(width, height, Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)сделать предварительный просмотр прозрачным (работает только для
TextureView)WindowManager.LayoutParams params = new WindowManager.LayoutParams( width, height, /*...*/ PixelFormat.TRANSPARENT); params.alpha = 0;рабочий пример (проверено на 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; } }
есть способ сделать это, но это несколько сложнее. что нужно сделать, это прикрепить surfaceholder к оконному менеджеру из сервиса
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);и затем установить
surfaceview.setZOrderOnTop(true); mHolder.setFormat(PixelFormat.TRANSPARENT);где mHolder-это держатель, который вы получаете из вида поверхности.
таким образом, вы можете играть с Альфа surfaceview, сделать его полностью прозрачным, но камера все равно получит кадры.
вот как я это делаю. надеюсь, это поможет:)
на Android 4.0 и выше (уровень API >= 14), вы можете использовать TextureView для предварительного просмотра потока камеры и сделать его невидимым, чтобы не показывать его пользователю. Вот как:
сначала создайте класс для реализации SurfaceTextureListener, который получит обратные вызовы create/update для поверхности предварительного просмотра. Этот класс также принимает объект камеры в качестве входных данных, так что он может вызвать функцию startPreview камеры, как только поверхность создано:
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! } }вам также необходимо реализовать класс обратного вызова для обработки данных предварительного просмотра:
public class CamCallback implements Camera.PreviewCallback{ public void onPreviewFrame(byte[] data, Camera camera){ // Process the camera data here } }используйте вышеуказанные классы CamPreview и CamCallback для настройки камеры в onCreate () вашей активности или аналогичной функции запуска:
// 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);
мы решили эту проблему с помощью фиктивного SurfaceView (не добавленного к фактическому GUI) в версиях ниже 3.0 (или, скажем, 4.0 как услуга камеры на планшете на самом деле не имеет смысла). В версиях >= 4.0 это работало только в эмуляторе ;( Использование SurfaceTexture (и setSurfaceTexture ()) вместо SurfaceView (и setSurfaceView ()) работало здесь. По крайней мере, это работает на Nexus S.
Я думаю, что это действительно недостаток платформы Android.
в "рабочем примере Сэма" (спасибо Сэм... )
если на istruction " wm.addView (preview, params);"
получить исключение " невозможно добавить окно android.вид.ViewRoot -- разрешение запрещено для этого типа окна"
разрешить с помощью этого разрешения в AndroidManifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
вы можете попробовать этот рабочий код , этот сервис нажмите переднее изображение , если вы хотите захватить изображение задней камеры, а затем раскомментировать backCamera в коде и прокомментировать frontCamera .
Примечание: - разрешить разрешение камеры и хранения для приложения и startService от деятельности или в любом месте.
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(); } } }
Comments