title: Android Camera2 JNI Image Processing - Surface not being unlocked from JNI?
body: <p>I've followed the Camera2Video example and also implemented some JNI code to do some image processing on the Surface belonging to the SurfaceTexture for the preview on the screen. This works fine but when I hit the record button it fails to record as I get a onConfigureFailed() from the CameraCaptureSession.StateCallback().</p>
  
  <p>My main JNI function is here -</p>
  
  <pre><code>extern "C"
  jstring
  Java_com_example_android_camera2video_Camera2VideoFragment_someImageProcessingJNI(
          JNIEnv *env,
          jobject, /* this */
          jint srcWidth,
          jint srcHeight,
          jobject y_srcBuffer,
          jobject u_srcBuffer,
          jobject v_srcBuffer,
          jobject dstSurface) {
  
      // Get pointers to the 3 planes (YUV) of the source (Image from ImageReader).
  
      uint8_t *srcLumaPtr = reinterpret_cast&lt;uint8_t *&gt;(env-&gt;GetDirectBufferAddress(y_srcBuffer));
      if (srcLumaPtr == NULL) {
          return NULL;
      }
  
      uint8_t *srcUPtr = reinterpret_cast&lt;uint8_t *&gt;(env-&gt;GetDirectBufferAddress(u_srcBuffer));
      if (srcUPtr == NULL) {
          return NULL;
      }
  
      uint8_t *srcVPtr = reinterpret_cast&lt;uint8_t *&gt;(env-&gt;GetDirectBufferAddress(v_srcBuffer));
      if (srcVPtr == NULL) {
          return NULL;
      }
  
      // Destination setup.
      // YUV - NV21
  
      int dstWidth;
      int dstHeight;
  
      // Get a ptr to the destination window - don't forget to release it.
      ANativeWindow *dstWin = ANativeWindow_fromSurface(env, dstSurface);
  
      // Acquire a reference on the given ANativeWindow object. This prevents the object from being
      // deleted until the reference is removed.
      ANativeWindow_acquire(dstWin);
  
      // Swapping due to the rotation.
      dstWidth = srcHeight;
      dstHeight = srcWidth;
  
      // Change the format and size of the window buffers.
      int32_t setBufferGeometryRet = ANativeWindow_setBuffersGeometry(dstWin, dstWidth, dstHeight,
                                                                      0 /*format unchanged*/);
  
      // Struct that represents a windows buffer.
      ANativeWindow_Buffer dstBuf;
  
      // Lock the window's next drawing surface for writing.
      if (int32_t err = ANativeWindow_lock(dstWin, &amp;dstBuf, NULL)) {
          ANativeWindow_release(dstWin);
          return NULL;
      }
  
      // uint8_t* dstLumaPtr = reinterpret_cast&lt;uint8_t*&gt;(dstBuf.bits);
      // If we want to write as 32-bit.
      uint32_t *dstLumaPtr = reinterpret_cast&lt;uint32_t *&gt;(dstBuf.bits);
      uint32_t *dstLumaPtr_orig = dstLumaPtr;
  
      // Buffer
      int32_t ANativeWindow_Buffer_Width = dstBuf.width;
      int32_t ANativeWindow_Buffer_Height = dstBuf.height;
      int32_t ANativeWindow_Buffer_Stride = dstBuf.stride;
  
      // Window
      int32_t dstNativeWinWidth = ANativeWindow_getWidth(dstWin);
      int32_t dstNativeWinHeight = ANativeWindow_getHeight(dstWin);
      int32_t dstNativeWinFormat = ANativeWindow_getFormat(dstWin);
  
      // Read from the YUV source which needs the 90 deg clockwise rotation.
      for (int srcCol = 0; srcCol &lt; srcWidth; srcCol++) {
          for (int srcRow = srcHeight - 1; srcRow &gt;= 0; srcRow--) {
  
              // packRGBA() just converts YUV to RGB.
              *dstLumaPtr = packRGBA(srcLumaPtr[srcRow * srcWidth + srcCol],
                                     srcUPtr[((srcRow / 2) * srcWidth) + (srcCol - (srcCol % 2))],
                                     srcVPtr[((srcRow / 2) * srcWidth) + (srcCol - (srcCol % 2))]);
              dstLumaPtr++;
  
              // We cannot simple write to destination pixels sequentially because of the
              // stride. Stride is the actual memory buffer width, while the image width is only
              // the wdith of valid pixels.
              // If we reach the end of a source row, we need to advance our destination
              // pointer to skip the padding cells.
              if (srcRow == 0)
                  dstLumaPtr += (ANativeWindow_Buffer_Stride - ANativeWindow_Buffer_Width);
  
          }
      }
  
      // ----------------------------------------------------------------------
      // Some image processing done here...
      // --------------------------------------------------------------------
  
      ANativeWindow_unlockAndPost(dstWin);
      ANativeWindow_release(dstWin);
  
      return env-&gt;NewStringUTF("Return from JNI");
  }
  </code></pre>
  
  <p>Now if I remove the call to the JNI function, the record button works. This makes me believe I am not releasing the Surface properly within this JNI code but I am not sure how to proceed to fix this.</p>
  
  <p>Many thanks in advance for any advice.</p>
  
  <hr>
  
  <p>Edit - I am getting the following message in the logs.</p>
  
  <p>10-11 10:15:41.749 8736-8736/com.example.android.camera2video E/BufferQueueProducer: [SurfaceTexture-0-8736-0] connect: already connected (cur=2 req=4)</p>