La cámara aérea más baja de la CPU a GPU enfoque en Android

Mi aplicación necesita procesar los fotogramas de cámara en directo en la CPU, antes de procesarlos en la GPU. También hay algunas otras cosas que se prestan en la GPU, que depende de los resultados del procesamiento de la CPU, Por lo tanto, es importante mantener todo sincronizado para que no procesemos el propio marco en la GPU hasta que los resultados del procesamiento de la CPU para ese marco también estén disponibles.

La pregunta es cuál es el enfoque más bajo de arriba para esto en android?

El procesamiento de la CPU en mi caso sólo necesita una imagen en escala de grises, por lo que un formato YUV donde está embalado el plano Y es ideal (y tiende a ser una buena coincidencia con el formato nativo de los dispositivos de la cámara también). NV12, NV21 o YUV completamente plano proporcionaría un acceso de bajo nivel a escala de grises, por lo que sería preferible en el lado de la CPU.

En la API original de la cámara, setPreviewCallbackWithBuffer () era la única forma razonable de obtener datos en la CPU para su procesamiento. Esto tenía el plano Y separado por lo que era ideal para el procesamiento de la CPU. Conseguir este marco disponible para OpenGL para renderizar en una baja velocidad era el aspecto más desafiante. Al final escribí una rutina de conversión de color NEON para la salida RGB565 y sólo uso glTexSubImage2d para obtener esto disponible en la GPU. Esto se implementó por primera vez en el marco de tiempo Nexus 1, donde incluso una llamada de 320×240 glTexSubImage2d tomó 50ms de tiempo de CPU (los conductores pobres tratando de hacer swizzling textura supongo – esto fue significativamente mejorado en una actualización del sistema más adelante).

De vuelta en el día que busqué en cosas como extensiones eglImage, pero no parecen estar disponibles o bien documentado lo suficiente para aplicaciones de usuario. Tuve una pequeña mirada en las clases internas de GraphicsBuffer de Android, pero idealmente quiero permanecer en el mundo de APIs públicas soportadas.

La API android.hardware.camera2 tenía la promesa de poder adjuntar tanto un ImageReader como un SurfaceTexture a una sesión de captura. Desafortunadamente, no puedo ver ninguna manera de asegurar el oleoducto secuencial correcto aquí – retener la llamada a updateTexImage () hasta que la CPU haya procesado es bastante fácil, pero si otro frame ha llegado durante ese procesamiento, updateTexImage () saltará directamente a la última marco. También parece que con múltiples salidas habrá copias independientes de los marcos en cada una de las colas que idealmente me gustaría evitar.

Idealmente esto es lo que me gustaría:

  1. El controlador de la cámara llena la memoria con el último cuadro
  2. La CPU obtiene un puntero a los datos en la memoria, puede leer datos Y sin que se haga una copia
  3. CPU procesa datos y establece un indicador en mi código cuando el marco está listo
  4. Cuando comience a renderizar un marco, compruebe si un nuevo marco está listo
  5. Llame a algún API para enlazar la misma memoria que una textura GL
  6. Cuando un marco más nuevo está listo, suelte el búfer que mantiene el marco anterior de nuevo en la piscina

No puedo ver una manera de hacer exactamente ese estilo de copia cero con API pública en android, pero ¿qué es lo más cercano que es posible obtener?

Una cosa loca que he intentado que parece funcionar, pero no está documentado: El ANativeWindow NDK API puede aceptar datos formato NV12, aunque la constante de formato adecuado no es uno de los de los encabezados públicos. Eso permite que SurfaceTexture se rellene con datos NV12 por memcpy () para evitar la conversión de color del lado de la CPU y cualquier swizzling que suceda en el lado del controlador en glTexImage2d. Eso es todavía una copia extra de los datos, aunque que se siente como que debería ser innecesario, y de nuevo como es indocumentado no puede funcionar en todos los dispositivos. Una cámara de copia cero secuencial compatible -> ImageReader -> SurfaceTexture o equivalente sería perfecta.

La manera más eficiente de procesar video es evitar la CPU por completo, pero suena como que no es una opción para usted. Las API públicas generalmente están orientadas a hacer todo en hardware, ya que es lo que el propio framework necesita, aunque hay algunas rutas para RenderScript. (Supongo que has visto la demo del filtro Grafika que usa shaders de fragmentos).

El acceso a los datos de la CPU se utiliza para indicar APIs de cámaras lentas o trabajar con GraphicBuffer y funciones EGL relativamente oscuras (por ejemplo, esta pregunta ). El punto de ImageReader era proporcionar acceso de copia cero a los datos YUV de la cámara.

Realmente no se puede serializar Camera -> ImageReader -> SurfaceTexture como ImageReader no tiene una API "forward the buffer". Lo cual es lamentable, ya que eso haría trivial. Podrías intentar replicar lo que SurfaceTexture hace, usando las funciones de EGL para empaquetar el búfer como una textura externa, pero de nuevo te encuentras en una zona no pública de GraphicBuffer, y me preocupo por los problemas de propiedad / vida útil del búfer.

No estoy seguro de cómo los caminos paralelos le ayudan (Camera2 -> ImageReader, Camera2 -> SurfaceTexture), ya que lo que está siendo enviado a SurfaceTexture no tendría sus modificaciones. FWIW, no implica una copia extra – en Lollipop o parecido, BufferQueue se actualizó para permitir que los buffers individuales se muevan a través de varias colas.

Es totalmente posible que haya algunas nuevas APIs de fantasía que no he visto todavía, pero por lo que sé que su enfoque ANativeWindow es probablemente el ganador. Sospecho que usted estaría mejor apagado con uno de los formatos de la cámara (YV12 o NV21) que NV12, pero no sé para seguro.

FWIW, soltará cuadros si su procesamiento toma demasiado tiempo, pero a menos que su proceso sea desigual (algunos cuadros tardan mucho más que otros) usted tendrá que caer los marcos no importa qué. Entrar en el reino de las APIs no públicas de nuevo, puede cambiar el SurfaceTexture a modo "síncrono", pero si los búferes se llenan todavía está cayendo cuadros.

  • Visualización del flujo de la cámara en un GLSurfaceView a través de SurfaceTexture
  • Cómo utilizar android ndk para acceder a la cámara
  • Zxing - Cambio de la vista de la cámara -90 grados
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.