Translate

вторник, 17 июля 2012 г.

Использование камеры в android приложениях. Создание приложения-камеры


Эта статья представляет собой перевод части этого официального мануала. В ней описано, как создать приложение-камеру,  которое может снимать фото и видео





Шаги по конструированию приложения:
   Обнаружить и получить доступ к камере — Проверяем наличие камеры на устройстве и получаем к ней доступ
   Создание класса предпросмотра — Объявление класса предпросмотра, который будет наследником SurfaceView и реализовывать интерфейс SurfaceHolder.  Этот класс сможет брать видео с камеры в реальном времени
   Создать Preview LayoutLayout в пользовательском интерфейсе, куда вы будете помещать окно предпросмотра
   Поставить слушатели событий — Создать методы, обрабатывающие действия пользователя
   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.
Внимание: Начиная с API 14, Camera.lock() и  Camera.unlock() вызываются автоматически.
В отличии от фотографирования, снятие видео требует очень педантичного следования определенному порядку вызова методов, приведенному ниже
1.  Open Camera - Методом Camera.open() получаем доступ к объекту класса Camera
2.  Connect Preview – Подготавливаем объект класса SurfaceView  к отображению  поля зрения камеры методом Camera.setPreviewDisplay().
3.  Start Preview – Вызываем  Camera.startPreview(), чтобы начать передавать изображение с камеры на экран
4.  Start Recording Video – Следующие шаги должны быть выполнены именно в указанном порядке:
1.  Unlock the CameraВыхвать метод Camera.unlock().
2.  Configure MediaRecorder – Вызвать следующие методы класса  MediaRecorder в указанном порядке:
1.  setCamera() – установить объект типа Camera, с которого рекордер должен получать видео
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()
6.  setPreviewDisplay() Укажите  SurfaceView для рекордера
Внимание: Вы обязаны вызывать методы класса MediaRecorder именно в таком порядке, иначе могу произойти непредсказуемые ошибки
3.  Prepare MediaRecorderПодготовить  MediaRecorder к записи с помощью MediaRecorder.prepare().
4.  Start MediaRecorderНачать запись с помощью метода MediaRecorder.start().
5.  Stop Recording Video – Вызовите эти методы по окончании съемки видео:
1.  Stop MediaRecorderОкончание записи видео: MediaRecorder.stop().
2.  Reset MediaRecorderОпционально, сбросьте параметры конфигурации рекордера методом MediaRecorder.reset().
3.  Release MediaRecorderОсвобождение ресурса рекордера вызовом MediaRecorder.release().
4.  Lock the Camera – Заблокировать камеру, чтобы можно было записать видео в другой раз  -  Camera.lock(). Этот метод вызывать не требуется, если используется API 14 и выше
6.  Stop the PreviewОстановите предпросмотр камеры - Camera.stopPreview().
7.  Release CameraОсвобождение ресурса камеры -  Camera.release().
Внимание: На самом деле, вы, конечно, можете записывать видео  без создания окна предпросмотра, с пропуском некоторых шагов из этой инструкции. Однако, едва ли это понравится пользователю – снимать то, что он не видит

Настройка MediaRecorder-а

При использовании  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;

        }

    }

}

Комментариев нет:

Отправить комментарий

Related Posts Plugin for WordPress, Blogger...