ОБНОВЛЕНО 28 октября 2015 года, чтобы отразить текущий прогресс. У меня есть приложение, которое позволяет пользователю установить параметры камеры для записи Motion JPEG, создать файл MJPEG, а затем пользователь может изменить эти настройки и создать другой файл с обновленными настройками. У меня возникает проблема обновления настроек кадров в секунду, когда начальное значение составляет не более 30 FPS. Когда начальное значение составляет 30 FPS, я могу обновиться до другого уровня FPS и успешно записать видео на этом уровне. Однако я не могу обновить с уровня, который не равен 30FPS другому уровню FPM. Я получаю сбой с LogCat, показывая проблему на.setPreviewFpsRange(): Проблемы с обновлением кадров в секунду в камере .setParameters()
camera.setParameters (параметры);
Полная LogCat ошибки ниже,
10-26 20:27:36.414: E/AndroidRuntime(2275): FATAL EXCEPTION: main
10-26 20:27:36.414: E/AndroidRuntime(2275): java.lang.RuntimeException: setParameters failed
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.hardware.Camera.native_setParameters(Native Method)
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.hardware.Camera.setParameters(Camera.java:1333)
10-26 20:27:36.414: E/AndroidRuntime(2275): at net.blepsias.riverwatch.RiverWatch.setCamera(RiverWatch.java:191)
10-26 20:27:36.414: E/AndroidRuntime(2275): at net.blepsias.riverwatch.RiverWatch.onClick(RiverWatch.java:167)
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.view.View.performClick(View.java:3514)
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.view.View$PerformClick.run(View.java:14111)
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.os.Handler.handleCallback(Handler.java:605)
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.os.Handler.dispatchMessage(Handler.java:92)
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.os.Looper.loop(Looper.java:137)
10-26 20:27:36.414: E/AndroidRuntime(2275): at android.app.ActivityThread.main(ActivityThread.java:4429)
10-26 20:27:36.414: E/AndroidRuntime(2275): at java.lang.reflect.Method.invokeNative(Native Method)
10-26 20:27:36.414: E/AndroidRuntime(2275): at java.lang.reflect.Method.invoke(Method.java:511)
10-26 20:27:36.414: E/AndroidRuntime(2275): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
10-26 20:27:36.414: E/AndroidRuntime(2275): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
10-26 20:27:36.414: E/AndroidRuntime(2275): at dalvik.system.NativeStart.main(Native Method)
Проверка LogCat цитируется строка 5 и 6, они соответствуют:
(191) camera.setParameters(parameters);
(167) setCamera(camera);
Ниже приложение. Я также включу файл .xml макета для справки, а также скриншот для заземления.
RiverWatch.java
public class RiverWatch extends Activity implements OnClickListener, SurfaceHolder.Callback, Camera.PreviewCallback {
public static final String LOGTAG = "VIDEOCAPTURE";
String szBoundaryStart = "\r\n\r\n--myboundary\r\nContent-Type: image/jpeg\r\nContent-Length: ";
String szBoundaryDeltaTime = "\r\nDelta-time: 110";
String szBoundaryEnd = "\r\n\r\n";
private SurfaceHolder holder;
private Camera camera;
private CamcorderProfile camcorderProfile;
Spinner spinnerCamcorderProfile;
public TextView tvFramesPerSecond, tvJpegQuality, tvSegmentDuration;
boolean bRecording = false;
boolean bPreviewRunning = false;
int intFramesPerSecond = 30000; //this is 30fps...mult by 1,000
int intJpegQuality=50; //must be above 20
int intSegmentDuration=10;
boolean ckbxRepeat=false;
byte[] previewCallbackBuffer;
File mjpegFile;
FileOutputStream fos;
BufferedOutputStream bos;
Button btnStartRecord, btnStopRecord, btnExit, btnChange;
Camera.Parameters parameters;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Date T = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String szFileName = "videocapture-"+sdf.format(T)+"-";
try {
mjpegFile = File.createTempFile(szFileName, ".mjpeg", Environment.getExternalStorageDirectory());
} catch (Exception e) {
finish();
}
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.main);
tvFramesPerSecond = (TextView) this.findViewById(R.id.textboxframespersecondxml);
int iFPS = intFramesPerSecond/1000;
String szFPS = Integer.toString(iFPS);
tvFramesPerSecond.setClickable(true);
tvFramesPerSecond.setText(szFPS);
tvFramesPerSecond.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
getSupportedPreviewFpsRange();
}
});
tvJpegQuality = (TextView) this.findViewById(R.id.textboxJpegQualityxml);
String szJpegQuality = Integer.toString(intJpegQuality);
tvJpegQuality.setText(szJpegQuality);
tvSegmentDuration = (TextView) this.findViewById(R.id.textboxSegmentDurationxml);
String szSegmentDuration = Integer.toString(intSegmentDuration);
tvSegmentDuration.setText(szSegmentDuration);
btnStartRecord = (Button) this.findViewById(R.id.StartRecordButton);
btnStartRecord.setOnClickListener(this);
btnStopRecord = (Button) this.findViewById(R.id.StopRecordButton);
btnStopRecord.setOnClickListener(this);
btnExit = (Button) this.findViewById(R.id.ExitButton);
btnExit.setOnClickListener(this);
btnChange = (Button) this.findViewById(R.id.ChangeButton);
btnChange.setOnClickListener(this);
camcorderProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_480P);
SurfaceView cameraView = (SurfaceView) findViewById(R.id.CameraView);
holder = cameraView.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
cameraView.setClickable(true);
cameraView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.StartRecordButton:
try {
fos = new FileOutputStream(mjpegFile);
bos = new BufferedOutputStream(fos);
bRecording = true;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Toast.makeText(this, "Recording started.", Toast.LENGTH_SHORT).show();
break;
case R.id.StopRecordButton:
try {
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(this, "Recording stopped.", Toast.LENGTH_SHORT).show();
break;
case R.id.ChangeButton:
//Frames Per Second- expressed x1000 in the function
String szFPS=tvFramesPerSecond.getText().toString();
int iFPS = Integer.parseInt(szFPS);
intFramesPerSecond = iFPS *1000;
//Jpeg quality- cant be <20 or >100, checks this and populates field with entered or corrected value.
String szJpegQuality=tvJpegQuality.getText().toString();
int intJpegQualityTemp = Integer.parseInt(szJpegQuality);
if (intJpegQualityTemp < 21){//...can't be less than 21
intJpegQuality = 21;
}else if(intJpegQualityTemp > 100){//can't be greater than 100
intJpegQuality = 100;
}else{ //quality is between 21 and 100...
intJpegQuality = intJpegQualityTemp;
}
szJpegQuality = Integer.toString(intJpegQuality);
tvJpegQuality.setText(szJpegQuality);
//Segment duration
String szSegmentDuration=tvSegmentDuration.getText().toString();
intSegmentDuration = Integer.parseInt(szSegmentDuration);
releaseCamera();
setCamera(camera);
camera.startPreview();
Toast.makeText(this, "Change button pressed.", Toast.LENGTH_SHORT).show();
break;
case R.id.ExitButton:
System.exit(0);
break;
}
}
public void releaseCamera(){
camera.stopPreview();
//camera.release(); //...cause crash
//camera = null;
}
public void setCamera(Camera camera){
Camera.Parameters parameters=camera.getParameters();
parameters.setPreviewFpsRange(intFramesPerSecond, intFramesPerSecond);//note: This is fps x 1000 (!)
parameters.setPreviewSize(camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
Log.v(LOGTAG,"FPS: " + parameters.getSupportedPreviewFpsRange());
camera.setParameters(parameters);
}
public void getSupportedPreviewFpsRange(){
/****************************************************************
* getSupportedPreviewFpsRange()- Returns specified frame rate
* (.getSupportedPreviewFpsRange()) to log file and also displays
* as toast message.
****************************************************************/
Camera.Parameters camParameter = camera.getParameters();
List<int[]> frame = camParameter.getSupportedPreviewFpsRange();
Iterator<int[]> supportedPreviewFpsIterator = frame.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.d(LOGTAG, "FPS6: " + sb.toString());
Toast.makeText(this, "FPS = "+sb.toString(), Toast.LENGTH_SHORT).show();
}//*****************end getSupportedPreviewFpsRange()**********************
}
public void surfaceCreated(SurfaceHolder holder) {
camera = Camera.open();
}
@SuppressLint("NewApi")
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (!bRecording) {
if (bPreviewRunning = true){
camera.stopPreview();
} try {
parameters = camera.getParameters();
parameters.setPreviewSize(camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
parameters.setPreviewFpsRange(intFramesPerSecond, intFramesPerSecond);//note: This is fps x 1000 (!)
//p.setPreviewFrameRate(intFramesPerSecond);
camera.setParameters(parameters);
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(this);
camera.setDisplayOrientation(90);
camera.startPreview();
bPreviewRunning = true;
}
catch (IOException e) {
e.printStackTrace();
}
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (bRecording) {
bRecording = false;
try {
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
bPreviewRunning = false;
camera.release();
finish();
}
public void onPreviewFrame(byte[] b, Camera c) {
if (bRecording) {
// Assuming ImageFormat.NV21
if (parameters.getPreviewFormat() == ImageFormat.NV21) {
try {
YuvImage im = new YuvImage(b, ImageFormat.NV21, parameters.getPreviewSize().width, parameters.getPreviewSize().height, null);
Rect r = new Rect(0,0,parameters.getPreviewSize().width,parameters.getPreviewSize().height);
ByteArrayOutputStream jpegByteArrayOutputStream = new ByteArrayOutputStream();
im.compressToJpeg(r, intJpegQuality, jpegByteArrayOutputStream);//note: qual = 20 or less doesn't work.
byte[] jpegByteArray = jpegByteArrayOutputStream.toByteArray();
byte[] boundaryBytes = (szBoundaryStart + jpegByteArray.length + szBoundaryDeltaTime + szBoundaryEnd).getBytes();
bos.write(boundaryBytes);
bos.write(jpegByteArray);
bos.flush();
//bos.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
Log.v(LOGTAG,"NOT THE RIGHT FORMAT");
}
}
}
@Override
public void onConfigurationChanged(Configuration conf){
super.onConfigurationChanged(conf);
}
}
Layout main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/StartRecordButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Recording" />
<Button
android:id="@+id/StopRecordButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop Recording" />
<Button
android:id="@+id/ChangeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dip"
android:text="Reset settings" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<TextView
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Frames/second:" />
<EditText
android:id="@+id/textboxframespersecondxml"
android:editable="true"
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="0"
android:layout_marginRight="10dip"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<TextView
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="JPEG image quality:" />
<EditText
android:id="@+id/textboxJpegQualityxml"
android:editable="true"
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="0"
android:layout_marginRight="10dip"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:text="Camcorder profile: " />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<TextView
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Segment duration (file length):" />
<EditText
android:id="@+id/textboxSegmentDurationxml"
android:editable="true"
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="0"
android:layout_marginRight="10dip"/>
<TextView
style="@style/myStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" minutes" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="right" >
<CheckBox
android:id="@+id/repeat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Repeat" />
<Button
android:id="@+id/ExitButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Exit Application" />
</LinearLayout>
<SurfaceView
android:id="@+id/CameraView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Скриншот: Отображение поля, кнопки и поверхности конфигурации
Разрешение: Похоже, что большая часть вышеуказанного ошибочного поведения может быть связана с первичным испытательным устройством, которое является Panasonic Toughpad JT-B1. Запуск
.getSupportedPreviewFpsRange();
на этом устройстве возвращает диапазон 8 000-30 000 кадров в секунду. Однако многие значения в этом диапазоне приводят к сбоям, а некоторые значения вне этого диапазона работают нормально. Тестирование Samsung S4 Active не привело ни к одному из этих несоответствий, при этом все значения в возвращаемом диапазоне (4000 - 30 000) работали нормально, а не проверенные значения вне этого диапазона, демонстрирующие любую функциональность, как ожидалось.
Вставка: 'camera.stopPreview(); p = camera.getParameters(); p.setPreviewSize (camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight); p.setPreviewFpsRange (intFramesPerSecond, intFramesPerSecond); camera.setParameters (p); camera.startPreview(); ' в распределительном регистре R.id.ChangeButton делает код вызывает правильное изменение частоты кадров, однако, я все еще получаю сбои при ' camera.setParameters (P); ' для последующих обновлений, почему? – portsample
Суть проблемы - это «p.setPreviewFpsRange (intFramesPerSecond, intFramesPerSecond)», поскольку комментирование этого события останавливает сбои и позволяет повторять обновления. – portsample
Обратите внимание, что API-интерфейс камеры не позволяет установить диапазон предварительного просмотра FPS произвольными значениями. Вы должны запросить параметры камеры для списка поддерживаемых диапазонов, и любая другая комбинация не гарантируется. –