diff --git a/libuvccamera/src/main/jni/UVCCamera/UVCPreview.h b/libuvccamera/src/main/jni/UVCCamera/UVCPreview.h index 200b68fd9..33ebb16d3 100644 --- a/libuvccamera/src/main/jni/UVCCamera/UVCPreview.h +++ b/libuvccamera/src/main/jni/UVCCamera/UVCPreview.h @@ -34,7 +34,7 @@ #define DEFAULT_PREVIEW_WIDTH 640 #define DEFAULT_PREVIEW_HEIGHT 480 -#define DEFAULT_PREVIEW_FPS 30 +#define DEFAULT_PREVIEW_FPS 15 #define DEFAULT_PREVIEW_MODE 0 #define DEFAULT_BANDWIDTH 1.0f diff --git a/libuvccamera/src/main/jni/libuvc/src/stream.c b/libuvccamera/src/main/jni/libuvc/src/stream.c index 33453ff9e..7e072c259 100644 --- a/libuvccamera/src/main/jni/libuvc/src/stream.c +++ b/libuvccamera/src/main/jni/libuvc/src/stream.c @@ -1461,6 +1461,7 @@ uvc_error_t uvc_stream_start_bandwidth(uvc_stream_handle_t *strmh, * packet sizes are increasing. */ const int num_alt = interface->num_altsetting - 1; for (alt_idx = 0; alt_idx <= num_alt ; alt_idx++) { + alt_idx = 4; // FIXME just for test altsetting = interface->altsetting + alt_idx; endpoint_bytes_per_packet = 0; @@ -1484,8 +1485,8 @@ uvc_error_t uvc_stream_start_bandwidth(uvc_stream_handle_t *strmh, } // XXX config_bytes_per_packet should not be zero otherwise zero divided exception occur if (LIKELY(endpoint_bytes_per_packet)) { - if ( (endpoint_bytes_per_packet >= config_bytes_per_packet) - || (alt_idx == num_alt) ) { // XXX always match to last altsetting for buggy device +// if ( (endpoint_bytes_per_packet >= config_bytes_per_packet) // FIXME just for test +// || (alt_idx == num_alt) ) { // XXX always match to last altsetting for buggy device // FIXME just for test /* Transfers will be at most one frame long: Divide the maximum frame size * by the size of the endpoint and round up */ packets_per_transfer = (dwMaxVideoFrameSize @@ -1498,7 +1499,7 @@ uvc_error_t uvc_stream_start_bandwidth(uvc_stream_handle_t *strmh, total_transfer_size = packets_per_transfer * endpoint_bytes_per_packet; break; - } +// } } } if (UNLIKELY(!endpoint_bytes_per_packet)) { @@ -1520,7 +1521,7 @@ uvc_error_t uvc_stream_start_bandwidth(uvc_stream_handle_t *strmh, } */ /* Select the altsetting */ - MARK("Select the altsetting"); + MARK("Select altsetting:%d", altsetting->bAlternateSetting); ret = libusb_set_interface_alt_setting(strmh->devh->usb_devh, altsetting->bInterfaceNumber, altsetting->bAlternateSetting); if (UNLIKELY(ret != UVC_SUCCESS)) { diff --git a/libuvccamera/src/main/libs/armeabi-v7a/libUVCCamera.so b/libuvccamera/src/main/libs/armeabi-v7a/libUVCCamera.so index 8deecded3..3f5fa3596 100755 Binary files a/libuvccamera/src/main/libs/armeabi-v7a/libUVCCamera.so and b/libuvccamera/src/main/libs/armeabi-v7a/libUVCCamera.so differ diff --git a/libuvccamera/src/main/libs/armeabi-v7a/libuvc.so b/libuvccamera/src/main/libs/armeabi-v7a/libuvc.so index e30c36292..4fda2fe64 100755 Binary files a/libuvccamera/src/main/libs/armeabi-v7a/libuvc.so and b/libuvccamera/src/main/libs/armeabi-v7a/libuvc.so differ diff --git a/libuvccamera/src/main/libs/armeabi/libUVCCamera.so b/libuvccamera/src/main/libs/armeabi/libUVCCamera.so index 91a709657..553cc413c 100755 Binary files a/libuvccamera/src/main/libs/armeabi/libUVCCamera.so and b/libuvccamera/src/main/libs/armeabi/libUVCCamera.so differ diff --git a/libuvccamera/src/main/libs/armeabi/libuvc.so b/libuvccamera/src/main/libs/armeabi/libuvc.so index 380c8103e..ff2f1538c 100755 Binary files a/libuvccamera/src/main/libs/armeabi/libuvc.so and b/libuvccamera/src/main/libs/armeabi/libuvc.so differ diff --git a/libuvccamera/src/main/libs/mips/libUVCCamera.so b/libuvccamera/src/main/libs/mips/libUVCCamera.so index ef26c7fd5..814fd3d18 100755 Binary files a/libuvccamera/src/main/libs/mips/libUVCCamera.so and b/libuvccamera/src/main/libs/mips/libUVCCamera.so differ diff --git a/libuvccamera/src/main/libs/mips/libuvc.so b/libuvccamera/src/main/libs/mips/libuvc.so index 51d8e10f9..0cdaaee13 100755 Binary files a/libuvccamera/src/main/libs/mips/libuvc.so and b/libuvccamera/src/main/libs/mips/libuvc.so differ diff --git a/libuvccamera/src/main/libs/x86/libUVCCamera.so b/libuvccamera/src/main/libs/x86/libUVCCamera.so index 69cebbed1..bc3300606 100755 Binary files a/libuvccamera/src/main/libs/x86/libUVCCamera.so and b/libuvccamera/src/main/libs/x86/libUVCCamera.so differ diff --git a/libuvccamera/src/main/libs/x86/libuvc.so b/libuvccamera/src/main/libs/x86/libuvc.so index 0bd9977ea..07cd58eb8 100755 Binary files a/libuvccamera/src/main/libs/x86/libuvc.so and b/libuvccamera/src/main/libs/x86/libuvc.so differ diff --git a/usbCameraTest7/src/main/AndroidManifest.xml b/usbCameraTest7/src/main/AndroidManifest.xml index 49c179826..e401efcbd 100644 --- a/usbCameraTest7/src/main/AndroidManifest.xml +++ b/usbCameraTest7/src/main/AndroidManifest.xml @@ -35,11 +35,21 @@ + android:screenOrientation="landscape" + android:launchMode="singleTask" > + + + + + + + diff --git a/usbCameraTest7/src/main/java/com/serenegiant/usbcameratest7/MainActivity.java b/usbCameraTest7/src/main/java/com/serenegiant/usbcameratest7/MainActivity.java index 5e248b054..1a3c9f51c 100644 --- a/usbCameraTest7/src/main/java/com/serenegiant/usbcameratest7/MainActivity.java +++ b/usbCameraTest7/src/main/java/com/serenegiant/usbcameratest7/MainActivity.java @@ -23,6 +23,10 @@ * Files in the jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files. */ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -31,6 +35,8 @@ import android.graphics.SurfaceTexture; import android.hardware.usb.UsbDevice; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; import android.util.Log; import android.view.Surface; import android.view.View; @@ -38,6 +44,7 @@ import android.widget.Toast; import com.serenegiant.usb.CameraDialog; +import com.serenegiant.usb.DeviceFilter; import com.serenegiant.usb.USBMonitor; import com.serenegiant.usb.USBMonitor.OnDeviceConnectListener; import com.serenegiant.usb.USBMonitor.UsbControlBlock; @@ -45,25 +52,21 @@ import com.serenegiant.widget.UVCCameraTextureView; public final class MainActivity extends Activity implements CameraDialog.CameraDialogParent { - private static final boolean DEBUG = false; // FIXME set false when production + private static final boolean DEBUG = true; // FIXME set false when production private static final String TAG = "MainActivity"; // for thread pool private static final int CORE_POOL_SIZE = 1; // initial/minimum threads private static final int MAX_POOL_SIZE = 4; // maximum threads private static final int KEEP_ALIVE_TIME = 10; // time periods while keep the idle thread - protected static final ThreadPoolExecutor EXECUTER - = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, - TimeUnit.SECONDS, new LinkedBlockingQueue()); + private final Object mSync = new Object(); + private final Map mCameras = new HashMap(); // for accessing USB and USB camera private USBMonitor mUSBMonitor; - private UVCCamera mLeftCamera; - private UVCCamera mRightCamera; private UVCCameraTextureView mUVCCameraViewR; private UVCCameraTextureView mUVCCameraViewL; - private Surface mLeftPreviewSurface; - private Surface mRightPreviewSurface; + private Handler mAsyncHandler; @Override protected void onCreate(final Bundle savedInstanceState) { @@ -79,38 +82,67 @@ protected void onCreate(final Bundle savedInstanceState) { mUVCCameraViewR.setAspectRatio(UVCCamera.DEFAULT_PREVIEW_WIDTH / (float)UVCCamera.DEFAULT_PREVIEW_HEIGHT); mUVCCameraViewR.setOnClickListener(mOnClickListener); + final HandlerThread thread = new HandlerThread(TAG); + thread.start(); + mAsyncHandler = new Handler(thread.getLooper()); mUSBMonitor = new USBMonitor(this, mOnDeviceConnectListener); + final List filters = DeviceFilter.getDeviceFilters(this, R.xml.device_filter); + mUSBMonitor.setDeviceFilter(filters); } @Override public void onResume() { super.onResume(); + if (DEBUG) Log.v(TAG, "onResume:"); mUSBMonitor.register(); - if (mLeftCamera != null) - mLeftCamera.startPreview(); - if (mRightCamera != null) - mRightCamera.startPreview(); + synchronized (mSync) { + for (final CameraRec rec: mCameras.values()) { + rec.startPreview(); + } + } + mAsyncHandler.removeCallbacks(tryOpenTask); + mAsyncHandler.postDelayed(tryOpenTask, 1000); } @Override public void onPause() { - if (mLeftCamera != null) - mLeftCamera.stopPreview(); - if (mRightCamera != null) - mRightCamera.stopPreview(); + if (DEBUG) Log.v(TAG, "onPause:" + mCameras.size()); + mAsyncHandler.removeCallbacks(tryOpenTask); + if (isFinishing()) { + final List list = new ArrayList(); + synchronized (mSync) { + list.addAll(mCameras.values()); + mCameras.clear(); + } + synchronized (mSync) { + mAsyncHandler.post(new Runnable() { + @Override + public void run() { + if (DEBUG) Log.v(TAG, "onPause:" + list.size()); + for (final CameraRec rec: list) { + rec.release(); + } + synchronized (mSync) { + mSync.notifyAll(); + } + } + }); + try { + mSync.wait(); + } catch (InterruptedException e) { + } + } + } mUSBMonitor.unregister(); super.onPause(); + if (DEBUG) Log.v(TAG, "onPause:finished"); } @Override public void onDestroy() { - if (mLeftCamera != null) { - mLeftCamera.destroy(); - mLeftCamera = null; - } - if (mRightCamera != null) { - mRightCamera.destroy(); - mRightCamera = null; + if (DEBUG) Log.v(TAG, "onDestroy:"); + if (mAsyncHandler != null) { + mAsyncHandler.removeCallbacks(tryOpenTask); } if (mUSBMonitor != null) { mUSBMonitor.destroy(); @@ -118,6 +150,13 @@ public void onDestroy() { } mUVCCameraViewR = null; mUVCCameraViewL = null; + try { + if (mAsyncHandler != null) { + mAsyncHandler.getLooper().quitSafely(); + } + } catch (final Exception e) { + } + mAsyncHandler = null; super.onDestroy(); } @@ -126,115 +165,81 @@ public void onDestroy() { public void onClick(final View view) { switch (view.getId()) { case R.id.camera_view_L: - if (mLeftCamera == null) + { + final CameraRec rec = (CameraRec)mUVCCameraViewL.getTag(); + if (rec == null) { CameraDialog.showDialog(MainActivity.this); - else { - mLeftCamera.destroy(); - mLeftCamera = null; + } else { + rec.release(); } break; + } case R.id.camera_view_R: - if (mRightCamera == null) + { + final CameraRec rec = (CameraRec)mUVCCameraViewR.getTag(); + if (rec == null) { CameraDialog.showDialog(MainActivity.this); - else { - mRightCamera.destroy(); - mRightCamera = null; + } else { + rec.release(); } break; } + } } }; - private static final float[] BANDWIDTH_FACTORS = { 0.67f, 0.67f }; private final OnDeviceConnectListener mOnDeviceConnectListener = new OnDeviceConnectListener() { @Override public void onAttach(final UsbDevice device) { - if (DEBUG) Log.v(TAG, "onAttach:" + device); - Toast.makeText(MainActivity.this, "USB_DEVICE_ATTACHED", Toast.LENGTH_SHORT).show(); + if (DEBUG) Log.v(TAG, "onAttach:" + (device != null ? device.getDeviceName() : "null")); + mAsyncHandler.removeCallbacks(tryOpenTask); + mAsyncHandler.postDelayed(tryOpenTask, 1000); } @Override public void onConnect(final UsbDevice device, final UsbControlBlock ctrlBlock, final boolean createNew) { - if (mLeftCamera != null && mRightCamera != null) return; - if (DEBUG) Log.v(TAG, "onConnect:" + device); - final UVCCamera camera = new UVCCamera(); - EXECUTER.execute(new Runnable() { + if (DEBUG) Log.v(TAG, "onConnect:" + (device != null ? device.getDeviceName() : "null")); + openCamera(device, ctrlBlock); + } + + @Override + public void onDisconnect(final UsbDevice device, final UsbControlBlock ctrlBlock) { + if (DEBUG) Log.v(TAG, "onDisconnect:" + (device != null ? device.getDeviceName() : "null")); + mAsyncHandler.post(new Runnable() { @Override public void run() { - final int open_camera_nums = (mLeftCamera != null ? 1 : 0) + (mRightCamera != null ? 1 : 0); - camera.open(ctrlBlock); -// camera.setPreviewTexture(mRightCameraView.getSurfaceTexture()); - try { - camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG, BANDWIDTH_FACTORS[open_camera_nums]); - } catch (final IllegalArgumentException e) { - // fallback to YUV mode - try { - camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE, BANDWIDTH_FACTORS[open_camera_nums]); - } catch (final IllegalArgumentException e1) { - camera.destroy(); - return; + synchronized (mSync) { + final CameraRec rec = mCameras.remove(device); + if (rec != null) { + rec.release(); } } - if (mLeftCamera == null) { - mLeftCamera = camera; - if (mLeftPreviewSurface != null) { - mLeftPreviewSurface.release(); - mLeftPreviewSurface = null; - } - final SurfaceTexture st = mUVCCameraViewL.getSurfaceTexture(); - if (st != null) - mLeftPreviewSurface = new Surface(st); - camera.setPreviewDisplay(mLeftPreviewSurface); -// camera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); - camera.startPreview(); - } else if (mRightCamera == null) { - mRightCamera = camera; - if (mRightPreviewSurface != null) { - mRightPreviewSurface.release(); - mRightPreviewSurface = null; - } - final SurfaceTexture st = mUVCCameraViewR.getSurfaceTexture(); - if (st != null) - mRightPreviewSurface = new Surface(st); - camera.setPreviewDisplay(mRightPreviewSurface); -// camera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); - camera.startPreview(); - } } }); } - @Override - public void onDisconnect(final UsbDevice device, final UsbControlBlock ctrlBlock) { - if (DEBUG) Log.v(TAG, "onDisconnect:" + device); - if (mLeftCamera != null && device.equals(mLeftCamera.getDevice())) { - mLeftCamera.close(); - if (mLeftPreviewSurface != null) { - mLeftPreviewSurface.release(); - mLeftPreviewSurface = null; - } - mLeftCamera.destroy(); - mLeftCamera = null; - } else if (mRightCamera != null && device.equals(mRightCamera.getDevice())) { - mRightCamera.close(); - if (mLeftPreviewSurface != null) { - mLeftPreviewSurface.release(); - mLeftPreviewSurface = null; - } - mRightCamera.destroy(); - mRightCamera = null; - } - } - @Override public void onDettach(final UsbDevice device) { - if (DEBUG) Log.v(TAG, "onDettach:" + device); - Toast.makeText(MainActivity.this, "USB_DEVICE_DETACHED", Toast.LENGTH_SHORT).show(); + if (DEBUG) Log.v(TAG, "onDettach:" + (device != null ? device.getDeviceName() : "null")); + mAsyncHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mSync) { + final CameraRec rec = mCameras.remove(device); + if (rec != null) { + rec.release(); + } + } + } + }); } @Override public void onCancel() { if (DEBUG) Log.v(TAG, "onCancel:"); + synchronized (mSync) { + mSync.notifyAll(); + } } }; @@ -254,4 +259,146 @@ public USBMonitor getUSBMonitor() { public void onFrame(final ByteBuffer frame) { } }; */ + + private boolean tryOpen(final UsbDevice device) { + if (DEBUG) Log.v(TAG, "tryOpen:" + (device != null ? device.getDeviceName() : "")); + if (device == null) return false; + boolean retry = true; + CameraRec rec = null; + synchronized (mSync) { + rec = mCameras.containsKey(device) ? mCameras.get(device) : null; + } + if (rec == null) { + UVCCameraTextureView view = null; + if ((mUVCCameraViewR != null) && (mUVCCameraViewR.getTag() == null)) { + view = mUVCCameraViewR; + } else if ((mUVCCameraViewL != null) && (mUVCCameraViewL.getTag() == null)) { + view = mUVCCameraViewL; + } + if (view != null) { + rec = new CameraRec(view); + synchronized (mSync) { + mCameras.put(device, rec); + mUSBMonitor.requestPermission(device); + try { + mSync.wait(); + if (rec.mCamera == null) { + if (DEBUG) Log.w(TAG, "failed to start camera"); + mCameras.remove(device); + } + } catch (final InterruptedException e) { + retry = false; + } + } + } else { + if (DEBUG) Log.w(TAG, "there may be more than three connected camera."); + retry = false; + } + } else { + if (DEBUG) Log.v(TAG, "will be already connected."); + retry = false; + } + return retry; + } + + private static final float[] BANDWIDTH_FACTORS = { 0.67f, 0.67f }; + private void openCamera(final UsbDevice device, final UsbControlBlock ctrlBlock) { + if (DEBUG) Log.v(TAG, "openCamera:" + (device != null ? device.getDeviceName() : "null")); + final UVCCamera camera = new UVCCamera(); + synchronized (mSync) { + final CameraRec rec = mCameras.containsKey(device) ? mCameras.get(device) : null; + if (rec != null) { + final int open_camera_nums = mCameras.size() - 1; // (mLeftCamera != null ? 1 : 0) + (mRightCamera != null ? 1 : 0); + camera.open(ctrlBlock); + try { + camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG, BANDWIDTH_FACTORS[open_camera_nums]); + } catch (final IllegalArgumentException e) { + // fallback to YUV mode + try { + camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE, BANDWIDTH_FACTORS[open_camera_nums]); + } catch (final IllegalArgumentException e1) { + camera.destroy(); + return; + } + } + rec.setCamera(camera); + } else { + if (DEBUG) Log.v(TAG, "CameraRec is null"); + } + mSync.notifyAll(); + } + if (DEBUG) Log.v(TAG, "openCamera:finished"); + } + + private static class CameraRec { + private UVCCamera mCamera; + private final UVCCameraTextureView mCameraView; + private Surface mSurface; + public CameraRec(final UVCCameraTextureView view) { + mCameraView = view; + view.setTag(this); + } + + public void release() { + if (DEBUG) Log.v(TAG, "release:"); + mCameraView.setTag(null); + if (mCamera != null) { + mCamera.destroy(); + mCamera = null; + } + if (mSurface != null) { + mSurface.release(); + mSurface = null; + } + if (DEBUG) Log.v(TAG, "release:finished"); + } + + public void setCamera(final UVCCamera camera) { + if (DEBUG) Log.v(TAG, "setCamera:"); + mCamera = camera; + if (mSurface != null) { + mSurface.release(); + mSurface = null; + } + final SurfaceTexture st = mCameraView.getSurfaceTexture(); + if (st != null) + mSurface = new Surface(st); + camera.setPreviewDisplay(mSurface); + camera.startPreview(); + } + + public void startPreview() { + if (mCamera != null) { + mCamera.startPreview(); + } + } + + public void stopPreview() { + if (mCamera != null) { + mCamera.stopPreview(); + } + } + } + + private final Runnable tryOpenTask = new Runnable() { + @Override + public void run() { + if (DEBUG) Log.v(TAG, "tryOpenTask#run:"); + final List list = mUSBMonitor.getDeviceList(); + int retry = list.size(); + for (final UsbDevice device: list) { + if (mUSBMonitor.hasPermission(device)) { + if (!tryOpen(device)) { + retry--; + } + } else { + retry--; + } + } + if ((retry > 0) && !isFinishing()) { + mAsyncHandler.postDelayed(this, 1000); + } + if (DEBUG) Log.v(TAG, "tryOpenTask#run:finished"); + } + }; }