Browse Source

supporting Android9.0 and fixing capture image failed

main 2.3.0
Jiangdg 6 years ago
parent
commit
9092e8c9e0
  1. 5
      app/src/main/AndroidManifest.xml
  2. 3
      app/src/main/java/com/jiangdg/usbcamera/application/MyApplication.java
  3. 69
      app/src/main/java/com/jiangdg/usbcamera/utils/CrashHandler.java
  4. 2
      app/src/main/java/com/jiangdg/usbcamera/view/SplashActivity.java
  5. 7
      app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java
  6. 2
      app/src/main/res/values/strings.xml
  7. 2
      build.gradle
  8. 16
      libusbcamera/src/main/java/com/jiangdg/usbcamera/UVCCameraHelper.java
  9. 72
      libusbcamera/src/main/java/com/serenegiant/usb/USBMonitor.java
  10. 20
      libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java
  11. 7
      libusbcamera/src/main/res/xml/device_filter.xml

5
app/src/main/AndroidManifest.xml

@ -3,7 +3,10 @@
package="com.jiangdg.usbcamera"> package="com.jiangdg.usbcamera">
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.usb.host"/>
<application <application
android:name=".application.MyApplication" android:name=".application.MyApplication"

3
app/src/main/java/com/jiangdg/usbcamera/application/MyApplication.java

@ -13,13 +13,12 @@ import com.jiangdg.usbcamera.utils.CrashHandler;
public class MyApplication extends Application { public class MyApplication extends Application {
private CrashHandler mCrashHandler; private CrashHandler mCrashHandler;
// File Directory in sd card // File Directory in sd card
public static final String DIRECTORY_NAME = "UVCCamera"; public static final String DIRECTORY_NAME = "USBCamera";
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
mCrashHandler = CrashHandler.getInstance(); mCrashHandler = CrashHandler.getInstance();
mCrashHandler.init(getApplicationContext(), getClass()); mCrashHandler.init(getApplicationContext(), getClass());
} }
} }

69
app/src/main/java/com/jiangdg/usbcamera/utils/CrashHandler.java

@ -22,9 +22,9 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告. * UncaughtException handler class
* *
* @author user * @author jiangdg on 2017/6/27.
* *
*/ */
public class CrashHandler implements UncaughtExceptionHandler { public class CrashHandler implements UncaughtExceptionHandler {
@ -33,22 +33,16 @@ public class CrashHandler implements UncaughtExceptionHandler {
public static final String PROGRAM_BROKEN_ACTION = "com.teligen.wccp.PROGRAM_BROKEN"; public static final String PROGRAM_BROKEN_ACTION = "com.teligen.wccp.PROGRAM_BROKEN";
// 系统默认的UncaughtException处理类
private UncaughtExceptionHandler mDefaultHandler; private UncaughtExceptionHandler mDefaultHandler;
// CrashHandler实例
private static CrashHandler instance = new CrashHandler(); private static CrashHandler instance = new CrashHandler();
// 程序的Context对象
private Context mContext; private Context mContext;
// 程序的主Activity的class
private Class<?> mainActivityClass; private Class<?> mainActivityClass;
// 用来存储设备信息和异常信息
private Map<String, String> infos = new HashMap<String, String>(); private Map<String, String> infos = new HashMap<String, String>();
/** 保证只有一个CrashHandler实例 */
private CrashHandler() { private CrashHandler() {
} }
/** 获取CrashHandler实例 ,单例模式 */
public static CrashHandler getInstance() { public static CrashHandler getInstance() {
return instance; return instance;
} }
@ -56,22 +50,16 @@ public class CrashHandler implements UncaughtExceptionHandler {
public void init(Context context, Class<?> activityClass) { public void init(Context context, Class<?> activityClass) {
mContext = context; mContext = context;
this.setMainActivityClass(activityClass); this.setMainActivityClass(activityClass);
// 获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this); Thread.setDefaultUncaughtExceptionHandler(this);
} }
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override @Override
public void uncaughtException(Thread thread, Throwable ex) { public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) { if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex); mDefaultHandler.uncaughtException(thread, ex);
} else { } else {
//重启应用,释放资源
System.out.println("uncaughtException--->" + ex.getMessage()); System.out.println("uncaughtException--->" + ex.getMessage());
// Log.e(TAG, ex.getMessage()); // Log.e(TAG, ex.getMessage());
logError(ex); logError(ex);
@ -80,18 +68,10 @@ public class CrashHandler implements UncaughtExceptionHandler {
} catch (InterruptedException e) { } catch (InterruptedException e) {
// Log.e("debug", "error:", e); // Log.e("debug", "error:", e);
} }
// AppManagerUtils.removeAllActivities(); exitApp();
// AppManagerUtils.restartApp(mContext,mContext.getPackageName());
// AppManagerUtils.releaseAppResource();
} }
} }
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex) { private boolean handleException(Throwable ex) {
if (ex == null) { if (ex == null) {
return false; return false;
@ -101,25 +81,20 @@ public class CrashHandler implements UncaughtExceptionHandler {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
Toast.makeText(mContext.getApplicationContext(), Toast.makeText(mContext.getApplicationContext(),
"unknown exception!Please checking logs in sd card.", Toast.LENGTH_LONG).show(); "unknown exception and exiting...Please checking logs in sd card!", Toast.LENGTH_LONG).show();
Looper.loop(); Looper.loop();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
} }
}).start(); }).start();
// 收集设备参数信息
collectDeviceInfo(mContext.getApplicationContext()); collectDeviceInfo(mContext.getApplicationContext());
// 保存日志文件
logError(ex); logError(ex);
return true; return true;
} }
/** private void exitApp() {
* 收集设备参数信息 android.os.Process.killProcess(android.os.Process.myPid());
* System.exit(0);
* @param ctx }
*/
public void collectDeviceInfo(Context ctx) { public void collectDeviceInfo(Context ctx) {
try { try {
PackageManager pm = ctx.getPackageManager(); PackageManager pm = ctx.getPackageManager();
@ -133,7 +108,6 @@ public class CrashHandler implements UncaughtExceptionHandler {
infos.put("versionCode", versionCode); infos.put("versionCode", versionCode);
} }
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
// Log.e(TAG, "收集包信息出现错误", e);
} }
Field[] fields = Build.class.getDeclaredFields(); Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) { for (Field field : fields) {
@ -141,25 +115,19 @@ public class CrashHandler implements UncaughtExceptionHandler {
field.setAccessible(true); field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString()); infos.put(field.getName(), field.get(null).toString());
} catch (Exception e) { } catch (Exception e) {
// Log.e(TAG, "收集系统信息出现错误", e);
} }
} }
} }
/**
* 保存错误信息到文件中
*
* @param ex
* @return 返回文件名称,便于将文件传送到服务器
*/
private void logError(Throwable ex) { private void logError(Throwable ex) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
// for (Map.Entry<String, String> entry : infos.entrySet()) { for (Map.Entry<String, String> entry : infos.entrySet()) {
// String key = entry.getKey(); String key = entry.getKey();
// String value = entry.getValue(); String value = entry.getValue();
// sb.append(key + "=" + value + "\n"); sb.append(key + "=" + value + "\n");
// } }
int num = ex.getStackTrace().length; int num = ex.getStackTrace().length;
for (int i=0;i<num;i++){ for (int i=0;i<num;i++){
sb.append(ex.getStackTrace()[i].toString()); sb.append(ex.getStackTrace()[i].toString());
@ -170,7 +138,7 @@ public class CrashHandler implements UncaughtExceptionHandler {
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
fos = new FileOutputStream(file); fos = new FileOutputStream(file);
fos.write((sb.toString()+"异常:"+ex.getLocalizedMessage()).getBytes()); fos.write((sb.toString()+"exception:"+ex.getLocalizedMessage()).getBytes());
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} catch (IOException e) { } catch (IOException e) {
@ -182,7 +150,6 @@ public class CrashHandler implements UncaughtExceptionHandler {
e.printStackTrace(); e.printStackTrace();
} }
} }
// Log.e(TAG, "出现未捕捉异常,程序异常退出!", ex);
} }
public Class<?> getMainActivityClass() { public Class<?> getMainActivityClass() {

2
app/src/main/java/com/jiangdg/usbcamera/view/SplashActivity.java

@ -46,7 +46,7 @@ public class SplashActivity extends AppCompatActivity {
} }
private boolean isVersionM() { private boolean isVersionM() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
} }
private void checkAndRequestPermissions() { private void checkAndRequestPermissions() {

7
app/src/main/java/com/jiangdg/usbcamera/view/USBCameraActivity.java

@ -35,6 +35,7 @@ import com.serenegiant.usb.common.AbstractUVCCameraHandler;
import com.serenegiant.usb.encoder.RecordParams; import com.serenegiant.usb.encoder.RecordParams;
import com.serenegiant.usb.widget.CameraViewInterface; import com.serenegiant.usb.widget.CameraViewInterface;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -147,7 +148,7 @@ public class USBCameraActivity extends AppCompatActivity implements CameraDialog
mCameraHelper.setOnPreviewFrameListener(new AbstractUVCCameraHandler.OnPreViewResultListener() { mCameraHelper.setOnPreviewFrameListener(new AbstractUVCCameraHandler.OnPreViewResultListener() {
@Override @Override
public void onPreviewResult(byte[] nv21Yuv) { public void onPreviewResult(byte[] nv21Yuv) {
Log.d(TAG, "onPreviewResult: "+nv21Yuv.length);
} }
}); });
} }
@ -227,7 +228,7 @@ public class USBCameraActivity extends AppCompatActivity implements CameraDialog
showShortMsg("sorry,camera open failed"); showShortMsg("sorry,camera open failed");
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
String picPath = UVCCameraHelper.ROOT_PATH + MyApplication.DIRECTORY_NAME +"images/"+ System.currentTimeMillis() String picPath = UVCCameraHelper.ROOT_PATH + MyApplication.DIRECTORY_NAME +"/images/"+ System.currentTimeMillis()
+ UVCCameraHelper.SUFFIX_JPEG; + UVCCameraHelper.SUFFIX_JPEG;
mCameraHelper.capturePicture(picPath, new AbstractUVCCameraHandler.OnCaptureListener() { mCameraHelper.capturePicture(picPath, new AbstractUVCCameraHandler.OnCaptureListener() {
@Override @Override
@ -243,7 +244,7 @@ public class USBCameraActivity extends AppCompatActivity implements CameraDialog
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
if (!mCameraHelper.isPushing()) { if (!mCameraHelper.isPushing()) {
String videoPath = UVCCameraHelper.ROOT_PATH + MyApplication.DIRECTORY_NAME +"videos/"+ System.currentTimeMillis(); String videoPath = UVCCameraHelper.ROOT_PATH + MyApplication.DIRECTORY_NAME +"/videos/"+ System.currentTimeMillis();
FileUtils.createfile(FileUtils.ROOT_PATH + "test666.h264"); FileUtils.createfile(FileUtils.ROOT_PATH + "test666.h264");
// if you want to record,please create RecordParams like this // if you want to record,please create RecordParams like this
RecordParams params = new RecordParams(); RecordParams params = new RecordParams();

2
app/src/main/res/values/strings.xml

@ -1,3 +1,3 @@
<resources> <resources>
<string name="app_name">AndroidUSBCamera</string> <string name="app_name">USBCamera</string>
</resources> </resources>

2
build.gradle

@ -27,7 +27,7 @@ ext {
commonLibVersion= '2.12.4' commonLibVersion= '2.12.4'
versionCompiler = 27 versionCompiler = 27
versionTarget = 27 versionTarget = 27
minSdkVersion = 23 minSdkVersion = 21
versionNameString = '1.0.0' versionNameString = '1.0.0'
javaSourceCompatibility = JavaVersion.VERSION_1_8 javaSourceCompatibility = JavaVersion.VERSION_1_8
javaTargetCompatibility = JavaVersion.VERSION_1_8 javaTargetCompatibility = JavaVersion.VERSION_1_8

16
libusbcamera/src/main/java/com/jiangdg/usbcamera/UVCCameraHelper.java

@ -32,9 +32,10 @@ public class UVCCameraHelper {
private static final String TAG = "UVCCameraHelper"; private static final String TAG = "UVCCameraHelper";
private int previewWidth = 640; private int previewWidth = 640;
private int previewHeight = 480; private int previewHeight = 480;
// 高分辨率YUV格式帧率较低
public static final int FRAME_FORMAT_YUYV = UVCCamera.FRAME_FORMAT_YUYV; public static final int FRAME_FORMAT_YUYV = UVCCamera.FRAME_FORMAT_YUYV;
// 默认使用MJPEG // Default using MJPEG
// if your device is connected,but have no images
// please try to change it to FRAME_FORMAT_YUYV
public static final int FRAME_FORMAT_MJPEG = UVCCamera.FRAME_FORMAT_MJPEG; public static final int FRAME_FORMAT_MJPEG = UVCCamera.FRAME_FORMAT_MJPEG;
public static final int MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS; public static final int MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS;
public static final int MODE_CONTRAST = UVCCamera.PU_CONTRAST; public static final int MODE_CONTRAST = UVCCamera.PU_CONTRAST;
@ -108,13 +109,13 @@ public class UVCCameraHelper {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
// 休眠500ms,等待Camera创建完毕 // wait for camera created
try { try {
Thread.sleep(500); Thread.sleep(500);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
// 开启预览 // start previewing
startPreview(mCamView); startPreview(mCamView);
} }
}).start(); }).start();
@ -172,13 +173,13 @@ public class UVCCameraHelper {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
// 休眠500ms,等待Camera创建完毕 // wait for camera created
try { try {
Thread.sleep(500); Thread.sleep(500);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
// 开启预览 // start previewing
startPreview(mCamView); startPreview(mCamView);
} }
}).start(); }).start();
@ -238,7 +239,8 @@ public class UVCCameraHelper {
.getDeviceFilters(mActivity.getApplicationContext(), R.xml.device_filter); .getDeviceFilters(mActivity.getApplicationContext(), R.xml.device_filter);
if (mUSBMonitor == null || deviceFilters == null) if (mUSBMonitor == null || deviceFilters == null)
return null; return null;
return mUSBMonitor.getDeviceList(deviceFilters.get(0)); // matching all of filter devices
return mUSBMonitor.getDeviceList(deviceFilters);
} }
public void capturePicture(String savePath,AbstractUVCCameraHandler.OnCaptureListener listener) { public void capturePicture(String savePath,AbstractUVCCameraHandler.OnCaptureListener listener) {

72
libusbcamera/src/main/java/com/serenegiant/usb/USBMonitor.java

@ -30,11 +30,14 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.os.Build; import android.os.Build;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -43,9 +46,15 @@ import android.util.SparseArray;
import com.serenegiant.utils.BuildCheck; import com.serenegiant.utils.BuildCheck;
import com.serenegiant.utils.HandlerThreadHandler; import com.serenegiant.utils.HandlerThreadHandler;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -298,24 +307,65 @@ public final class USBMonitor {
*/ */
public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) throws IllegalStateException { public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed) throw new IllegalStateException("already destroyed"); if (destroyed) throw new IllegalStateException("already destroyed");
// get detected devices
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList(); final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
// store those devices info before matching filter xml file
String fileName = Environment.getExternalStorageDirectory()+ "/USBCamera/failed_devices.txt";
File logFile = new File(fileName);
FileWriter fw = null;
PrintWriter pw = null;
final List<UsbDevice> result = new ArrayList<UsbDevice>(); final List<UsbDevice> result = new ArrayList<UsbDevice>();
if (deviceList != null) { try {
if ((filters == null) || filters.isEmpty()) { fw = new FileWriter(logFile, true);
result.addAll(deviceList.values()); pw = new PrintWriter(fw);
} else { if (deviceList != null) {
for (final UsbDevice device: deviceList.values() ) { if ((filters == null) || filters.isEmpty()) {
for (final DeviceFilter filter: filters) { result.addAll(deviceList.values());
if ((filter != null) && filter.matches(device)) { } else {
// when filter matches for (final UsbDevice device: deviceList.values() ) {
if (!filter.isExclude) { // match devices
result.add(device); for (final DeviceFilter filter: filters) {
if ((filter != null) && filter.matches(device) || (filter != null && filter.mSubclass == device.getDeviceSubclass())) {
// when filter matches
if (!filter.isExclude) {
result.add(device);
}
break;
} else {
// collection failed dev's class and subclass
String devModel = android.os.Build.MODEL;
String devSystemVersion = android.os.Build.VERSION.RELEASE;
String devClass = String.valueOf(device.getDeviceClass());
String subClass = String.valueOf(device.getDeviceSubclass());
if(pw != null) {
StringBuilder sb = new StringBuilder();
sb.append(devModel);
sb.append("/");
sb.append(devSystemVersion);
sb.append(":");
sb.append("class="+devClass+", subclass="+subClass);
pw.println(sb.toString());
pw.flush();
fw.flush();
}
} }
break;
} }
} }
} }
} }
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
return result; return result;
} }

20
libusbcamera/src/main/java/com/serenegiant/usb/common/AbstractUVCCameraHandler.java

@ -813,8 +813,6 @@ public abstract class AbstractUVCCameraHandler extends Handler {
saveYuv2Jpeg(picPath, yuv); saveYuv2Jpeg(picPath, yuv);
} }
}).start(); }).start();
isCaptureStill = false;
} }
// 视频 // 视频
if (mH264Consumer != null) { if (mH264Consumer != null) {
@ -829,27 +827,23 @@ public abstract class AbstractUVCCameraHandler extends Handler {
ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length); ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
boolean result = yuvImage.compressToJpeg(new Rect(0, 0, mWidth, mHeight), 100, bos); boolean result = yuvImage.compressToJpeg(new Rect(0, 0, mWidth, mHeight), 100, bos);
if (result) { if (result) {
byte[] buffer = bos.toByteArray();
Bitmap bmp = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
byte[] buffer = bos.toByteArray();
File file = new File(path); File file = new File(path);
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
fos = new FileOutputStream(file); fos = new FileOutputStream(file);
// fixing bm is null bug instead of using BitmapFactory.decodeByteArray
fos.write(buffer);
fos.close();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
}
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
try {
fos.flush();
fos.close();
bmp.recycle();
if (mCaptureListener != null) {
mCaptureListener.onCaptureResult(path);
}
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (mCaptureListener != null) {
mCaptureListener.onCaptureResult(path);
}
} }
try { try {
bos.close(); bos.close();

7
libusbcamera/src/main/res/xml/device_filter.xml

@ -25,4 +25,11 @@
<usb> <usb>
<usb-device class="239" subclass="2" /> <!-- all device of UVC --> <usb-device class="239" subclass="2" /> <!-- all device of UVC -->
<!-- a few android 9.0 -->
<usb-device class="14" subclass="9" />
<usb-device class="2" subclass="0" />
<usb-device class="6" subclass="-1" />
<usb-device product-id="4836" vendor-id="9573" />
<usb-device product-id="2229" vendor-id="1133" />
<usb-device product-id="640" vendor-id="1409" />
</usb> </usb>

Loading…
Cancel
Save