Эта статья представляет собой перевод части этого официального мануала. В ней описано, как создать приложение-камеру, которое может снимать фото и видео
Шаги по конструированию приложения:
• Обнаружить и получить доступ к камере — Проверяем наличие камеры на устройстве и получаем к ней доступ
• Создание класса предпросмотра — Объявление класса предпросмотра, который будет наследником SurfaceView и реализовывать интерфейс SurfaceHolder. Этот класс сможет брать видео с камеры в реальном времени
• Создать Preview Layout — Layout в пользовательском интерфейсе, куда вы будете помещать окно предпросмотра
• Поставить слушатели событий — Создать методы, обрабатывающие действия пользователя
• Capture and Save Files — Написать код для захвата медиафайлов и сохранения их
• Освобожение камеры - После использования камеры, нужно объявить, что вы ее больше не используете, чтобы другое приложение могло получить к ней доступ
Далее рассказано, как получить доступ к камере, снять фото или видео, и освободить ресурс после его использования
Внимание: обязательно вызывайте метод camera.release() после ее использования, иначе другие попытки получить в ней доступ завершатся с ошибкой
Если вы не указали в манифесте permission доступа к камере, ее наличие можно проверить при выполнении программы при помощи PackageManager.hasSystemFeature(), как показано ниже:
private boolean checkCameraHardware(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ // камера присутствует return true; } else { // камера отсутствует return false; } }
Android устройства могут иметь более одной камеры, например: задняя для снятия фото и видео, передняя — для видеозвонков. API Level 9 и выше позволяют узнать количество камер на устройстве с помощью метода Camera.getNumberOfCameras()
После того, как вы убедились в наличии камеры на устройстве пользователя, нужно получить к ней доступ — определить объект класса Camera
Для получения доступа к первой камере, используйте Camera.open() и будьте готовы обрабатывать исключения, как в коде внизу:
/** Безопасный способ получить доступ к камере */ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); // попытка получить Camera экземпляр } catch (Exception e){ // Камера недоступна (или не существует) } return c; }
Внимание: Всегда помещайте код с методом Camera.open() в блок try-cacth и обрабатывайте исключения. В противном случае ваше приложение может «упасть»
При использовании API версии 9 и выше, можно получить доступ к какой-либо камере (когда их больше 1-й) по ее номеру: Camera.open(int). Код сверху в таком случае дает доступ к задней камере устройства
Чтобы представить пользователю информацию о том, что находится в поле зрения камеры, создают класс предпросмотра. Класс предпросмотра может захватывать изображение с камеры и показывать его пользователю в реальном времени.
Приведенный ниже код показывает, как создать класс предпросмотра, расширение SurfaceView. Этот класс реализует интерфейс SurfaceHolder.Callback
/** A basic Camera preview 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; // Установка SurfaceHolder.Callback, чтобы реагировать на открытие, изменения, и разрушение Surface 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) { //Surface создан, теперь нужно указать камере. Куда поместить preview try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // Пусто. Позаботьтесь об освобождении ресурса камеры в Activity } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Если preview было изменено // Позаботьтесь о прекращении предпросмотра до переопределения данных окна предпросмотра if (mHolder.getSurface() == null){ // preview surface не существует return; } // сначала остановим предпросмотр try { mCamera.stopPreview(); } catch (Exception e){ // игнорируем: попытка остановить несуществующий предпросмотр } //Здесь изменяются параметры предпросмотра // начать предпросмотр с новыми настройками try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
Если вы хотите кастомизировать размер окна предпросмотра, сделайте это в методе SurfaceChanged , как показано выше. Получить значения допустимых параметров можно методом getSupportedPreviewSizes(). Не устанавливайте произвольные значения с помощью метода setPreviewSize()
Помещение preview на layout
Окно предпросмотра камеры, как и все остальные элементы пользовательского интерфейса, должно быть помещено на Layout, чтобы пользователь мог его увидеть.
Приведенный XML-код показывает пример пользовательского интерфейса, включающего FrameLayout, который будет служить контейнером для класса предпросмотра — его поместим туда программно с помощью метода FrameLayout.addView().
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
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>
Также желательно запретить приложению переходить в портретную ориентацию с помощью следующего кода в манифесте приложения
<activity android:name=".CameraActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
<!-- заставляем Activity всегда находится в ландшафтном режиме -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
В коде Activity нужно будет добавить экземпляр класса предпросмотра на Framelayout. Также нужно обеспечить освобождение ресурса камеры, если пользователь перестает использовать Activity. Пример, приведенный ниже, показывает, как поместить окно предпросмотра на Activity:
public class CameraActivity extends Activity { private Camera mCamera; private CameraPreview mPreview; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Получаем доступ к эеземпляру камеры – код этого метода приведен выше mCamera = getCameraInstance(); // Создаем Preview и помещаем его на Layout. mPreview = new CameraPreview(this, mCamera); FrameLayout preview = (FrameLayout) findViewById(id.camera_preview); preview.addView(mPreview); } }
Фотографирование
После того, как вы создали класс предпросмотра и поместили его на layout, можно писать код для захвата фото или видео. В коде приложения, вы должны установить слушатели событий на элементы пользовательского интерфейса.
Для взятия фотографии используется метод Camera.takePicture(), принимающий 3 параметра. Чтобы сделать фотографию в JPEG, нужно реализовать Camera.PictureCallback, чтобы принять данные и записать их в файл как в нижеприведенном примере:
private PictureCallback mPicture = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE); if (pictureFile == null){ Log.d(TAG, "Error creating media file, check storage permissions: " +e.getMessage()); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } } };
Произведем фотографирования с помощью метода Camera.takePicture(), который будет выполняться по нажатию на кнопку:
Button captureButton = (Button) findViewById(id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // берем изображение с камеры mCamera.takePicture(null, null, mPicture); } } );
Внимание: создание объекта mPicture производится в коде выше
Внимание: Никогда не забывайте освобождать ресурс камеры после ее использования с помощью метода Camera.release()
Видеозапись
Видеозапись с камеры на Android требует внимательного управления объектом Camera и его взаимодействием с объектом класса MediaRecorder. При записи видео, вы должны вызывать методы Camera.lock() и Camera.unlock() для получения доступа к камере объектом класса MediaRecorder.
В отличии от фотографирования, снятие видео требует очень педантичного следования определенному порядку вызова методов, приведенному ниже
2. Connect Preview – Подготавливаем объект класса SurfaceView к отображению поля зрения камеры методом Camera.setPreviewDisplay().
3. Start Preview – Вызываем Camera.startPreview(), чтобы начать передавать изображение с камеры на экран
4. Start Recording Video – Следующие шаги должны быть выполнены именно в указанном порядке:
2. setAudioSource() – Установить источник аудиоинформации - константу MediaRecorder.AudioSource.CAMCORDER.
3. setVideoSource() – Определить источник видеоинформации - константу MediaRecorder.VideoSource.CAMERA.
4. Установить формат выходного видео и его кодировку. Для Android API Level 8 и выше, используйте метод MediaRecorder.setProfile , получив экземпляр профиля в помощью метода CamcorderProfile.get(). Для Android 2.2 и ниже, придется устанавливать все параметры вручную:
1. setOutputFormat() – Формат видео. Используйте установленный по умолчанию или константу MediaRecorder.OutputFormat.MPEG_4.
2. setAudioEncoder() – Кодировка аудио.. Используйте установленную по умолчанию или MediaRecorder.AudioEncoder.AMR_NB.
3. setVideoEncoder() – Кодировка видо. Используйте установленную по умолчанию или MediaRecorder.VideoEncoder.MPEG_4_SP.
5. setOutputFile() – Установите путь в выходному файл с помощью метода getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
Внимание: Вы обязаны вызывать методы класса MediaRecorder именно в таком порядке, иначе могу произойти непредсказуемые ошибки
5. Stop Recording Video – Вызовите эти методы по окончании съемки видео:
2. Reset MediaRecorder – Опционально, сбросьте параметры конфигурации рекордера методом MediaRecorder.reset().
4. Lock the Camera – Заблокировать камеру, чтобы можно было записать видео в другой раз - Camera.lock(). Этот метод вызывать не требуется, если используется API 14 и выше
Внимание: На самом деле, вы, конечно, можете записывать видео без создания окна предпросмотра, с пропуском некоторых шагов из этой инструкции. Однако, едва ли это понравится пользователю – снимать то, что он не видит
При использовании MediaRecorder-а, вы обязательно должны вызывать все методы по его настройке в правильном порядке, а затем - MediaRecorder.prepare(). Пример показывает, как правильно написать этот код:
private boolean prepareVideoRecorder(){ mCamera = getCameraInstance(); mMediaRecorder = new MediaRecorder(); // Step 1: Разблокируем камеру mCamera.unlock(); mMediaRecorder.setCamera(mCamera); // Step 2: Устанавливаем источники аудио и видеоинформации mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // Step 3: Если API>=8, устанавливаем CamcorderProfile mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); // Step 4: Определяем выходную директорию mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()); // Step 5: Установка preview для записи mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface()); // Step 6: Подготовка MediaRecorder-а и обработка возможных ошибок try { mMediaRecorder.prepare(); } catch (IllegalStateException e) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } catch (IOException e) { Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } return true; }
Если вы используете API меньше 8-й версии, в Step 3 вместо установки CamcorderProfiile придется сделать следующее:
// Step 3: Установка форматов и кодировок
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
Приведенный пример использует стандартные настройки выходного видео. Можно указать свои с помощью следующих методов класса MediaRecorder:
В таком порядке пишут видео с помощью класса MediaRecorder:
1. Разблокируем камеру - Camera.unlock()
2. Настраиваем MediaRecorder как в коде, приведенном выше
3. Начать запись - MediaRecorder.start()
4. Видео записывается
5. Останавливаем запись - MediaRecorder.stop()
6. Освобождаем рекордер - MediaRecorder.release()
7. Блокируем камеру - Camera.lock()
Пример ниже демонстрирует пример исполнения данного алгоритма:
private boolean isRecording = false; // Добавляет слушатель событий на кнопку Button captureButton = (Button) findViewById(id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (isRecording) { // заканчиваем запись и освобождаем ресурс рекордера mMediaRecorder.stop(); // остановка записи releaseMediaRecorder(); mCamera.lock(); // блокируем камеру // Информируем пользователя об окончании записи setCaptureButtonText("Capture"); isRecording = false; } else { // инициализируем камеру if (prepareVideoRecorder()) { // камера доступна и разблокирована, MediaRecorder готов // теперь можно начинать снимать mMediaRecorder.start(); // Информируем пользователя, что запись начата setCaptureButtonText("Stop"); isRecording = true; } else { // освобождаем камеру, так как произошла какая-то ошибка при настройке рекордера releaseMediaRecorder(); // сообщаем юзеру об ошибке } } } } );
Внимание: метод prepareVideoRecorder() описан в разделе о настройке рекордела
Камера – ресурс, который может использовать не только ваше приложение, поэтому надо быть особенно внимательным при управлении им и не забывать вовремя его освобождать. В противном случае, другие попытки доступа к камере, будут вызывать ошибки
Используйте для освобождения ресурсов систему наподобие приведенной ниже:
public class CameraActivity extends Activity { private Camera mCamera; private SurfaceView mPreview; private MediaRecorder mMediaRecorder; ... @Override protected void onPause() { super.onPause(); releaseMediaRecorder(); // сначала освободим MediaRecorder releaseCamera(); // освобождние камеры } private void releaseMediaRecorder(){ if (mMediaRecorder != null) { mMediaRecorder.reset(); // сбросим все настройки рекордера mMediaRecorder.release(); // освобождаем mMediaRecorder = null; mCamera.lock(); // блокируем камеру } } private void releaseCamera(){ if (mCamera != null){ mCamera.release(); mCamera = null; } } }
Комментариев нет:
Отправить комментарий