Как получить необработанные данные предварительного просмотра с объекта Camera, по крайней мере, 15 кадров в секунду в Android? - PullRequest
30 голосов
/ 17 октября 2011

Мне нужно получить необработанные данные предварительного просмотра из объекта Camera как минимум 15 кадров в секунду , но я могу получить кадр только за 110 миллисекунд, что означает, что я могу получить только 9 кадров в секунду.Ниже приведен мой код.

Camera mCamera = Camera.open();
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewFrameRate(30);
parameters.setPreviewFpsRange(15000,30000);
mCamera.setParameters(parameters);
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
//dataBufferSize stands for the byte size for a picture frame
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
mCamera.addCallbackBuffer(new byte[dataBufferSize]);
mCamera.setPreviewDisplay(videoCaptureViewHolder);
//videoCaptureViewHolder is a SurfaceHolder object
mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
  private long timestamp=0;
  public synchronized void onPreviewFrame(byte[] data, Camera camera) {
    Log.v("CameraTest","Time Gap = "+(System.currentTimeMillis()-timestamp));
    timestamp=System.currentTimeMillis();
    //do picture data process
    camera.addCallbackBuffer(data);
    return;
  }
}
mCamera.startPreview();

В приведенном выше кратком коде dataBufferSize и videoCaptureViewHolder определены и рассчитаны или назначены в других операторах.

Я запускаю свой код, я вижу предварительный просмотр нана экране появляется журнал:

...
V/CameraTest( 5396): Time Gap = 105
V/CameraTest( 5396): Time Gap = 112
V/CameraTest( 5396): Time Gap = 113
V/CameraTest( 5396): Time Gap = 115
V/CameraTest( 5396): Time Gap = 116
V/CameraTest( 5396): Time Gap = 113
V/CameraTest( 5396): Time Gap = 115
...

Это означает, что onPreviewFrame (byte [] data, Camera camera) вызывается каждые 110 миллисекунд, поэтому я могу получить не более 9 кадровв секунду.И независимо от того, какую частоту кадров предварительного просмотра я установил с помощью проблемы setPreviewFrameRate () и какой диапазон Fps предварительного просмотра я установил с помощью проблемы setPreviewFpsRange () , журнал остается неизменным.

Кто-нибудь может помочь мне с этой проблемой?Мне нужно получить необработанные данные предварительного просмотра от объекта Camera по крайней мере 15 кадров в секунду.Заранее спасибо.

Я поместил весь свой код ниже.

CameraTest.java

package test.cameratest;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import android.app.Activity;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;

public class CameraTestActivity extends Activity {
    SurfaceView mVideoCaptureView;
    Camera mCamera;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mVideoCaptureView = (SurfaceView) findViewById(R.id.video_capture_surface);
        SurfaceHolder videoCaptureViewHolder = mVideoCaptureView.getHolder();
        videoCaptureViewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        videoCaptureViewHolder.addCallback(new Callback() {
            public void surfaceDestroyed(SurfaceHolder holder) {
            }

            public void surfaceCreated(SurfaceHolder holder) {
                startVideo();
            }

            public void surfaceChanged(SurfaceHolder holder, int format,
                    int width, int height) {
            }
        });
    }
    private void startVideo() {
        SurfaceHolder videoCaptureViewHolder = null;
        try {
            mCamera = Camera.open();
        } catch (RuntimeException e) {
            Log.e("CameraTest", "Camera Open filed");
            return;
        }
        mCamera.setErrorCallback(new ErrorCallback() {
            public void onError(int error, Camera camera) {
            }
        }); 
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewFrameRate(30);
        parameters.setPreviewFpsRange(15000,30000);
        List<int[]> supportedPreviewFps=parameters.getSupportedPreviewFpsRange();
        Iterator<int[]> supportedPreviewFpsIterator=supportedPreviewFps.iterator();
        while(supportedPreviewFpsIterator.hasNext()){
            int[] tmpRate=supportedPreviewFpsIterator.next();
            StringBuffer sb=new StringBuffer();
            sb.append("supportedPreviewRate: ");
            for(int i=tmpRate.length,j=0;j<i;j++){
                sb.append(tmpRate[j]+", ");
            }
            Log.v("CameraTest",sb.toString());
        }

        List<Size> supportedPreviewSizes=parameters.getSupportedPreviewSizes();
        Iterator<Size> supportedPreviewSizesIterator=supportedPreviewSizes.iterator();
        while(supportedPreviewSizesIterator.hasNext()){
            Size tmpSize=supportedPreviewSizesIterator.next();
            Log.v("CameraTest","supportedPreviewSize.width = "+tmpSize.width+"supportedPreviewSize.height = "+tmpSize.height);
        }

        mCamera.setParameters(parameters);
        if (null != mVideoCaptureView)
            videoCaptureViewHolder = mVideoCaptureView.getHolder();
        try {
            mCamera.setPreviewDisplay(videoCaptureViewHolder);
        } catch (Throwable t) {
        }
        Log.v("CameraTest","Camera PreviewFrameRate = "+mCamera.getParameters().getPreviewFrameRate());
        Size previewSize=mCamera.getParameters().getPreviewSize();
        int dataBufferSize=(int)(previewSize.height*previewSize.width*
                               (ImageFormat.getBitsPerPixel(mCamera.getParameters().getPreviewFormat())/8.0));
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
            private long timestamp=0;
            public synchronized void onPreviewFrame(byte[] data, Camera camera) {
                Log.v("CameraTest","Time Gap = "+(System.currentTimeMillis()-timestamp));
                timestamp=System.currentTimeMillis();
                try{
                    camera.addCallbackBuffer(data);
                }catch (Exception e) {
                    Log.e("CameraTest", "addCallbackBuffer error");
                    return;
                }
                return;
            }
        });
        try {
            mCamera.startPreview();
        } catch (Throwable e) {
            mCamera.release();
            mCamera = null;
            return;
        }
    }
    private void stopVideo() {
        if(null==mCamera)
            return;
        try {
            mCamera.stopPreview();
            mCamera.setPreviewDisplay(null);
            mCamera.setPreviewCallbackWithBuffer(null);
            mCamera.release();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        mCamera = null;
    }
    public void finish(){
        stopVideo();
        super.finish();
    };
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="test.cameratest"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="10" android:maxSdkVersion="10"/>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />    
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".CameraTestActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Ответы [ 6 ]

7 голосов
/ 17 октября 2011

Боюсь, вы не можете.Параметр предварительного просмотра частоты кадров является подсказкой для приложения камеры (которое выполняется в отдельном процессе) - и он может свободно принимать или игнорировать его.Это также не связано с поиском фрейма предварительного просмотра

Когда вы запрашиваете фрейм предварительного просмотра, вы просто говорите внешнему приложению, что хотите его иметь.Буфер для него выделяется в приложении камеры, а затем передается вашей активности через сегмент памяти mmaped - это занимает время.

Вы можете получить желаемую производительность на некоторых устройствах, но не обязательно на том, с которым играете.

Если вам нужна определенная частота кадров, вам придется захватывать видео, а затем анализировать / распаковывать результирующий двоичный поток.

6 голосов
/ 08 мая 2013

Мой опыт работы с камерой зависел от аппаратного обеспечения. Поэтому попробуйте запустить его на другом оборудовании, если сможете.

Также стоит попробовать еще несколько настроек камеры.

Спасибо, что включили пример кода между прочим.

4 голосов
/ 06 ноября 2011

Это не должно быть проблемой. Мое приложение androangelo (оно есть в продаже) получает скорость до 30 кадров в секунду (по крайней мере, я использовал тормоз скорости, чтобы замедлить его).

Пожалуйста, внимательно проверьте, заполнен ли Ваш журнал инструкциями сборщика мусора. Это тот случай, если добавлено слишком мало буферов. Это был трюк для меня. По крайней мере, я подошел, чтобы добавить 20! буферы к камере.

Тогда обработка каждого кадра должна проходить в отдельном потоке. Пока изображение находится в потоке для обработки, обратный вызов должен пропускать текущий кадр.

2 голосов
/ 08 июня 2013

Одна вещь, которая, по-видимому, увеличивает плавность предварительного просмотра, если не обязательно фактический FPS, это установка для PreviewFormat значения YV12, если оно поддерживается.Копируется меньше байтов, выровнено по 16 байтов и возможно оптимизировано другими способами:

    // PREVIEW FORMATS
    List<Integer> supportedPreviewFormats = parameters.getSupportedPreviewFormats();
    Iterator<Integer> supportedPreviewFormatsIterator = supportedPreviewFormats.iterator();
    while(supportedPreviewFormatsIterator.hasNext()){
        Integer previewFormat =supportedPreviewFormatsIterator.next();
        // 16 ~ NV16 ~ YCbCr
        // 17 ~ NV21 ~ YCbCr ~ DEFAULT
        // 4  ~ RGB_565
        // 256~ JPEG
        // 20 ~ YUY2 ~ YcbCr ...
        // 842094169 ~ YV12 ~ 4:2:0 YCrCb comprised of WXH Y plane, W/2xH/2 Cr & Cb. see documentation
        Log.v("CameraTest","Supported preview format:"+previewFormat);
        if (previewFormat == ImageFormat.YV12) {
            parameters.setPreviewFormat(previewFormat);
            Log.v("CameraTest","SETTING FANCY YV12 FORMAT");                
        }
    }

http://developer.android.com/reference/android/graphics/ImageFormat.html#YV12 описывает формат.Этот плюс несколько запасных буферов дает мне «промежутки времени» всего 80… что еще недостаточно «хорошо», но… лучше?(на самом деле у меня есть один в 69 ... но на самом деле, они в среднем более 90).Не знаете, насколько логирование замедляет процесс?

Установка параметра previewSize равным 320x240 (по сравнению с 1280x720) приводит к уменьшению диапазона до 50-70 мсек ... так что, может быть, это то, что вам нужно сделать?Следует признать, что эти небольшие данные могут быть гораздо менее полезными.

// все тестируются на Nexus4

2 голосов
/ 13 февраля 2012

В моем понимании, Android не позволяет пользователю устанавливать фиксированную частоту кадров и не гарантирует, что значение fps, которое вы укажете, будет соблюдаться, из-за времени выдержки кадра, которое устанавливается аппаратным или программным обеспечением камеры. Наблюдаемая частота кадров может зависеть от условий освещения. Например, определенный телефон может давать вам скорость предварительного просмотра 30 кадров в секунду при дневном освещении, но только 7 кадров в секунду, если вы снимаете в условиях низкой освещенности.

0 голосов
/ 02 августа 2012

Я обычно объявляю глобальный логический lockCameraUse.Функция обратного вызова обычно выглядит следующим образом.

  public void onPreviewFrame(byte[] data, Camera camera) {
    if (lockCameraUse) {
      camera.addCallbackBuffer(data);
      return;
    }
    lockCameraUse = true;
    // processinng data

    // done processing data
    camera.addCallbackBuffer(data);
    lockCameraUse = false;
    return;
  }
...