Android中如何从Camera对象获取原始预览数据至less15帧/秒?

我需要从Camera对象获取至less15帧每秒的原始预览数据,但我只能得到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()设置的预览帧速率以及根据问题setPreviewFpsRange()设置的预览Fps范围,日志都是相同的。

有人会帮我解决这个问题吗? 我需要从Camera对象获取至less15帧每秒的原始预览数据。 先谢谢你。

我把我的整个代码放在下面。

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> 

Solutions Collecting From Web of "Android中如何从Camera对象获取原始预览数据至less15帧/秒?"

我很害怕,你不能。 预览帧速率设置提示相机应用程序(在单独的过程中运行) – 它可以自由接受或默默忽略它。 这也与预览框检索无关

当您请求预览框架时,您只需说出您想要的外部应用程序。 它的缓冲区分配在相机应用程序,然后通过mmaped内存段传递给您的活动 – 这需要时间。

您可能会在某些设备上获得所需的性能,但不一定在您正在玩的设备上。

如果您需要定义帧速率,则必须捕捉video,然后parsing/解压缩生成的二进制stream。

我用相机的东西一直很繁琐和硬件依赖的经验。 所以如果可以的话,请尝试在其他硬件上运行它。

也可能值得尝试一些更多的相机设置。

  • 签出setRecordingHint(布尔提示) 。
  • 甚至可以尝试FOCUS_MODE 。

感谢您包含一个代码示例btw。

这不应该是一个问题。 我的androangelo(它在市场上)的应用程序可以达到每秒30帧(至less我实现了一个速度刹车来减慢速度)。

请仔细检查您的日志是否填充垃圾收集器语句。 如果添加的缓冲区太less,就是这种情况。 这对我来说是个诡计。 至less我来了加20! 缓冲到相机。

然后每个帧的处理应该在一个单独的线程中进行。 当图像在线程中进行处理时,callback应该跳过当前帧。

根据我的理解,Android不允许用户设置固定的帧率,也不保证您指定的fps的值将受到相机硬件或固件设置的帧曝光时间的影响。 您观察的帧频可能是照明条件的一个function。 例如,某个手机在白天可以提供30fps的预览速度,但是如果您在低光照条件下拍摄,则只能提供7fps的预览速度。

有一件事似乎增加了预览的stream动性,如果不是实际的FPS,就是将previewFormat设置为YV12(如果支持的话)。 复制的字节更less,16字节alignment,并可能以其他方式优化:

  // 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描述了格式&#x3002; 这加上一些备用缓冲区给我的“时间差”低至80 …这仍然不够“好”,但是…更好? (实际上我有一个在69 …但实际上,他们平均约为90)。 不知道有多less日志logging正在放慢速度?

将previewSize设置为320×240(与1280×720相比)可以将事情降低到50-70msec范围…所以也许这就是您需要做的事情? 不可否认,这些小数据可能没那么有用。

//所有在Nexus4上testing

我通常会声明一个全局布尔lockCameraUse。 callback函数通常看起来像这样。

  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; }