2015-10-26 37 views
1

更新2015年10月28日以反映當前進度。 我有一個應用程序,允許用戶爲Motion JPEG錄製設置攝像機參數,創建一個MJPEG文件,然後用戶可以修改這些設置並使用更新的設置創建另一個文件。當初始值不是30 FPS時,我在更新幀每秒設置時遇到問題。當初始值爲30 FPS時,我可以更新到不同的FPS級別併成功錄製該級別的視頻。但是,我無法將不等於30FPS的級別更新爲另一個FPM級別。我碰到LogCat出現問題.setPreviewFpsRange():更新攝像頭時每秒更新幀數.setParameters()

camera.setParameters(parameters);

錯誤的完整的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); 
    } 
} 

佈局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> 

截圖:顯示字段,按鈕和表面配置

分辨率:看起來上述不規律行爲可能與松下Toughpad JT-B1的主要測試設備有關。運行

.getSupportedPreviewFpsRange(); 

在此設備上返回的範圍爲8,000-30,000 fps。但是,此範圍內的許多值會導致崩潰,並且此範圍之外的某些值看起來工作正常。測試Samsung S4 Active並不會導致這些不一致,返回範圍內的所有值(4,000 - 30,000)都可以正常工作,而且此範圍外的測試值也不會展示任何預期的功能。

+0

插入以下內容, '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

+0

問題的癥結似乎是'p.setPreviewFpsRange(intFramesPerSecond,intFramesPerSecond);'作爲註釋停止崩潰並允許重複更新。 – portsample

+1

請注意,相機API不允許將預覽FPS範圍設置爲任意值。您應該查詢支持範圍列表的相機參數,並且不保證其他任何組合都能正常工作。 –

回答

2

Camera API不允許將預覽FPS範圍設置爲任意值。您應該查詢支持範圍列表的相機參數,並且不保證其他任何組合都能正常工作。

原則上,使用Camera.setParameters()的不受支持的值是undefined behavior。當您嘗試相同的輸入時,不同的設備會失敗或工作不同。

當然,您應該停止預覽以更改相機參數,然後重新開始預覽。

除此之外,您可能可以使用解決方法來保留受支持的參數。要達到2 fps,並切換到10 fps,您不需要更改相機設置。您的邏輯可以通過時間戳過濾出您的onPreviewFrame()中的相關幀。


此外,您的代碼在預覽回調方面並不理想。首先,您應該在單獨的處理程序線程中打開相機,然後預覽回調將不會到達UI線程(較新版本的Android對劫持CPU或網絡密集型任務的主線程的應用程序變得更加嫉妒)。

其次,考慮使用camera.setPreviewCallbackWithBuffer()來避免不必要的垃圾收集。這項技術的一個額外優點是,如果您只准備一個預覽緩衝區,則只有在您釋放時纔會收到預覽回調。所以,你可以簡單地使用代碼:

public void onPreviewFrame(byte[] data, Camera camera) { 
    long timestampBeforecompression = SystemClock.uptimeMillis(); 
    compress(data); 
    long compressionMillis = SystemClock.uptimeMillis() - timestampBeforecompression; 
    SystemClock.sleep(1000000/intFramesPerSecond - compressionMillis); 
    camera.addCallbackBuffer(data); 
} 

也許,你可以更精確,如果你也彌補了目前相機的幀速率,但是這種談論2或3時,FPS可能並不重要。


最後,還有一個提示:許多設備仍然支持棄用setPreviewFrameRate(),甚至宣佈支持FPS值可能會感興趣的你:

[1, 2, 3, 4, 5, 8, 10, 15, 20, 30] 

我沒有名字的Snapdragon -801平板電腦。

+0

這很有用。感謝您的評論和觀點。 – portsample