7

In Jelly Bean, è possibile creare un'allocazione Renderscript da SurfaceTexture riempita dall'anteprima della videocamera? Sto costruendo la mia applicazione all'interno dell'albero dei sorgenti di Android, quindi sto bene usando le API @hide come Allocation.setSurfaceTexture(). Tuttavia, vorrei evitare di utilizzare le API deprecate di RS Graphics. La domanda simile here non ha ricevuto risposta completa e non era specifica per JB.Uso di SurfaceTexture riempito dall'anteprima della fotocamera come input Renderscript Allocazione in Jelly Bean

ho i seguenti problemi quando si cerca il codice qui sotto:

  • I dati di entrare in Renderscript è sempre zero
  • Per la richiamata onFrameAvailable per ottenere più volte chiamato, devo updateTexImage() da quando ho chiama Allocation.ioReceive(), non viene richiamato più dopo la prima volta e c'è un "EGLDisplay non valido" nel logcat. Eppure pensavo che ioReceive() fosse la strada da percorrere - internamente anche updateTexImage().
  • I tipi di allocazione supportati includono RGBA8888 ma non NV21 (che è il formato di anteprima della videocamera), in che modo il codice RS sarà in grado di indirizzare i dati formattati in questo modo?

(So che il dispositivo con cui sto lavorando supporta la risoluzione VGA richiesta).

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); 
} 

}

Il codice RS è abbastanza semplice, solo cercando di rilevare i dati non nullo:

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; 

}

risposta

6

seguito-up sulla mia domanda:

Si scopre che la lettura dei buffer NV21 da SurfaceTexture riempita dalla telecamera non è possibile. Ho dovuto modificare il codice sorgente di Android per sperimentarlo (per gli esperti: ottenere il buffer corrente di SurfaceTexture, quindi bloccarlo per ottenere un vero puntatore del buffer - l'ho fatto nel RS Driver's rsdAllocationIoReceive()). Sarebbe stato grandioso evitare di eseguire una copia buffer dalla fotocamera alla RS.

L'ultima versione JB (MR1) ha un'applicazione di test chiamata LivePreview che esegue l'elaborazione RS nell'anteprima della telecamera. Tuttavia, utilizza i buffer di callback di anteprima allocati dall'applicazione, che sono quindi copiati nell'assegnazione di input. Interessantemente utilizza la nuova classe ScriptIntrinsicRgbToYuv per la conversione del colore. La maggior parte della conversione è assemblata a mano con codice Neon, quindi presumibilmente abbastanza veloce.

Potrebbe anche darsi che il Nexus 10 (che ha un Driver RS ​​con supporto Mali) fa questo sulla GPU, mi piacerebbe mettere le mani su questo dispositivo con cui giocare. (I donatori sono i benvenuti ;-)!)

Problemi correlati