我正在寫一個應用程序,以Android相機拍照。代碼顯示爲in this question。目前我不需要處理預覽框,所以setPreviewCallback()不用於相機。Android的相機在startPreview()凍結,沒有任何錯誤信息
在帶有768MB RAM的HTC Sensation設備上,相機在拍攝過程中會出現一些奇怪的動作。其中之一是,在拍攝照片並獲取onPictureTaken()回調中的數據後,startPreview()(拍攝下一張照片)會給出一個RuntimeException。目前,我只能捕捉異常並關閉/重新打開相機作爲解決方法。
但是,該設備存在更嚴重的問題。有時我重新打開相機,但startPreview()給我一個凍結的幀沒有例外或錯誤消息。我的AP不會崩潰。它仍然可以關閉相機並退出,但無法拍攝更多圖片(我將得到startPreview()或takePicture()失敗的運行時異常)。發生這種情況時,除非我重新啓動設備,否則內置攝像頭APP也會中斷。
作爲穩定性測試,我拍攝照片,獲取JPEG數據,但不要將它們寫入文件。當我拍攝約100張照片時,我需要重新打開相機約10次,最後相機會壞掉。如果我通過BitmapFactory解碼內存中的JPEG數據(仍未寫入文件),則重新打開/凍結率會大大增加。
我得到這些錯誤消息時startPreview()失敗:
E/SurfaceTexture(112): [SurfaceView] setBufferCount: client owns some buffers
E/SurfaceTextureClient(115): ISurfaceTexture::setBufferCount(7) returned Invalid argument
E/SurfaceTexture(112): [SurfaceView] dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=2 exceeded (dequeued=5)
E/QualcommCameraHardwareZSL(115): getBuffersAndStartPreview: dequeueBuffer failed for preview buffer. Error = -16
當預覽凍結,我沒有得到任何錯誤或相關的消息。當時甚至沒有關於內存不足異常的消息。如果我收我的應用程序並啓動內置攝像頭應用程序,它會立即與這些錯誤退出:
E/MemoryHeapBase(115): mmap(fd=142, size=9011200) failed (Out of memory)
E/QualcommCameraHardwareZSL(115): Failed to get camera memory for RawZSLAdsppool heap cnt(20)
E/MemoryHeapBase(115): mmap(fd=142, size=9011200) failed (Out of memory)
E/QualcommCameraHardwareZSL(115): Failed to get camera memory for RawZSLAdsppool heap cnt(18)
...
E/QualcommCameraHardwareZSL(115): initZslBuffer X failed cnt(0)
E/QualcommCameraHardwareZSL(115): initRaw X: error initializing mRawZSLAdspMapped
E/QualcommCameraHardwareZSL(115): Init ZSL buffers X failed
E/QualcommCameraHardwareZSL(115): Failed to allocate ZSL buffers
E/QualcommCameraHardwareZSL(115): Starting ZSL CAMERA_OPS_STREAMING_ZSL failed!!!
如果我再次啓動我的應用程序,我可以打開相機,無需上面的錯誤,但會以失敗startPreview()或takePicture()。然後我必須重新啓動設備。 由於該設備有一個768MB的小內存大小,我懷疑內存不足是主要問題,儘管我沒有直接從我的APP獲得OOM異常。
我已經測試了大約500奔跑〜重啓設備的15倍,並發現:
- startPreview()只失敗的RuntimeException拍照並獲得onPictureTaken()回調,因爲相機被打開後, 。
- 預覽凍結問題只發生在失敗的startPreview()後重新打開相機時。當一切正常時,它不會直接從startPreview()發生。
我知道如果我的應用程序崩潰而沒有正確關閉相機,它可能會鎖定相機。但是我已經檢查過我的APP仍然關閉並且在問題發生後釋放相機(但相機不可用,直到重新啓動設備)。相機內部似乎有些問題。任何人都可以幫我嗎?
編輯: 下面是有關打開相機和啓動預覽代碼:
public Camera m_camera;
int m_camera_index;
int m_camera_rotation;
String m_camera_focus_mode;
int m_preview_width;
int m_preview_height;
int m_picture_width;
int m_picture_height;
SurfaceHolder m_surface_holder;
boolean m_is_during_preview;
public CameraView(Context context)
{
super(context);
m_surface_holder = getHolder();
m_surface_holder.addCallback(this);
m_surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
m_is_during_preview = false;
}
public void OpenCamera()
{
CloseCamera();
// Determine m_camera_index on this device
...
m_camera = Camera.open(m_camera_index);
if (m_camera == null)
{
LogError("Failed to open any camera.");
return;
}
try
{
m_camera.setPreviewDisplay(m_surface_holder);
}
catch (IOException e)
{
e.printStackTrace();
m_camera = null;
return;
}
// Determine m_display_orientation
...
m_camera.setDisplayOrientation(m_display_orientation);
Camera.Parameters parameters = m_camera.getParameters();
// Determine m_preview_width x m_preview_height
// and m_picture_width x m_picture_height from the supported ones
...
parameters.setPictureSize(m_picture_width, m_picture_height);
parameters.setPreviewSize(m_preview_width, m_preview_height);
// Set m_camera_rotation so we get the picture data with correct orientation
m_camera_rotation = m_display_orientation;
if (m_activity.m_is_frontal_camera)
{
m_camera_rotation = 360 - m_display_orientation;
if (m_camera_rotation == 360)
m_camera_rotation = 0;
}
parameters.setRotation(m_camera_rotation);
parameters.setPreviewFormat(ImageFormat.NV21);
// Determine m_camera_focus_mode from the supported ones
...
parameters.setFocusMode(m_camera_focus_mode);
m_camera.setParameters(parameters);
m_is_during_preview = false;
}
public void CloseCamera()
{
if (m_camera != null)
{
StopPreview();
m_camera.release();
m_camera = null;
}
}
public void RestartCamera()
{
// Only use to restart the camera when startPreview() fails after taking a picture
CloseCamera();
m_camera = Camera.open(m_camera_index);
if (m_camera == null)
{
LogError("Failed to reopen camera.");
return;
}
try
{
m_camera.setPreviewDisplay(m_surface_holder);
}
catch (IOException e)
{
e.printStackTrace();
m_camera = null;
return;
}
m_camera.setDisplayOrientation(m_display_orientation);
Camera.Parameters parameters = m_camera.getParameters();
parameters.setPictureSize(m_picture_width, m_picture_height);
parameters.setPreviewSize(m_preview_width, m_preview_height);
parameters.setRotation(m_camera_rotation);
parameters.setPreviewFormat(ImageFormat.NV21);
parameters.setFocusMode(m_camera_focus_mode);
m_camera.setParameters(parameters);
StartPreview();
}
public void StartPreview()
{
if (m_camera == null)
return;
if (m_is_during_preview == true)
return;
m_camera.startPreview();
m_is_during_preview = true;
}
public void StopPreview()
{
if (m_is_during_preview == false)
return;
if (m_camera != null)
{
m_camera.stopPreview();
}
m_is_during_preview = false;
}
所述的紡絲畫面處理是試圖採取多個(預定數量)圖片,像突發模式:
final int multishot_count = 3;
...
public void TakePicture()
{
// Invoke multishot from UI when the camera preview is already started.
// startup of multishot task
...
m_take_picture_count = 0;
TakeOnePicture();
}
private void TakeOnePicture()
{
m_camera.takePicture(null, null, new Camera.PictureCallback()
{
@Override
public void onPictureTaken(byte[] data, final Camera camera)
{
m_take_picture_count++;
// feed the JPEG data to a queue and handle it in another thread
synchronized(m_jpeg_data_lock)
{
int data_size = data.length;
ByteBuffer buffer = ByteBuffer.allocateDirect(data_size);
buffer.put(data, 0, data_size);
m_jpeg_data_queue.offer(buffer);
if (m_take_picture_count == multishot_count)
m_is_all_pictures_taken = true;
}
if (m_take_picture_count < multishot_count)
{
StartPreviewAfterPictureTaken();
TakeOnePicture();
}
else
{
// Finalize the multishot task and process the image data
EndTakePictureTask end_take_picture_task = new EndTakePictureTask();
end_take_picture_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
}
private void StartPreviewAfterPictureTaken()
{
// Start preview in onPictureTaken() callback,
// which may get RuntimeException in some devices.
try
{
m_camera.startPreview();
}
catch (RuntimeException e)
{
LogError("Fail to start preview. Workaround: reopen camera.");
RestartCamera();
}
}
在許多示例代碼中,startPreview()僅在onPictureTaken()中調用。 StartPreviewAfterPictureTaken()過程用於處理startPreview可能的RuntimeException失敗。
我們不知道如何在第一個地方設置相機。顯示初始化相機的代碼以及如何拍攝照片。 –
爲什麼你不使用助手庫....檢查GitHub至少有10個好東西。不要重新發明輪子。 – OWADVL
你會推薦@OWADL –