web-dev-qa-db-ja.com

Androidで少なくとも毎秒15フレームのCameraオブジェクトから生のプレビューデータを取得する方法

Cameraオブジェクトから生のプレビューデータを少なくとも15フレーム/秒取得する必要がありますが、110ミリ秒でしかフレームを取得できません。つまり、1秒あたり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ミリ秒ごとに呼び出されるため、1秒あたり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>
30
EricOops

私は恐れています、あなたはできません。プレビューフレームレートの設定は、カメラの適用(別のプロセスで実行される)のヒントであり、それを受け入れるか、黙って無視してもかまいません。また、プレビューフレームの取得とは関係ありません。

プレビューフレームをリクエストするとき、必要な外部アプリケーションを指定するだけです。そのためのバッファーはカメラアプリケーションで割り当てられ、マッピングされたメモリセグメントを介してアクティビティに渡されます-これには時間がかかります。

一部のデバイスでは望ましいパフォーマンスが得られる場合がありますが、操作しているデバイスでは必ずしもそうとは限りません。

定義されたフレームレートが必要な場合は、ビデオをキャプチャし、結果のバイナリストリームを解析/解凍する必要があります。

7

カメラ関連の経験は手間とハードウェアに依存しています。可能であれば、他のハードウェアで実行してみてください。

また、いくつかのカメラ設定を試す価値があるかもしれません。

コードサンプルbtwを含めてくれてありがとう。

6
leorleor

これは問題にはなりません。私のアンドロアンジェロ(それは市場にあります)アプリは、毎秒最大30フレームを取得します(少なくとも、速度を下げるためにレートブレーキを実装しました)。

ログがガベージコレクターのステートメントでいっぱいになっていないか、慎重に確認してください。これは、追加されるバッファーが少なすぎる場合に当てはまります。これは私にとってトリックでした。少なくとも私は20を追加するようになりました!カメラへのバッファ。

次に、各フレームの処理は別のスレッドで行われる必要があります。画像が処理のためにスレッド内にある間、コールバックは現在のフレームをスキップする必要があります。

4
Kai

私の理解では、Androidは、ユーザーが固定フレームレートを設定することを許可していません。また、指定したfpsの値が尊重されることは保証されていません。これは、カメラによって設定されるフレーム露出時間によるものです。ハードウェアまたはファームウェア。観察するフレームレートは、照明条件の関数である可能性があります。たとえば、特定の電話では、日中の照明では30 fpsのプレビューレートが得られますが、低照度の条件で撮影している場合は7 fpsしか得られません。

2
Yevgeni Litvin

必ずしも実際のFPSとは言えないにせよ、プレビューの流動性を高めるように見える1つのことは、サポートされている場合は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で1つありますが、実際には平均で90前後です)。どれだけのロギングが物事を遅くしているのか分かりませんか?

PreviewSizeを320x240(1280x720ではなく)に設定すると、50〜70ミリ秒の範囲に収まります...おそらく、それが必要なのでしょうか。確かに、その小さなデータはあまり役に立たないかもしれません。

//すべてNexus4でテスト済み

2
Kaolin Fire

私は通常、グローバルなブール値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;
  }
0
DXM