7

Trong Jelly Bean, có thể tạo phân bổ Renderscript ra khỏi SurfaceTexture được lấp đầy bởi bản xem trước của máy ảnh không? Tôi đang xây dựng ứng dụng của mình từ bên trong cây nguồn Android vì vậy tôi sử dụng các API @hide như Allocation.setSurfaceTexture(). Tuy nhiên tôi muốn tránh sử dụng RS Graphics deprecated APIs. Câu hỏi tương tự here không được trả lời đầy đủ và không phải là JB cụ thể.Sử dụng SurfaceTexture được lấp đầy bởi Xem trước máy ảnh dưới dạng Phân bổ đầu vào Renderscript trong Jelly Bean

Tôi có vấn đề sau khi cố gắng vào mã bên dưới:

  • Các dữ liệu đi vào RenderScript luôn là zero
  • Đối với callback onFrameAvailable để liên tục kêu gọi, tôi phải updateTexImage() kể từ khi tôi gọi Allocation.ioReceive(), nó không được gọi trở lại nữa sau lần đầu tiên, và có một "EGLDisplay không hợp lệ" trong logcat. Tuy nhiên, tôi nghĩ ioReceive() là con đường để đi - nó nội bộ cũng updateTexImage().
  • Các loại phân bổ được hỗ trợ bao gồm RGBA8888 nhưng không NV21 (là định dạng xem trước máy ảnh), mã RS có thể định địa chỉ dữ liệu được định dạng theo cách này như thế nào?

(Tôi biết thiết bị tôi đang làm việc không hỗ trợ độ phân giải VGA được yêu cầu).

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

}

Mã RS là khá đơn giản, chỉ cần cố gắng để phát hiện dữ liệu không null:

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; 

}

Trả lời

6

Tiếp theo dõi về câu hỏi của riêng tôi:

Chỉ ra rằng việc đọc bộ đệm NV21 từ SurfaceTexture được lấp đầy bởi máy ảnh là không thể. Tôi đã phải sửa đổi mã nguồn Android để thử nghiệm với điều này (cho các chuyên gia: lấy bộ đệm hiện tại của SurfaceTexture, sau đó khóa nó để có được con trỏ đệm thực - tôi đã làm điều này trong rsdAllocationIoReceive() của Trình điều khiển RS. Điều này sẽ là tuyệt vời để tránh thực hiện một bản sao đệm từ máy ảnh đến RS.

Phiên bản JB mới nhất (phiên bản MR1) có ứng dụng thử nghiệm được gọi là LivePreview thực hiện xử lý RS trên bản xem trước máy ảnh. Tuy nhiên, nó sử dụng bộ đệm gọi lại xem trước do ứng dụng phân bổ, sau đó được sao chép vào Phân bổ đầu vào. Nó thú vị sử dụng lớp ScriptIntrinsicRgbToYuv mới cho việc chuyển đổi màu. Phần lớn chuyển đổi là cụm từ Neon được mã hóa bằng tay nên có lẽ là khá nhanh.

Thậm chí có thể là Nexus 10 (trong đó có trình điều khiển RS được Mali hậu thuẫn) thực hiện điều này trên GPU, tôi rất muốn có được bàn tay của mình trên thiết bị này để chơi cùng. (Các nhà tài trợ chào mừng ;-)!)

Các vấn đề liên quan