Uso de SurfaceTexture llenado por la previsualización de la cámara como entrada de Renderscript Asignación en Jelly Bean

En Jelly Bean, ¿es posible crear una asignación de Renderscript fuera de un SurfaceTexture lleno de la vista previa de la cámara? Estoy construyendo mi aplicación desde el árbol de fuentes de Android, así que estoy bien usando @hide APIs como Allocation.setSurfaceTexture (). Sin embargo, me gustaría evitar el uso de las API despreciadas de RS Graphics. La pregunta similar aquí no recibió una respuesta completa y no era específica de JB.

Tengo los siguientes problemas al intentar el código a continuación:

  • Los datos que entran en Renderscript siempre son cero
  • Para la devolución de llamada onFrameAvailable para obtener repetidamente llamada, tengo que updateTexImage () ya que cuando llamo Allocation.ioReceive (), no volver a llamar más después de la primera vez, y hay un "EGLDisplay no válido" en el logcat. Sin embargo, pensé ioReceive () es el camino a seguir – también internamente updateTexImage ().
  • Los tipos de asignación soportados incluyen RGBA8888 pero no NV21 (que es el formato de previsualización de cámara), ¿cómo el código RS podrá direccionar los datos formateados de esta manera?

(Sé que el dispositivo con el que estoy trabajando admite la resolución VGA solicitada).

public class SampleRSCPCActivity extends Activity implements SurfaceTexture.OnFrameAvailableListener { final static int DO_KERNEL = 0; private static final String TAG="SAMPLERSCP"; private static Camera mCamera; private Camera.Parameters mParams; private int mFrameWidth, mFrameHeight; private static SurfaceTexture mST; private RenderScript mRS; private Allocation mInAllocation; private Allocation mOutAllocation; private ScriptC_mono mScript; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, "onCreate()"); createGUI(); createCamera(); createRSEnvironment(); } public void onPause() { Log.i(TAG, "onPause"); mCamera.stopPreview(); mCamera.release(); mCamera = null; super.onPause(); } private void createCamera() { mCamera = Camera.open(); mParams = mCamera.getParameters(); mFrameWidth = 640; mFrameHeight = 480; mParams.setPreviewSize(mFrameWidth, mFrameHeight); mParams.setPreviewFormat(ImageFormat.NV21); mCamera.setParameters(mParams); } private void createRSEnvironment () { mRS = RenderScript.create(this); mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono); Type.Builder b = new Type.Builder(mRS, Element.U8(mRS)); int usage = Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT; mInAllocation = Allocation.createTyped(mRS, b.setX(mFrameWidth).setY(mFrameHeight).create(), usage); mOutAllocation = Allocation.createTyped(mRS, b.setX(mFrameWidth).setY(mFrameHeight).create()); Log.i(TAG, "Getting SurfaceTexture from input Allocation"); mST = mInAllocation.getSurfaceTexture(); mST.setOnFrameAvailableListener(this); try { Log.i(TAG, "Setting SurfaceTexture for camera preview"); mCamera.setPreviewTexture(mST); Log.i(TAG, "Starting preview"); mCamera.startPreview(); } catch (IOException e) { Log.e(TAG, "Oops, something got wrong with setting the camera preview texture"); } } private void createGUI() { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.main); } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == DO_KERNEL) Log.i(TAG, "Calling RS kernel"); mST.updateTexImage(); // mInAllocation.ioReceive(); mScript.forEach_root(mInAllocation, mOutAllocation); Log.i(TAG, "Finishing RS"); mRS.finish(); Log.i(TAG, "Ok"); } }; public void onFrameAvailable(SurfaceTexture st) { Log.i(TAG, "onFrameAvailable callback"); handler.sendEmptyMessage(DO_KERNEL); } 

}

El código RS es bastante simple, sólo tratando de detectar datos no nulos:

 void root(const uchar *v_in, uchar *v_out, uint32_t x, uint32_t y) { if (*v_in != 0) rsDebug("input data non null !", *v_in); *v_out = (x / 640) * 255; 

}

Seguimiento de mi propia pregunta:

Resulta que la lectura de los búferes NV21 de una SurfaceTexture llenada por la cámara no es posible. Tuve que modificar el código fuente de Android para experimentar con esto (para los expertos: obtener el buffer actual de SurfaceTexture, a continuación, bloquearlo para obtener un puntero de buffer real – lo hice en RS Driver's rsdAllocationIoReceive ()). Esto habría sido genial para evitar la realización de una copia de búfer de la cámara a RS.

La última versión de JB (MR1) tiene una aplicación de prueba llamada LivePreview que realiza el procesamiento RS en la vista previa de la cámara. Sin embargo, utiliza buffers de devolución de llamada de vista previa de aplicación, que se copian a la asignación de entrada. Curiosamente, utiliza la nueva clase ScriptIntrinsicRgbToYuv para la conversión de color. El grueso de la conversión es el montaje de neón codificado a mano, así presumiblemente bastante rápido.

Incluso podría ser que el Nexus 10 (que tiene un controlador de RS con respaldo de Malí) hace esto en la GPU, me encantaría tener mis manos en este dispositivo para jugar. (Donadores bienvenidos ;-)!)

  • ¿Por qué Google eligió RenderScript en lugar de OpenCL
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.