2015-11-11 2 views
1

Я пишу APP для съемки с камеры Android. Коды показаны in this question. В настоящее время мне не нужно обрабатывать рамку предварительного просмотра, поэтому setPreviewCallback() не используется для камеры.Камера для Android замораживается при запуске (без ошибок)

На устройстве HTC Sensation с ОЗУ 768 МБ камера имеет некоторые странные действия в процессе съемки. Одним из них является то, что после съемки и получения данных в обратном вызове onPictureTaken() startPreview() (чтобы сделать следующее изображение) дает исключение RuntimeException. В настоящее время я могу только поймать исключение и закрыть/снова открыть камеру в качестве обходного пути.

Однако на этом устройстве существует более серьезная проблема. Иногда я снова открываю камеру, но startPreview() дает мне замороженный фрейм без исключения или сообщение об ошибке. Мой AP не падает. Он все равно может закрыть камеру и выйти, но не может делать больше снимков (я получаю исключение во время выполнения startPreview() или takePicture() сбой). Когда это произойдет, встроенная камера APP также будет отключена, если я не перезапущу устройство.

Как тест стабильности, я беру фотографии, получаю данные JPEG, но не записываю их в файлы. Когда я беру около 100 фотографий, мне нужно снова открыть камеру примерно 10 раз, и, наконец, камера сломается. Если я дешифровать данные JPEG в памяти BitmapFactory (все еще не записывать в файлы), скорость повторного открытия/замораживания значительно увеличивается.

я получаю эти сообщения об ошибке при 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 

Когда предварительный просмотр замерзает, я не получаю сообщение об ошибке или связанных сообщений. В то время даже нет сообщения об исключении из памяти. Если я закрываю приложение и запустить встроенную камеру APP, он выходит сразу с этими ошибками:

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(). Затем я должен перезапустить устройство. Поскольку у устройства небольшой размер оперативной памяти 768 МБ, я подозреваю, что основной проблемой является нехватка памяти, хотя я не получаю исключение OOM непосредственно из своего APP.

я испытал около 500 пробегов с ~ 15 раз перезапуска устройства, и обнаружили, что:

  1. startPreview() не только с RuntimeException после съемки и получать onPictureTaken() обратного вызова, так как камера открыта ,
  2. Предварительный просмотр замороженной проблемы возникает только при повторном открытии камеры после сбоя startPreview(). Это происходит не сразу от startPreview(), когда все в порядке.

Я знаю, что если мой APP аварийно завершает работу, не закрывая камеру, он может заблокировать камеру. Но я проверил, что мой APP все еще закрывается и отпускает камеру после того, как проблема (но камера недоступна до перезапуска устройства). Кажется, что что-то не так внутри камеры. Может ли кто-нибудь мне помочь?

Edit: Вот коды об открытии камеры и начала просмотра:

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() используется для обработки возможного исключения RuntimeException для startPreview.

+0

Мы не знаем, как вы устанавливаете камеру в первую очередь. Покажите код, в котором вы инициализируете камеру, и как вы делаете снимки. –

+0

почему вы не используете вспомогательную библиотеку .... проверьте, что у GitHub есть как минимум 10 полезных. не изобретать велосипед. – OWADVL

+0

Что бы вы порекомендовали @OWADL –

ответ

2

Спасибо за предложение от Alex Cohn, я нашел решение. Это решение тестируется только на устройстве HTC Sensation, но оно оказывает значительное влияние на это устройство.

Я просто поставить команду спать до startPreview(), который будет называться в onPictureTaken():

try 
{ 
    Thread.sleep(20); 
} 
catch (Exception e) 
{ 
    e.printStackTrace(); 
} 

Затем я запустить тест в 100 раз, где каждый прогон занимает 3 кадра подряд. Со сном я не получил никаких ошибок из startPreview() в 100 прогонах. Если нет сна, мне нужно повторно открыть камеру 78 раз и перезапустить устройство 8 раз за 100 прогонов.

+0

Очень полезная информация. Это просто решение, но я думаю, что это нормально для этого случая. Я снова проверю и дам вам знать результат! – Phuong

Смежные вопросы