Как получить необработанные данные предварительного просмотра из объекта Camera не менее 15 кадров в секунду в Android?
Мне нужно получить необработанные данные предварительного просмотра из объекта 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 (байт [] data, Camera camera) вызывается каждые 110 миллисекунд, поэтому я могу получить не более 9 кадров в секунду. И независимо от того, какую частоту кадров предварительного просмотра я задал с помощью проблемы setPreviewFrameRate(), и какой превью диапазон Fps, который я задал с помощью проблемы setPreviewFpsRange(), журнал тот же.
Помог ли кто-нибудь мне помощь по этой проблеме? Мне нужно получить необработанные данные предварительного просмотра с объекта камеры не менее 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>
Ответы
Ответ 1
Я боюсь, вы не можете. Предварительный параметр настройки частоты кадров - это подсказка для приложения камеры (которая выполняется в отдельном процессе) - и она может принимать или молча игнорировать ее. Это также не связано с поиском кадра предварительного просмотра
Когда вы запрашиваете рамку предварительного просмотра, вы просто говорите внешнее приложение, которое вы хотели бы получить. Буфер для него выделяется в приложении камеры, а затем передается в вашу деятельность через сегмент памяти в виде мегапикселя - это требует времени.
Вы можете получить желаемую производительность на некоторых устройствах, но не обязательно на том, с которым вы играете.
Если вам нужна определенная частота кадров, вам нужно будет захватить видео, а затем проанализировать/распаковать результирующий двоичный поток.
Ответ 2
У меня опыт работы с камерой был неудобным и зависящим от оборудования. Поэтому попробуйте запустить его на другом оборудовании, если сможете.
Возможно, стоит попробовать еще несколько настроек камеры.
Спасибо за то, что вы добавили пример кода.
Ответ 3
Это не должно быть проблемой. Мое приложение androangelo (оно на рынке) получает до 30 кадров в секунду (по крайней мере, я применил тормоз скорости, чтобы замедлить его).
Пожалуйста, внимательно проверьте, заполнен ли ваш журнал заявлениями-сборщиками мусора. Это так, если добавлено слишком мало буферов. Это был трюк для меня. По крайней мере, я подошел, чтобы добавить 20! буферов к камере.
Затем обработка каждого кадра должна проходить в отдельном потоке. Пока изображение находится в потоке для обработки, обратный вызов должен пропускать текущий кадр.
Ответ 4
В моем понимании, Android не позволяет пользователю устанавливать фиксированную частоту кадров, а также гарантировать, что значение fps, которое вы укажете, будет соблюдаться, связано с временем экспозиции кадра, которое устанавливается аппаратным или программным обеспечением камеры. Частота кадров, которую вы наблюдаете, может быть функцией освещения. Например, определенный телефон может дать вам предварительный просмотр 30 кадров в дневном свете, но только 7 кадров в секунду, если вы снимаете в условиях низкой освещенности.
Ответ 5
Одна вещь, которая, по-видимому, увеличивает текучесть предварительного просмотра, если не фактический 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). Не знаете, сколько записей замедляет работу?
Настройка предварительного просмотра до 320x240 (против 1280x720) снижает скорость до 50-70 мс... возможно, что вам нужно делать? По общему признанию, эти небольшие данные могут быть намного менее полезными.
//все протестированы на Nexus4
Ответ 6
Я обычно объявляю глобальное логическое 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;
}