修改速度取值,更新行车记录仪代码。
DEMO必须依附于预览获取YUV数据,不符合需求,这里先接入直播,后续需要按照源码抽离直接通过USB获取YUV的服务

Signed-off-by: donghongyu <donghongyu@zhidaoauto.com>
This commit is contained in:
donghongyu
2022-02-24 17:47:33 +08:00
parent 9df5dd5203
commit 353e8492b9
56 changed files with 2268 additions and 438 deletions

View File

@@ -238,6 +238,7 @@ ext {
mogo_core_function_v2x : "com.mogo.eagle.core.function.impl:v2x:${MOGO_CORE_FUNCTION_V2X_VERSION}", mogo_core_function_v2x : "com.mogo.eagle.core.function.impl:v2x:${MOGO_CORE_FUNCTION_V2X_VERSION}",
mogo_core_function_api : "com.mogo.eagle.core.function:api:${MOGO_CORE_FUNCTION_API_VERSION}", mogo_core_function_api : "com.mogo.eagle.core.function:api:${MOGO_CORE_FUNCTION_API_VERSION}",
mogo_core_function_call : "com.mogo.eagle.core.function:call:${MOGO_CORE_FUNCTION_CALL_VERSION}", mogo_core_function_call : "com.mogo.eagle.core.function:call:${MOGO_CORE_FUNCTION_CALL_VERSION}",
mogo_core_function_carcorder : "com.mogo.eagle.core.function:carcorder:${MOGO_CORE_FUNCTION_CARCORDER_VERSION}",
mogo_core_data : "com.mogo.eagle.core:data:${MOGO_CORE_DATA_VERSION}", mogo_core_data : "com.mogo.eagle.core:data:${MOGO_CORE_DATA_VERSION}",
mogo_core_res : "com.mogo.eagle.core:res:${MOGO_CORE_RES_VERSION}", mogo_core_res : "com.mogo.eagle.core:res:${MOGO_CORE_RES_VERSION}",
mogo_core_utils : "com.mogo.eagle.core:utils:${MOGO_CORE_UTILS_VERSION}", mogo_core_utils : "com.mogo.eagle.core:utils:${MOGO_CORE_UTILS_VERSION}",

View File

@@ -137,7 +137,7 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
data.putOpt("lon", stateInfo.getValues().getLon()); data.putOpt("lon", stateInfo.getValues().getLon());
data.putOpt("lat", stateInfo.getValues().getLat()); data.putOpt("lat", stateInfo.getValues().getLat());
data.putOpt("alt", stateInfo.getValues().getAlt()); data.putOpt("alt", stateInfo.getValues().getAlt());
data.putOpt("speed", stateInfo.getValues().getGnss_speed()); data.putOpt("speed", stateInfo.getValues().getVehicle_speed());
data.putOpt("heading", stateInfo.getValues().getHeading()); data.putOpt("heading", stateInfo.getValues().getHeading());
data.putOpt("acceleration", stateInfo.getValues().getAcceleration()); data.putOpt("acceleration", stateInfo.getValues().getAcceleration());
data.putOpt("yawRate", stateInfo.getValues().getYaw_rate()); data.putOpt("yawRate", stateInfo.getValues().getYaw_rate());

View File

@@ -52,6 +52,8 @@ dependencies {
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) { if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogoserviceapi implementation rootProject.ext.dependencies.mogoserviceapi
implementation rootProject.ext.dependencies.modulecommon implementation rootProject.ext.dependencies.modulecommon
implementation project(':libraries:map-usbcamera')
implementation rootProject.ext.dependencies.mogo_core_utils implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_function_api implementation rootProject.ext.dependencies.mogo_core_function_api
@@ -61,6 +63,8 @@ dependencies {
implementation project(':services:mogo-service-api') implementation project(':services:mogo-service-api')
implementation project(':modules:mogo-module-common') implementation project(':modules:mogo-module-common')
implementation project(':libraries:map-usbcamera')
implementation project(':core:mogo-core-utils') implementation project(':core:mogo-core-utils')
implementation project(':core:mogo-core-function-api') implementation project(':core:mogo-core-function-api')
implementation project(':core:mogo-core-function-call') implementation project(':core:mogo-core-function-call')

View File

@@ -1,3 +1,3 @@
GROUP=com.mogo.eagle.core.function.impl GROUP=com.mogo.eagle.core.function.impl
POM_ARTIFACT_ID=devatools POM_ARTIFACT_ID=carcorder
VERSION_CODE=1 VERSION_CODE=1

View File

@@ -1,4 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.eagle.core.function.carcorder"> package="com.mogo.eagle.core.function.carcorder">
<application>
<service
android:name=".service.CarcorderService"
android:enabled="true"
android:exported="true"
android:process=":uvcservice">
<intent-filter>
<action android:name="com.mogo.launcher.action.CARCORDER_SERVICE" />
</intent-filter>
</service>
</application>
</manifest> </manifest>

View File

@@ -0,0 +1,162 @@
package com.mogo.eagle.core.function.carcorder.service
import android.content.Intent
import android.hardware.usb.UsbDevice
import android.os.IBinder
import android.util.Log
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.usbcamera.UVCCameraHelper
import com.serenegiant.usb.IFrameCallback
import com.serenegiant.usb.USBMonitor
import com.serenegiant.usb.USBMonitor.OnDeviceConnectListener
import com.serenegiant.usb.USBMonitor.UsbControlBlock
import com.serenegiant.usb.UVCCamera
import com.serenegiant.usb.common.BaseService
import com.serenegiant.usb.encoder.MediaVideoBufferEncoder
/**
* 行车记录仪服务
* @author donghongyu
*/
class CarcorderService : BaseService() {
private val DEBUG = true
val TAG = CarcorderService::class.java.name
// 挂载的USB设备集合
private var mDeviceList: List<UsbDevice>? = null
// USB 设备连接工具
private var mUSBMonitor: USBMonitor? = null
// 用于接入UVC摄像机
private var mUVCCamera: UVCCamera? = null
// 相机控制
private var mCtrlBlock: UsbControlBlock? = null
/**
* 配置相机基本按书
*/
private val previewWidth = 640
private val previewHeight = 480
// Default using MJPEG
// if your device is connected,but have no images
// please try to change it to FRAME_FORMAT_YUYV
val FRAME_FORMAT_MJPEG: Int = UVCCamera.FRAME_FORMAT_MJPEG
val MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS
val MODE_CONTRAST = UVCCamera.PU_CONTRAST
private val mFrameFormat = UVCCameraHelper.FRAME_FORMAT_MJPEG
override fun onCreate() {
super.onCreate()
if (DEBUG) {
Logger.d(TAG, "onCreate……")
}
if (mUSBMonitor == null) {
mUSBMonitor = USBMonitor(applicationContext, mOnDeviceConnectListener)
mUSBMonitor!!.register()
mDeviceList = mUSBMonitor!!.deviceList
}
}
override fun onDestroy() {
super.onDestroy()
if (DEBUG) Log.d(TAG, "onDestroy:")
if (mUSBMonitor != null) {
mUSBMonitor!!.unregister()
mUSBMonitor = null
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onRebind(intent: Intent) {
if (DEBUG) Log.d(TAG, "onRebind:$intent")
}
override fun onUnbind(intent: Intent): Boolean {
if (DEBUG) Log.d(TAG, "onUnbind:$intent")
if (DEBUG) Log.d(TAG, "onUnbind:finished")
return true
}
//**********************************************************************************************************************************
private val mSync: Any = Any()
private val mVideoEncoder: MediaVideoBufferEncoder? = null
/**
* USB 设备连接监听
*/
private val mOnDeviceConnectListener: OnDeviceConnectListener = object : OnDeviceConnectListener {
override fun onAttach(device: UsbDevice) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onAttach:${device.deviceName}---mDeviceList:${mDeviceList?.size}")
mUSBMonitor!!.requestPermission(device)
}
override fun onConnect(device: UsbDevice, ctrlBlock: UsbControlBlock, createNew: Boolean) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onConnect:${device.deviceName}")
openCamera(device, ctrlBlock, createNew)
}
override fun onDisconnect(device: UsbDevice, ctrlBlock: UsbControlBlock) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onDisconnect:${device.deviceName}")
}
override fun onDettach(device: UsbDevice) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onDettach:${device.deviceName}")
}
override fun onCancel(device: UsbDevice) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onCancel:${device.deviceName}")
}
}
/**
* 连接相机
*/
private fun openCamera(device: UsbDevice, ctrlBlock: UsbControlBlock, createNew: Boolean) {
if (mUVCCamera == null) {
mUVCCamera = UVCCamera()
mUVCCamera!!.open(ctrlBlock)
mUVCCamera!!.setStatusCallback { statusClass, event, selector, statusAttribute, data ->
if (DEBUG) Log.d(TAG, "IStatusCallback#onStatus(statusClass=${statusClass},event=${event},selector=${selector},statusAttribute=${statusAttribute},data=${data})")
}
try {
mUVCCamera!!.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG)
} catch (e: Exception) {
e.printStackTrace()
try {
mUVCCamera!!.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE)
} catch (e: Exception) {
e.printStackTrace()
mUVCCamera!!.destroy()
}
}
mUVCCamera!!.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP)
mUVCCamera!!.startPreview()
}
}
/**
* 视频帧回掉
*/
private val mIFrameCallback = IFrameCallback { frame ->
if (DEBUG) Log.d(TAG, "IFrameCallback#onFrame:${frame}")
}
}

View File

@@ -160,6 +160,10 @@ class CarcorderPreviewView private constructor(
Log.d(TAG, "onDisConnectDev") Log.d(TAG, "onDisConnectDev")
showShortMsg("相机断开连接") showShortMsg("相机断开连接")
} }
override fun onCancelDev(device: UsbDevice?) {
Log.d(TAG, "onCancelDev" + device?.deviceName)
}
} }

View File

@@ -107,6 +107,7 @@ MOGO_CORE_FUNCTION_V2X_VERSION=0.0.58.10
MOGO_CORE_DATA_VERSION=0.0.58.10 MOGO_CORE_DATA_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_API_VERSION=0.0.58.10 MOGO_CORE_FUNCTION_API_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_CALL_VERSION=0.0.58.10 MOGO_CORE_FUNCTION_CALL_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_CARCORDER_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_DEVATOOLS_VERSION=0.0.58.10 MOGO_CORE_FUNCTION_DEVATOOLS_VERSION=0.0.58.10
MOGO_CORE_RES_VERSION=0.0.58.10 MOGO_CORE_RES_VERSION=0.0.58.10
MOGO_CORE_UTILS_VERSION=0.0.58.10 MOGO_CORE_UTILS_VERSION=0.0.58.10

View File

@@ -0,0 +1,39 @@
package com.mogo.usbcamera;
import com.serenegiant.usb.USBMonitor;
/**
* @author donghongyu
* USB摄像头方案获取权限、获取YUV数据
*/
public class USBCameraHelper {
private static USBCameraHelper mCameraHelper;
/**
* USB Manager
*/
private USBMonitor mUSBMonitor;
private USBCameraHelper() {
}
public static USBCameraHelper getInstance() {
if (mCameraHelper == null) {
mCameraHelper = new USBCameraHelper();
}
return mCameraHelper;
}
public void registerUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.register();
}
}
public void unregisterUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.unregister();
}
}
}

View File

@@ -74,6 +74,9 @@ public class UVCCameraHelper {
void onConnectDev(UsbDevice device, boolean isConnected); void onConnectDev(UsbDevice device, boolean isConnected);
void onDisConnectDev(UsbDevice device); void onDisConnectDev(UsbDevice device);
void onCancelDev(UsbDevice device);
} }
public void initUSBMonitor(Activity activity, CameraViewInterface cameraView, final OnMyDevConnectListener listener) { public void initUSBMonitor(Activity activity, CameraViewInterface cameraView, final OnMyDevConnectListener listener) {
@@ -106,18 +109,15 @@ public class UVCCameraHelper {
public void onConnect(final UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) { public void onConnect(final UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
mCtrlBlock = ctrlBlock; mCtrlBlock = ctrlBlock;
openCamera(ctrlBlock); openCamera(ctrlBlock);
new Thread(new Runnable() { new Thread(() -> {
@Override // wait for camera created
public void run() { try {
// wait for camera created Thread.sleep(500);
try { } catch (InterruptedException e) {
Thread.sleep(500); e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
// start previewing
startPreview(mCamView);
} }
// start previewing
startPreview(mCamView);
}).start(); }).start();
if (listener != null) { if (listener != null) {
listener.onConnectDev(device, true); listener.onConnectDev(device, true);
@@ -135,6 +135,9 @@ public class UVCCameraHelper {
@Override @Override
public void onCancel(UsbDevice device) { public void onCancel(UsbDevice device) {
if (listener != null) {
listener.onCancelDev(device);
}
} }
}); });
@@ -249,7 +252,6 @@ public class UVCCameraHelper {
public void capturePicture(String savePath, AbstractUVCCameraHandler.OnCaptureListener listener) { public void capturePicture(String savePath, AbstractUVCCameraHandler.OnCaptureListener listener) {
if (mCameraHandler != null && mCameraHandler.isOpened()) { if (mCameraHandler != null && mCameraHandler.isOpened()) {
File file = new File(savePath); File file = new File(savePath);
if (!Objects.requireNonNull(file.getParentFile()).exists()) { if (!Objects.requireNonNull(file.getParentFile()).exists()) {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();

View File

@@ -0,0 +1,23 @@
package com.serenegiant.dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
public abstract class DialogFragmentEx extends DialogFragment {
protected static final String ARGS_KEY_REQUEST_CODE = "requestCode";
protected static final String ARGS_KEY_ID_TITLE = "title";
protected static final String ARGS_KEY_ID_MESSAGE = "message";
protected static final String ARGS_KEY_TAG = "tag";
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
final Bundle args = getArguments();
if (args != null) {
outState.putAll(args);
}
}
}

View File

@@ -0,0 +1,136 @@
package com.serenegiant.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import com.serenegiant.utils.BuildCheck;
/**
* パーミッション要求前に説明用のダイアログを表示するためのDialogFragment
*/
@SuppressWarnings("deprecation")
@Deprecated
public class MessageDialogFragment extends DialogFragment {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
private static final String TAG = MessageDialogFragment.class.getSimpleName();
public static interface MessageDialogListener {
public void onMessageDialogResult(final MessageDialogFragment dialog, final int requestCode, final String[] permissions, final boolean result);
}
public static MessageDialogFragment showDialog(final Activity parent, final int requestCode, final int id_title, final int id_message, final String[] permissions) {
final MessageDialogFragment dialog = newInstance(requestCode, id_title, id_message, permissions);
dialog.show(parent.getFragmentManager(), TAG);
return dialog;
}
public static MessageDialogFragment showDialog(final Fragment parent, final int requestCode, final int id_title, final int id_message, final String[] permissions) {
final MessageDialogFragment dialog = newInstance(requestCode, id_title, id_message, permissions);
dialog.setTargetFragment(parent, parent.getId());
dialog.show(parent.getFragmentManager(), TAG);
return dialog;
}
public static MessageDialogFragment newInstance(final int requestCode, final int id_title, final int id_message, final String[] permissions) {
final MessageDialogFragment fragment = new MessageDialogFragment();
final Bundle args = new Bundle();
// ここでパラメータをセットする
args.putInt("requestCode", requestCode);
args.putInt("title", id_title);
args.putInt("message", id_message);
args.putStringArray("permissions", permissions != null ? permissions : new String[]{});
fragment.setArguments(args);
return fragment;
}
private MessageDialogListener mDialogListener;
public MessageDialogFragment() {
super();
// デフォルトコンストラクタが必要
}
@SuppressLint("NewApi")
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
// コールバックインターフェースを取得
if (activity instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)activity;
}
if (mDialogListener == null) {
final Fragment fragment = getTargetFragment();
if (fragment instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)fragment;
}
}
if (mDialogListener == null) {
if (BuildCheck.isAndroid4_2()) {
final Fragment target = getParentFragment();
if (target instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)target;
}
}
}
if (mDialogListener == null) {
// Log.w(TAG, "caller activity/fragment must implement PermissionDetailDialogFragmentListener");
throw new ClassCastException(activity.toString());
}
}
// @Override
// public void onCreate(final Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
// }
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
final int requestCode = getArguments().getInt("requestCode");
final int id_title = getArguments().getInt("title");
final int id_message = getArguments().getInt("message");
final String[] permissions = args.getStringArray("permissions");
return new AlertDialog.Builder(getActivity())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(id_title)
.setMessage(id_message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
// 本当はここでパーミッション要求をしたいだけどこのダイアログがdismissしてしまって結果を受け取れないので
// 呼び出し側へ返してそこでパーミッション要求する。なのでこのダイアログは単にメッセージを表示するだけ
try {
mDialogListener.onMessageDialogResult(MessageDialogFragment.this, requestCode, permissions, true);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
)
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, int whichButton) {
try {
mDialogListener.onMessageDialogResult(MessageDialogFragment.this, requestCode, permissions, false);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
)
.create();
}
}

View File

@@ -0,0 +1,213 @@
package com.serenegiant.dialog;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.serenegiant.utils.BuildCheck;
public class MessageDialogFragmentV4 extends DialogFragmentEx {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
private static final String TAG = MessageDialogFragmentV4.class.getSimpleName();
private static final String ARGS_KEY_PERMISSIONS = "permissions";
/**
* ダイアログの表示結果を受け取るためのコールバックリスナー
*/
public static interface MessageDialogListener {
public void onMessageDialogResult(
@NonNull final MessageDialogFragmentV4 dialog, final int requestCode,
@NonNull final String[] permissions, final boolean result);
}
/**
* ダイアログ表示のためのヘルパーメソッド
* @param parent
* @param requestCode
* @param id_title
* @param id_message
* @param permissions
* @return
* @throws IllegalStateException
*/
public static MessageDialogFragmentV4 showDialog(
@NonNull final FragmentActivity parent, final int requestCode,
@StringRes final int id_title, @StringRes final int id_message,
@NonNull final String[] permissions) throws IllegalStateException {
final MessageDialogFragmentV4 dialog
= newInstance(requestCode, id_title, id_message, permissions);
dialog.show(parent.getSupportFragmentManager(), TAG);
return dialog;
}
/**
* ダイアログ表示のためのヘルパーメソッド
* @param parent
* @param requestCode
* @param id_title
* @param id_message
* @param permissions
* @return
* @throws IllegalStateException
*/
public static MessageDialogFragmentV4 showDialog(
@NonNull final Fragment parent, final int requestCode,
@StringRes final int id_title, @StringRes final int id_message,
@NonNull final String[] permissions) throws IllegalStateException {
final MessageDialogFragmentV4 dialog
= newInstance(requestCode, id_title, id_message, permissions);
dialog.setTargetFragment(parent, parent.getId());
dialog.show(parent.requireFragmentManager(), TAG);
return dialog;
}
/**
* ダイアログ生成のためのヘルパーメソッド
* ダイアログ自体を直接生成せずにこのメソッドを呼び出すこと
* @param requestCode
* @param id_title
* @param id_message
* @param permissions
* @return
*/
public static MessageDialogFragmentV4 newInstance(
final int requestCode,
@StringRes final int id_title, @StringRes final int id_message,
@NonNull final String[] permissions) {
final MessageDialogFragmentV4 fragment = new MessageDialogFragmentV4();
final Bundle args = new Bundle();
// ここでパラメータをセットする
args.putInt(ARGS_KEY_REQUEST_CODE, requestCode);
args.putInt(ARGS_KEY_ID_TITLE, id_title);
args.putInt(ARGS_KEY_ID_MESSAGE, id_message);
args.putStringArray(ARGS_KEY_PERMISSIONS, permissions);
fragment.setArguments(args);
return fragment;
}
private MessageDialogListener mDialogListener;
/**
* コンストラクタ, 直接生成せずに#newInstanceを使うこと
*/
public MessageDialogFragmentV4() {
super();
// デフォルトコンストラクタが必要
}
@Override
public void onAttach(final Context context) {
super.onAttach(context);
// コールバックインターフェースを取得
if (context instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)context;
}
if (mDialogListener == null) {
final Fragment fragment = getTargetFragment();
if (fragment instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)fragment;
}
}
if (mDialogListener == null) {
if (BuildCheck.isAndroid4_2()) {
final Fragment target = getParentFragment();
if (target instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)target;
}
}
}
if (mDialogListener == null) {
// Log.w(TAG, "caller activity/fragment must implement PermissionDetailDialogFragmentListener");
throw new ClassCastException(context.toString());
}
}
// @Override
// public void onCreate(final Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
// }
@NonNull
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Bundle args = savedInstanceState != null ? savedInstanceState : requireArguments();
final int id_title = args.getInt(ARGS_KEY_ID_TITLE);
final int id_message = args.getInt(ARGS_KEY_ID_MESSAGE);
final Activity activity = requireActivity();
return new AlertDialog.Builder(activity)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(id_title)
.setMessage(id_message)
.setPositiveButton(android.R.string.ok, mOnClickListener)
.setNegativeButton(android.R.string.cancel, mOnClickListener)
.create();
}
private final DialogInterface.OnClickListener mOnClickListener
= new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
// 本当はここでパーミッション要求をしたいだけどこのダイアログがdismissしてしまって結果を受け取れないので
// 呼び出し側へ返してそこでパーミッション要求する。なのでこのダイアログは単にメッセージを表示するだけ
callOnMessageDialogResult(which == DialogInterface.BUTTON_POSITIVE);
}
};
@Override
public void onCancel(final DialogInterface dialog) {
super.onCancel(dialog);
callOnMessageDialogResult(false);
}
/**
* コールバックリスナー呼び出しのためのヘルパーメソッド
* @param result
*/
private void callOnMessageDialogResult(final boolean result)
throws IllegalStateException {
final Bundle args = requireArguments();
final int requestCode = args.getInt(ARGS_KEY_REQUEST_CODE);
final String[] permissions = args.getStringArray(ARGS_KEY_PERMISSIONS);
try {
mDialogListener.onMessageDialogResult(
MessageDialogFragmentV4.this,
requestCode, permissions, result);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb; package com.serenegiant.usb;
import android.app.Activity; import android.app.Activity;
@@ -24,199 +47,192 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class CameraDialog extends DialogFragment { public class CameraDialog extends DialogFragment {
private static final String TAG = CameraDialog.class.getSimpleName(); private static final String TAG = CameraDialog.class.getSimpleName();
public interface CameraDialogParent { public interface CameraDialogParent {
public USBMonitor getUSBMonitor(); public USBMonitor getUSBMonitor();
public void onDialogResult(boolean canceled);
}
/**
* Helper method
* @param parent FragmentActivity
* @return
*/
public static CameraDialog showDialog(final Activity parent/* add parameters here if you need */) {
CameraDialog dialog = newInstance(/* add parameters here if you need */);
try {
dialog.show(parent.getFragmentManager(), TAG);
} catch (final IllegalStateException e) {
dialog = null;
}
return dialog;
}
public void onDialogResult(boolean canceled); public static CameraDialog newInstance(/* add parameters here if you need */) {
} final CameraDialog dialog = new CameraDialog();
final Bundle args = new Bundle();
// add parameters here if you need
dialog.setArguments(args);
return dialog;
}
/** protected USBMonitor mUSBMonitor;
* Helper method private Spinner mSpinner;
* private DeviceListAdapter mDeviceListAdapter;
* @param parent FragmentActivity
* @return public CameraDialog(/* no arguments */) {
*/ // Fragment need default constructor
public static CameraDialog showDialog(final Activity parent/* add parameters here if you need */) { }
CameraDialog dialog = newInstance(/* add parameters here if you need */);
@SuppressWarnings("deprecation")
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
if (mUSBMonitor == null)
try { try {
dialog.show(parent.getFragmentManager(), TAG); mUSBMonitor = ((CameraDialogParent)activity).getUSBMonitor();
} catch (final IllegalStateException e) { } catch (final ClassCastException e) {
dialog = null; } catch (final NullPointerException e) {
} }
return dialog; if (mUSBMonitor == null) {
} throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
}
}
public static CameraDialog newInstance(/* add parameters here if you need */) { @Override
final CameraDialog dialog = new CameraDialog();
final Bundle args = new Bundle();
// add parameters here if you need
dialog.setArguments(args);
return dialog;
}
protected USBMonitor mUSBMonitor;
private Spinner mSpinner;
private DeviceListAdapter mDeviceListAdapter;
public CameraDialog(/* no arguments */) {
// Fragment need default constructor
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
if (mUSBMonitor == null) {
try {
mUSBMonitor = ((CameraDialogParent) activity).getUSBMonitor();
} catch (final NullPointerException | ClassCastException e) {
e.printStackTrace();
}
}
if (mUSBMonitor == null) {
throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
}
}
@Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (savedInstanceState == null) { if (savedInstanceState == null)
savedInstanceState = getArguments(); savedInstanceState = getArguments();
} }
}
@Override @Override
public void onSaveInstanceState(final Bundle saveInstanceState) { public void onSaveInstanceState(final Bundle saveInstanceState) {
final Bundle args = getArguments(); final Bundle args = getArguments();
if (args != null) { if (args != null)
saveInstanceState.putAll(args); saveInstanceState.putAll(args);
} super.onSaveInstanceState(saveInstanceState);
super.onSaveInstanceState(saveInstanceState); }
}
@Override @Override
public Dialog onCreateDialog(final Bundle savedInstanceState) { public Dialog onCreateDialog(final Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(initView()); builder.setView(initView());
builder.setTitle(R.string.select); builder.setTitle(R.string.select);
builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener); builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
builder.setNegativeButton(android.R.string.cancel, mOnDialogClickListener); builder.setNegativeButton(android.R.string.cancel , mOnDialogClickListener);
builder.setNeutralButton(R.string.refresh, null); builder.setNeutralButton(R.string.refresh, null);
final Dialog dialog = builder.create(); final Dialog dialog = builder.create();
dialog.setCancelable(true); dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true); dialog.setCanceledOnTouchOutside(true);
return dialog; return dialog;
} }
/** /**
* create view that this fragment shows * create view that this fragment shows
* * @return
* @return */
*/ private final View initView() {
private final View initView() { final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null); mSpinner = (Spinner)rootView.findViewById(R.id.spinner1);
mSpinner = (Spinner) rootView.findViewById(R.id.spinner1); final View empty = rootView.findViewById(android.R.id.empty);
final View empty = rootView.findViewById(android.R.id.empty); mSpinner.setEmptyView(empty);
mSpinner.setEmptyView(empty); return rootView;
return rootView; }
}
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
updateDevices(); updateDevices();
final Button button = (Button) getDialog().findViewById(android.R.id.button3); final Button button = (Button)getDialog().findViewById(android.R.id.button3);
if (button != null) { if (button != null) {
button.setOnClickListener(mOnClickListener); button.setOnClickListener(mOnClickListener);
} }
} }
private final OnClickListener mOnClickListener = new OnClickListener() { private final OnClickListener mOnClickListener = new OnClickListener() {
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
switch (v.getId()) { switch (v.getId()) {
case android.R.id.button3: case android.R.id.button3:
updateDevices(); updateDevices();
break; break;
} }
} }
}; };
private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() { private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(final DialogInterface dialog, final int which) { public void onClick(final DialogInterface dialog, final int which) {
switch (which) { switch (which) {
case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_POSITIVE:
final Object item = mSpinner.getSelectedItem(); final Object item = mSpinner.getSelectedItem();
if (item instanceof UsbDevice) { if (item instanceof UsbDevice) {
mUSBMonitor.requestPermission((UsbDevice) item); mUSBMonitor.requestPermission((UsbDevice)item);
((CameraDialogParent) getActivity()).onDialogResult(false); ((CameraDialogParent)getActivity()).onDialogResult(false);
} }
break; break;
case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEGATIVE:
((CameraDialogParent) getActivity()).onDialogResult(true); ((CameraDialogParent)getActivity()).onDialogResult(true);
break; break;
} }
} }
}; };
@Override @Override
public void onCancel(final DialogInterface dialog) { public void onCancel(final DialogInterface dialog) {
((CameraDialogParent) getActivity()).onDialogResult(true); ((CameraDialogParent)getActivity()).onDialogResult(true);
super.onCancel(dialog); super.onCancel(dialog);
} }
public void updateDevices() { public void updateDevices() {
// mUSBMonitor.dumpDevices(); // mUSBMonitor.dumpDevices();
final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter); final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0))); mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
mSpinner.setAdapter(mDeviceListAdapter); mSpinner.setAdapter(mDeviceListAdapter);
} }
private static final class DeviceListAdapter extends BaseAdapter { private static final class DeviceListAdapter extends BaseAdapter {
private final LayoutInflater mInflater; private final LayoutInflater mInflater;
private final List<UsbDevice> mList; private final List<UsbDevice> mList;
public DeviceListAdapter(final Context context, final List<UsbDevice> list) { public DeviceListAdapter(final Context context, final List<UsbDevice>list) {
mInflater = LayoutInflater.from(context); mInflater = LayoutInflater.from(context);
mList = list != null ? list : new ArrayList<UsbDevice>(); mList = list != null ? list : new ArrayList<UsbDevice>();
} }
@Override @Override
public int getCount() { public int getCount() {
return mList.size(); return mList.size();
} }
@Override @Override
public UsbDevice getItem(final int position) { public UsbDevice getItem(final int position) {
if ((position >= 0) && (position < mList.size())) { if ((position >= 0) && (position < mList.size()))
return mList.get(position); return mList.get(position);
} else { else
return null; return null;
} }
}
@Override @Override
public long getItemId(final int position) { public long getItemId(final int position) {
return position; return position;
} }
@Override @Override
public View getView(final int position, View convertView, final ViewGroup parent) { public View getView(final int position, View convertView, final ViewGroup parent) {
if (convertView == null) { if (convertView == null) {
convertView = mInflater.inflate(R.layout.listitem_device, parent, false); convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
} }
if (convertView instanceof CheckedTextView) { if (convertView instanceof CheckedTextView) {
final UsbDevice device = getItem(position); final UsbDevice device = getItem(position);
((CheckedTextView) convertView).setText( ((CheckedTextView)convertView).setText(
String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName())); String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
} }
return convertView; return convertView;
} }
} }
} }

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb; package com.serenegiant.usb;
import android.content.Context; import android.content.Context;

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb; package com.serenegiant.usb;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb; package com.serenegiant.usb;
import android.os.Parcel; import android.os.Parcel;

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb; package com.serenegiant.usb;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -97,20 +120,15 @@ public final class USBMonitor {
} }
public USBMonitor(final Context context, final OnDeviceConnectListener listener) { public USBMonitor(final Context context, final OnDeviceConnectListener listener) {
if (DEBUG) { if (DEBUG) Log.v(TAG, "USBMonitor:Constructor");
Log.v(TAG, "USBMonitor:Constructor"); if (listener == null)
}
if (listener == null) {
throw new IllegalArgumentException("OnDeviceConnectListener should not null."); throw new IllegalArgumentException("OnDeviceConnectListener should not null.");
}
mWeakContext = new WeakReference<Context>(context); mWeakContext = new WeakReference<Context>(context);
mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE); mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
mOnDeviceConnectListener = listener; mOnDeviceConnectListener = listener;
mAsyncHandler = HandlerThreadHandler.createHandler(TAG); mAsyncHandler = HandlerThreadHandler.createHandler(TAG);
destroyed = false; destroyed = false;
if (DEBUG) { if (DEBUG) Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
}
} }
/** /**
@@ -118,9 +136,7 @@ public final class USBMonitor {
* never reuse again * never reuse again
*/ */
public void destroy() { public void destroy() {
if (DEBUG) { if (DEBUG) Log.i(TAG, "destroy:");
Log.i(TAG, "destroy:");
}
unregister(); unregister();
if (!destroyed) { if (!destroyed) {
destroyed = true; destroyed = true;
@@ -153,13 +169,9 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public synchronized void register() throws IllegalStateException { public synchronized void register() throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
if (mPermissionIntent == null) { if (mPermissionIntent == null) {
if (DEBUG) { if (DEBUG) Log.i(TAG, "register:");
Log.i(TAG, "register:");
}
final Context context = mWeakContext.get(); final Context context = mWeakContext.get();
if (context != null) { if (context != null) {
mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0); mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
@@ -209,9 +221,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException { public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
mDeviceFilters.clear(); mDeviceFilters.clear();
mDeviceFilters.add(filter); mDeviceFilters.add(filter);
} }
@@ -222,9 +232,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException { public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
mDeviceFilters.add(filter); mDeviceFilters.add(filter);
} }
@@ -234,9 +242,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException { public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
mDeviceFilters.remove(filter); mDeviceFilters.remove(filter);
} }
@@ -246,9 +252,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public void setDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException { public void setDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
mDeviceFilters.clear(); mDeviceFilters.clear();
mDeviceFilters.addAll(filters); mDeviceFilters.addAll(filters);
} }
@@ -259,9 +263,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public void addDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException { public void addDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
mDeviceFilters.addAll(filters); mDeviceFilters.addAll(filters);
} }
@@ -270,9 +272,7 @@ public final class USBMonitor {
* @param filters * @param filters
*/ */
public void removeDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException { public void removeDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
mDeviceFilters.removeAll(filters); mDeviceFilters.removeAll(filters);
} }
@@ -282,9 +282,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public int getDeviceCount() throws IllegalStateException { public int getDeviceCount() throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
return getDeviceList().size(); return getDeviceList().size();
} }
@@ -294,9 +292,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public List<UsbDevice> getDeviceList() throws IllegalStateException { public List<UsbDevice> getDeviceList() throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
return getDeviceList(mDeviceFilters); return getDeviceList(mDeviceFilters);
} }
@@ -307,9 +303,7 @@ public final class USBMonitor {
* @throws IllegalStateException * @throws IllegalStateException
*/ */
public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) throws IllegalStateException { public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed) { if (destroyed) throw new IllegalStateException("already destroyed");
throw new IllegalStateException("already destroyed");
}
// get detected devices // 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 // store those devices info before matching filter xml file
@@ -353,8 +347,8 @@ public final class USBMonitor {
break; break;
} else { } else {
// collection failed dev's class and subclass // collection failed dev's class and subclass
String devModel = android.os.Build.MODEL; String devModel = Build.MODEL;
String devSystemVersion = android.os.Build.VERSION.RELEASE; String devSystemVersion = Build.VERSION.RELEASE;
String devClass = String.valueOf(device.getDeviceClass()); String devClass = String.valueOf(device.getDeviceClass());
String subClass = String.valueOf(device.getDeviceSubclass()); String subClass = String.valueOf(device.getDeviceSubclass());
try{ try{

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb; package com.serenegiant.usb;
import android.util.SparseArray; import android.util.SparseArray;

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb; package com.serenegiant.usb;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;

View File

@@ -2,9 +2,7 @@ package com.serenegiant.usb.common;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat; import android.graphics.ImageFormat;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
@@ -22,7 +20,6 @@ import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import com.serenegiant.usb.IFrameCallback; import com.serenegiant.usb.IFrameCallback;
import com.serenegiant.usb.ParentPreviewConstraintLayout;
import com.serenegiant.usb.Size; import com.serenegiant.usb.Size;
import com.serenegiant.usb.USBMonitor; import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera; import com.serenegiant.usb.UVCCamera;
@@ -52,7 +49,6 @@ import java.nio.ByteBuffer;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
@@ -68,19 +64,19 @@ public abstract class AbstractUVCCameraHandler extends Handler {
// 对外回调接口 // 对外回调接口
public interface CameraCallback { public interface CameraCallback {
void onOpen(); public void onOpen();
void onClose(); public void onClose();
void onStartPreview(); public void onStartPreview();
void onStopPreview(); public void onStopPreview();
void onStartRecording(); public void onStartRecording();
void onStopRecording(); public void onStopRecording();
void onError(final Exception e); public void onError(final Exception e);
} }
public static OnEncodeResultListener mListener; public static OnEncodeResultListener mListener;
@@ -180,16 +176,12 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
public void close() { public void close() {
if (DEBUG) { if (DEBUG) Log.v(TAG, "close:");
Log.v(TAG, "close:");
}
if (isOpened()) { if (isOpened()) {
stopPreview(); stopPreview();
sendEmptyMessage(MSG_CLOSE); sendEmptyMessage(MSG_CLOSE);
} }
if (DEBUG) { if (DEBUG) Log.v(TAG, "close:finished");
Log.v(TAG, "close:finished");
}
} }
// 切换分辨率 // 切换分辨率
@@ -214,18 +206,14 @@ public abstract class AbstractUVCCameraHandler extends Handler {
// 关闭Camera预览 // 关闭Camera预览
public void stopPreview() { public void stopPreview() {
if (DEBUG) { if (DEBUG) Log.v(TAG, "stopPreview:");
Log.v(TAG, "stopPreview:");
}
removeMessages(MSG_PREVIEW_START); removeMessages(MSG_PREVIEW_START);
if (isRecording()) { if (isRecording()) {
stopRecording(); stopRecording();
} }
if (isPreviewing()) { if (isPreviewing()) {
final CameraThread thread = mWeakThread.get(); final CameraThread thread = mWeakThread.get();
if (thread == null) { if (thread == null) return;
return;
}
synchronized (thread.mSync) { synchronized (thread.mSync) {
sendEmptyMessage(MSG_PREVIEW_STOP); sendEmptyMessage(MSG_PREVIEW_STOP);
if (!isCameraThread()) { if (!isCameraThread()) {
@@ -239,12 +227,10 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
} }
} }
if (DEBUG) { if (DEBUG) Log.v(TAG, "stopPreview:finished");
Log.v(TAG, "stopPreview:finished");
}
} }
public void captureStill(final String path, AbstractUVCCameraHandler.OnCaptureListener listener) { public void captureStill(final String path, OnCaptureListener listener) {
AbstractUVCCameraHandler.mCaptureListener = listener; AbstractUVCCameraHandler.mCaptureListener = listener;
checkReleased(); checkReleased();
sendMessage(obtainMessage(MSG_CAPTURE_STILL, path)); sendMessage(obtainMessage(MSG_CAPTURE_STILL, path));
@@ -366,9 +352,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
@Override @Override
public void handleMessage(final Message msg) { public void handleMessage(final Message msg) {
final CameraThread thread = mWeakThread.get(); final CameraThread thread = mWeakThread.get();
if (thread == null) { if (thread == null) return;
return;
}
switch (msg.what) { switch (msg.what) {
case MSG_OPEN: case MSG_OPEN:
thread.handleOpen((USBMonitor.UsbControlBlock) msg.obj); thread.handleOpen((USBMonitor.UsbControlBlock) msg.obj);
@@ -472,17 +456,13 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
public AbstractUVCCameraHandler getHandler() { public AbstractUVCCameraHandler getHandler() {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "getHandler:");
Log.v(TAG_THREAD, "getHandler:");
}
synchronized (mSync) { synchronized (mSync) {
if (mHandler == null) { if (mHandler == null)
try { try {
mSync.wait(); mSync.wait();
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
e.printStackTrace();
} }
}
} }
return mHandler; return mHandler;
} }
@@ -528,9 +508,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
public void handleOpen(final USBMonitor.UsbControlBlock ctrlBlock) { public void handleOpen(final USBMonitor.UsbControlBlock ctrlBlock) {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleOpen:");
Log.v(TAG_THREAD, "handleOpen:");
}
handleClose(); handleClose();
try { try {
final UVCCamera camera = new UVCCamera(); final UVCCamera camera = new UVCCamera();
@@ -542,15 +520,12 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} catch (final Exception e) { } catch (final Exception e) {
callOnError(e); callOnError(e);
} }
if (DEBUG) { if (DEBUG)
Log.i(TAG, "supportedSize:" + (mUVCCamera != null ? mUVCCamera.getSupportedSize() : null)); Log.i(TAG, "supportedSize:" + (mUVCCamera != null ? mUVCCamera.getSupportedSize() : null));
}
} }
public void handleClose() { public void handleClose() {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleClose:");
Log.v(TAG_THREAD, "handleClose:");
}
handleStopPusher(); handleStopPusher();
final UVCCamera camera; final UVCCamera camera;
synchronized (mSync) { synchronized (mSync) {
@@ -565,12 +540,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
public void handleStartPreview(final Object surface) { public void handleStartPreview(final Object surface) {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
Log.v(TAG_THREAD, "handleStartPreview:"); if ((mUVCCamera == null) || mIsPreviewing) return;
}
if ((mUVCCamera == null) || mIsPreviewing) {
return;
}
try { try {
mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor); mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor);
// 获取USB Camera预览数据使用NV21颜色会失真 // 获取USB Camera预览数据使用NV21颜色会失真
@@ -603,9 +574,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
public void handleStopPreview() { public void handleStopPreview() {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:");
Log.v(TAG_THREAD, "handleStopPreview:");
}
if (mIsPreviewing) { if (mIsPreviewing) {
if (mUVCCamera != null) { if (mUVCCamera != null) {
mUVCCamera.stopPreview(); mUVCCamera.stopPreview();
@@ -617,20 +586,14 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
callOnStopPreview(); callOnStopPreview();
} }
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:finished");
Log.v(TAG_THREAD, "handleStopPreview:finished");
}
} }
// 捕获静态图片 // 捕获静态图片
public void handleCaptureStill(final String path) { public void handleCaptureStill(final String path) {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleCaptureStill:");
Log.v(TAG_THREAD, "handleCaptureStill:");
}
final Activity parent = mWeakParent.get(); final Activity parent = mWeakParent.get();
if (parent == null) { if (parent == null) return;
return;
}
// mSoundPool.play(mSoundId, 0.2f, 0.2f, 0, 0, 1.0f); // play shutter sound // mSoundPool.play(mSoundId, 0.2f, 0.2f, 0, 0, 1.0f); // play shutter sound
try { try {
final Bitmap bitmap = mWeakCameraView.get().captureStillImage(mWidth, mHeight); final Bitmap bitmap = mWeakCameraView.get().captureStillImage(mWidth, mHeight);
@@ -647,7 +610,6 @@ public abstract class AbstractUVCCameraHandler extends Handler {
os.flush(); os.flush();
mHandler.sendMessage(mHandler.obtainMessage(MSG_MEDIA_UPDATE, outputFile.getPath())); mHandler.sendMessage(mHandler.obtainMessage(MSG_MEDIA_UPDATE, outputFile.getPath()));
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace();
} }
} finally { } finally {
os.close(); os.close();
@@ -706,9 +668,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
private H264EncodeConsumer mH264Consumer; private H264EncodeConsumer mH264Consumer;
public void handleStartPusher(RecordParams params) { public void handleStartPusher(RecordParams params) {
if ((mUVCCamera == null) || (mH264Consumer != null)) { if ((mUVCCamera == null) || (mH264Consumer != null))
return; return;
}
// // 获取USB Camera预览数据 // // 获取USB Camera预览数据
// mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); // mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21);
@@ -747,9 +708,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
// 停止音视频编码线程 // 停止音视频编码线程
stopAudioRecord(); stopAudioRecord();
stopVideoRecord(); stopVideoRecord();
if(isSupportOverlay) { if(isSupportOverlay)
TxtOverlay.getInstance().release(); TxtOverlay.getInstance().release();
}
// // 停止捕获视频数据 // // 停止捕获视频数据
// if (mUVCCamera != null) { // if (mUVCCamera != null) {
// mUVCCamera.stopCapture(); // mUVCCamera.stopCapture();
@@ -915,23 +875,18 @@ public abstract class AbstractUVCCameraHandler extends Handler {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void handleUpdateMedia(final String path) { public void handleUpdateMedia(final String path) {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleUpdateMedia:path=" + path);
Log.v(TAG_THREAD, "handleUpdateMedia:path=" + path);
}
final Activity parent = mWeakParent.get(); final Activity parent = mWeakParent.get();
final boolean released = (mHandler == null) || mHandler.mReleased; final boolean released = (mHandler == null) || mHandler.mReleased;
if (parent != null && parent.getApplicationContext() != null) { if (parent != null && parent.getApplicationContext() != null) {
try { try {
if (DEBUG) { if (DEBUG) Log.i(TAG, "MediaScannerConnection#scanFile");
Log.i(TAG, "MediaScannerConnection#scanFile");
}
MediaScannerConnection.scanFile(parent.getApplicationContext(), new String[]{path}, null, null); MediaScannerConnection.scanFile(parent.getApplicationContext(), new String[]{path}, null, null);
} catch (final Exception e) { } catch (final Exception e) {
Log.e(TAG, "handleUpdateMedia:", e); Log.e(TAG, "handleUpdateMedia:", e);
} }
if (released || parent.isDestroyed()) { if (released || parent.isDestroyed())
handleRelease(); handleRelease();
}
} else { } else {
Log.w(TAG, "MainActivity already destroyed"); Log.w(TAG, "MainActivity already destroyed");
// give up to add this movie to MediaStore now. // give up to add this movie to MediaStore now.
@@ -941,71 +896,57 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} }
public void handleRelease() { public void handleRelease() {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleRelease:mIsRecording=" + mIsRecording);
Log.v(TAG_THREAD, "handleRelease:mIsRecording=" + mIsRecording);
}
handleClose(); handleClose();
mCallbacks.clear(); mCallbacks.clear();
if (!mIsRecording) { if (!mIsRecording) {
mHandler.mReleased = true; mHandler.mReleased = true;
Looper.myLooper().quit(); Looper.myLooper().quit();
} }
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleRelease:finished");
Log.v(TAG_THREAD, "handleRelease:finished");
}
} }
// 自动对焦 // 自动对焦
public void handleCameraFoucs() { public void handleCameraFoucs() {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
Log.v(TAG_THREAD, "handleStartPreview:"); if ((mUVCCamera == null) || !mIsPreviewing)
}
if ((mUVCCamera == null) || !mIsPreviewing) {
return; return;
}
mUVCCamera.setAutoFocus(true); mUVCCamera.setAutoFocus(true);
} }
// 获取支持的分辨率 // 获取支持的分辨率
public List<Size> getSupportedSizes() { public List<Size> getSupportedSizes() {
if ((mUVCCamera == null) || !mIsPreviewing) { if ((mUVCCamera == null) || !mIsPreviewing)
return null; return null;
}
return mUVCCamera.getSupportedSizeList(); return mUVCCamera.getSupportedSizeList();
} }
private final MediaEncoder.MediaEncoderListener mMediaEncoderListener = new MediaEncoder.MediaEncoderListener() { private final MediaEncoder.MediaEncoderListener mMediaEncoderListener = new MediaEncoder.MediaEncoderListener() {
@Override @Override
public void onPrepared(final MediaEncoder encoder) { public void onPrepared(final MediaEncoder encoder) {
if (DEBUG) { if (DEBUG) Log.v(TAG, "onPrepared:encoder=" + encoder);
Log.v(TAG, "onPrepared:encoder=" + encoder);
}
mIsRecording = true; mIsRecording = true;
if (encoder instanceof MediaVideoEncoder) { if (encoder instanceof MediaVideoEncoder)
try { try {
mWeakCameraView.get().setVideoEncoder((MediaVideoEncoder) encoder); mWeakCameraView.get().setVideoEncoder((MediaVideoEncoder) encoder);
} catch (final Exception e) { } catch (final Exception e) {
Log.e(TAG, "onPrepared:", e); Log.e(TAG, "onPrepared:", e);
} }
} if (encoder instanceof MediaSurfaceEncoder)
if (encoder instanceof MediaSurfaceEncoder) {
try { try {
mWeakCameraView.get().setVideoEncoder((MediaSurfaceEncoder) encoder); mWeakCameraView.get().setVideoEncoder((MediaSurfaceEncoder) encoder);
mUVCCamera.startCapture(((MediaSurfaceEncoder) encoder).getInputSurface()); mUVCCamera.startCapture(((MediaSurfaceEncoder) encoder).getInputSurface());
} catch (final Exception e) { } catch (final Exception e) {
Log.e(TAG, "onPrepared:", e); Log.e(TAG, "onPrepared:", e);
} }
}
} }
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override @Override
public void onStopped(final MediaEncoder encoder) { public void onStopped(final MediaEncoder encoder) {
if (DEBUG) { if (DEBUG) Log.v(TAG_THREAD, "onStopped:encoder=" + encoder);
Log.v(TAG_THREAD, "onStopped:encoder=" + encoder);
}
if ((encoder instanceof MediaVideoEncoder) if ((encoder instanceof MediaVideoEncoder)
|| (encoder instanceof MediaSurfaceEncoder)) { || (encoder instanceof MediaSurfaceEncoder))
try { try {
mIsRecording = false; mIsRecording = false;
final Activity parent = mWeakParent.get(); final Activity parent = mWeakParent.get();
@@ -1027,7 +968,6 @@ public abstract class AbstractUVCCameraHandler extends Handler {
} catch (final Exception e) { } catch (final Exception e) {
Log.e(TAG, "onPrepared:", e); Log.e(TAG, "onPrepared:", e);
} }
}
} }
@Override @Override

View File

@@ -0,0 +1,321 @@
package com.serenegiant.usb.common;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import com.mogo.usbcamera.R;
import com.serenegiant.dialog.MessageDialogFragmentV4;
import com.serenegiant.utils.BuildCheck;
import com.serenegiant.utils.HandlerThreadHandler;
import com.serenegiant.utils.PermissionCheck;
/**
* Created by saki on 2016/11/18.
*
*/
public class BaseActivity extends AppCompatActivity
implements MessageDialogFragmentV4.MessageDialogListener {
private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
private static final String TAG = BaseActivity.class.getSimpleName();
/** UI操作のためのHandler */
private final Handler mUIHandler = new Handler(Looper.getMainLooper());
private final Thread mUiThread = mUIHandler.getLooper().getThread();
/** ワーカースレッド上で処理するためのHandler */
private Handler mWorkerHandler;
private long mWorkerThreadID = -1;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ワーカースレッドを生成
if (mWorkerHandler == null) {
mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
}
}
@Override
protected void onPause() {
clearToast();
super.onPause();
}
@Override
protected synchronized void onDestroy() {
// ワーカースレッドを破棄
if (mWorkerHandler != null) {
try {
mWorkerHandler.getLooper().quit();
} catch (final Exception e) {
//
}
mWorkerHandler = null;
}
super.onDestroy();
}
//================================================================================
/**
* UIスレッドでRunnableを実行するためのヘルパーメソッド
* @param task
* @param duration
*/
public final void runOnUiThread(final Runnable task, final long duration) {
if (task == null) {
return;
}
mUIHandler.removeCallbacks(task);
if ((duration > 0) || Thread.currentThread() != mUiThread) {
mUIHandler.postDelayed(task, duration);
} else {
try {
task.run();
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
/**
* UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
* @param task
*/
public final void removeFromUiThread(final Runnable task) {
if (task == null) {
return;
}
mUIHandler.removeCallbacks(task);
}
/**
* ワーカースレッド上で指定したRunnableを実行する
* 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
* @param task
* @param delayMillis
*/
protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
if ((task == null) || (mWorkerHandler == null)) {
return;
}
try {
mWorkerHandler.removeCallbacks(task);
if (delayMillis > 0) {
mWorkerHandler.postDelayed(task, delayMillis);
} else if (mWorkerThreadID == Thread.currentThread().getId()) {
task.run();
} else {
mWorkerHandler.post(task);
}
} catch (final Exception e) {
// ignore
}
}
/**
* 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
* @param task
*/
protected final synchronized void removeEvent(final Runnable task) {
if (task == null) {
return;
}
try {
mWorkerHandler.removeCallbacks(task);
} catch (final Exception e) {
// ignore
}
}
//================================================================================
private Toast mToast;
/**
* Toastでメッセージを表示
* @param msg
*/
protected void showToast(@StringRes final int msg, final Object... args) {
removeFromUiThread(mShowToastTask);
mShowToastTask = new ShowToastTask(msg, args);
runOnUiThread(mShowToastTask, 0);
}
/**
* Toastが表示されていればキャンセルする
*/
protected void clearToast() {
removeFromUiThread(mShowToastTask);
mShowToastTask = null;
try {
if (mToast != null) {
mToast.cancel();
mToast = null;
}
} catch (final Exception e) {
// ignore
}
}
private ShowToastTask mShowToastTask;
private final class ShowToastTask implements Runnable {
final int msg;
final Object args;
private ShowToastTask(@StringRes final int msg, final Object... args) {
this.msg = msg;
this.args = args;
}
@Override
public void run() {
try {
if (mToast != null) {
mToast.cancel();
mToast = null;
}
final String _msg = (args != null) ? getString(msg, args) : getString(msg);
mToast = Toast.makeText(BaseActivity.this, _msg, Toast.LENGTH_SHORT);
mToast.show();
} catch (final Exception e) {
// ignore
}
}
}
//================================================================================
/**
* MessageDialogFragmentメッセージダイアログからのコールバックリスナー
* @param dialog
* @param requestCode
* @param permissions
* @param result
*/
@SuppressLint("NewApi")
@Override
public void onMessageDialogResult(final MessageDialogFragmentV4 dialog, final int requestCode, final String[] permissions, final boolean result) {
if (result) {
// メッセージダイアログでOKを押された時はパーミッション要求する
if (BuildCheck.isMarshmallow()) {
requestPermissions(permissions, requestCode);
return;
}
}
// メッセージダイアログでキャンセルされた時とAndroid6でない時は自前でチェックして#checkPermissionResultを呼び出す
for (final String permission: permissions) {
checkPermissionResult(requestCode, permission, PermissionCheck.hasPermission(this, permission));
}
}
/**
* パーミッション要求結果を受け取るためのメソッド
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); // 何もしてないけど一応呼んどく
final int n = Math.min(permissions.length, grantResults.length);
for (int i = 0; i < n; i++) {
checkPermissionResult(requestCode, permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
}
}
/**
* パーミッション要求の結果をチェック
* ここではパーミッションを取得できなかった時にToastでメッセージ表示するだけ
* @param requestCode
* @param permission
* @param result
*/
protected void checkPermissionResult(final int requestCode, final String permission, final boolean result) {
// パーミッションがないときにはメッセージを表示する
if (!result && (permission != null)) {
if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
showToast(R.string.permission_audio);
}
if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
showToast(R.string.permission_ext_storage);
}
if (Manifest.permission.INTERNET.equals(permission)) {
showToast(R.string.permission_network);
}
}
}
// 動的パーミッション要求時の要求コード
protected static final int REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE = 0x12345;
protected static final int REQUEST_PERMISSION_AUDIO_RECORDING = 0x234567;
protected static final int REQUEST_PERMISSION_NETWORK = 0x345678;
protected static final int REQUEST_PERMISSION_CAMERA = 0x537642;
/**
* 外部ストレージへの書き込みパーミッションが有るかどうかをチェック
* なければ説明ダイアログを表示する
* @return true 外部ストレージへの書き込みパーミッションが有る
*/
protected boolean checkPermissionWriteExternalStorage() {
if (!PermissionCheck.hasWriteExternalStorage(this)) {
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE,
R.string.permission_title, R.string.permission_ext_storage_request,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE});
return false;
}
return true;
}
/**
* 録音のパーミッションが有るかどうかをチェック
* なければ説明ダイアログを表示する
* @return true 録音のパーミッションが有る
*/
protected boolean checkPermissionAudio() {
if (!PermissionCheck.hasAudio(this)) {
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_AUDIO_RECORDING,
R.string.permission_title, R.string.permission_audio_recording_request,
new String[]{Manifest.permission.RECORD_AUDIO});
return false;
}
return true;
}
/**
* ネットワークアクセスのパーミッションが有るかどうかをチェック
* なければ説明ダイアログを表示する
* @return true ネットワークアクセスのパーミッションが有る
*/
protected boolean checkPermissionNetwork() {
if (!PermissionCheck.hasNetwork(this)) {
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_NETWORK,
R.string.permission_title, R.string.permission_network_request,
new String[]{Manifest.permission.INTERNET});
return false;
}
return true;
}
/**
* カメラアクセスのパーミッションがあるかどうかをチェック
* なければ説明ダイアログを表示する
* @return true カメラアクセスのパーミッションが有る
*/
protected boolean checkPermissionCamera() {
if (!PermissionCheck.hasCamera(this)) {
MessageDialogFragmentV4.showDialog(this, REQUEST_PERMISSION_CAMERA,
R.string.permission_title, R.string.permission_camera_request,
new String[]{Manifest.permission.CAMERA});
return false;
}
return true;
}
}

View File

@@ -0,0 +1,348 @@
package com.serenegiant.usb.common;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import com.mogo.usbcamera.R;
import com.serenegiant.dialog.MessageDialogFragment;
import com.serenegiant.utils.BuildCheck;
import com.serenegiant.utils.HandlerThreadHandler;
import com.serenegiant.utils.PermissionCheck;
/**
* Created by saki on 2016/11/19.
*/
public class BaseFragment extends Fragment
implements MessageDialogFragment.MessageDialogListener {
private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
private static final String TAG = BaseFragment.class.getSimpleName();
/**
* UI操作のためのHandler
*/
private final Handler mUIHandler = new Handler(Looper.getMainLooper());
private final Thread mUiThread = mUIHandler.getLooper().getThread();
/**
* ワーカースレッド上で処理するためのHandler
*/
private Handler mWorkerHandler;
private long mWorkerThreadID = -1;
public BaseFragment() {
super();
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ワーカースレッドを生成
if (mWorkerHandler == null) {
mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
}
}
@Override
public void onPause() {
clearToast();
super.onPause();
}
@Override
public synchronized void onDestroy() {
// ワーカースレッドを破棄
if (mWorkerHandler != null) {
try {
mWorkerHandler.getLooper().quit();
} catch (final Exception e) {
//
}
mWorkerHandler = null;
}
super.onDestroy();
}
//================================================================================
/**
* UIスレッドでRunnableを実行するためのヘルパーメソッド
*
* @param task
* @param duration
*/
public final void runOnUiThread(final Runnable task, final long duration) {
if (task == null) {
return;
}
mUIHandler.removeCallbacks(task);
if ((duration > 0) || Thread.currentThread() != mUiThread) {
mUIHandler.postDelayed(task, duration);
} else {
try {
task.run();
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
/**
* UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
*
* @param task
*/
public final void removeFromUiThread(final Runnable task) {
if (task == null) {
return;
}
mUIHandler.removeCallbacks(task);
}
/**
* ワーカースレッド上で指定したRunnableを実行する
* 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
*
* @param task
* @param delayMillis
*/
protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
if ((task == null) || (mWorkerHandler == null)) {
return;
}
try {
mWorkerHandler.removeCallbacks(task);
if (delayMillis > 0) {
mWorkerHandler.postDelayed(task, delayMillis);
} else if (mWorkerThreadID == Thread.currentThread().getId()) {
task.run();
} else {
mWorkerHandler.post(task);
}
} catch (final Exception e) {
// ignore
}
}
/**
* 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
*
* @param task
*/
protected final synchronized void removeEvent(final Runnable task) {
if (task == null) {
return;
}
try {
mWorkerHandler.removeCallbacks(task);
} catch (final Exception e) {
// ignore
}
}
//================================================================================
private Toast mToast;
/**
* Toastでメッセージを表示
*
* @param msg
*/
protected void showToast(@StringRes final int msg, final Object... args) {
removeFromUiThread(mShowToastTask);
mShowToastTask = new ShowToastTask(msg, args);
runOnUiThread(mShowToastTask, 0);
}
/**
* Toastが表示されていればキャンセルする
*/
protected void clearToast() {
removeFromUiThread(mShowToastTask);
mShowToastTask = null;
try {
if (mToast != null) {
mToast.cancel();
mToast = null;
}
} catch (final Exception e) {
// ignore
}
}
private ShowToastTask mShowToastTask;
private final class ShowToastTask implements Runnable {
final int msg;
final Object args;
private ShowToastTask(@StringRes final int msg, final Object... args) {
this.msg = msg;
this.args = args;
}
@Override
public void run() {
try {
if (mToast != null) {
mToast.cancel();
mToast = null;
}
if (args != null) {
final String _msg = getString(msg, args);
mToast = Toast.makeText(getActivity(), _msg, Toast.LENGTH_SHORT);
} else {
mToast = Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT);
}
mToast.show();
} catch (final Exception e) {
// ignore
}
}
}
//================================================================================
/**
* MessageDialogFragmentメッセージダイアログからのコールバックリスナー
*
* @param dialog
* @param requestCode
* @param permissions
* @param result
*/
@SuppressLint("NewApi")
@Override
public void onMessageDialogResult(final MessageDialogFragment dialog, final int requestCode, final String[] permissions, final boolean result) {
if (result) {
// メッセージダイアログでOKを押された時はパーミッション要求する
if (BuildCheck.isMarshmallow()) {
requestPermissions(permissions, requestCode);
return;
}
}
// メッセージダイアログでキャンセルされた時とAndroid6でない時は自前でチェックして#checkPermissionResultを呼び出す
for (final String permission : permissions) {
checkPermissionResult(requestCode, permission, PermissionCheck.hasPermission(getActivity(), permission));
}
}
/**
* パーミッション要求結果を受け取るためのメソッド
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); // 何もしてないけど一応呼んどく
final int n = Math.min(permissions.length, grantResults.length);
for (int i = 0; i < n; i++) {
checkPermissionResult(requestCode, permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
}
}
/**
* パーミッション要求の結果をチェック
* ここではパーミッションを取得できなかった時にToastでメッセージ表示するだけ
*
* @param requestCode
* @param permission
* @param result
*/
protected void checkPermissionResult(final int requestCode, final String permission, final boolean result) {
// パーミッションがないときにはメッセージを表示する
if (!result && (permission != null)) {
if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
showToast(R.string.permission_audio);
}
if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
showToast(R.string.permission_ext_storage);
}
if (Manifest.permission.INTERNET.equals(permission)) {
showToast(R.string.permission_network);
}
}
}
// 動的パーミッション要求時の要求コード
protected static final int REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE = 0x12345;
protected static final int REQUEST_PERMISSION_AUDIO_RECORDING = 0x234567;
protected static final int REQUEST_PERMISSION_NETWORK = 0x345678;
protected static final int REQUEST_PERMISSION_CAMERA = 0x537642;
/**
* 外部ストレージへの書き込みパーミッションが有るかどうかをチェック
* なければ説明ダイアログを表示する
*
* @return true 外部ストレージへの書き込みパーミッションが有る
*/
protected boolean checkPermissionWriteExternalStorage() {
if (!PermissionCheck.hasWriteExternalStorage(getActivity())) {
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE,
R.string.permission_title, R.string.permission_ext_storage_request,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE});
return false;
}
return true;
}
/**
* 録音のパーミッションが有るかどうかをチェック
* なければ説明ダイアログを表示する
*
* @return true 録音のパーミッションが有る
*/
protected boolean checkPermissionAudio() {
if (!PermissionCheck.hasAudio(getActivity())) {
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_AUDIO_RECORDING,
R.string.permission_title, R.string.permission_audio_recording_request,
new String[]{Manifest.permission.RECORD_AUDIO});
return false;
}
return true;
}
/**
* ネットワークアクセスのパーミッションが有るかどうかをチェック
* なければ説明ダイアログを表示する
*
* @return true ネットワークアクセスのパーミッションが有る
*/
protected boolean checkPermissionNetwork() {
if (!PermissionCheck.hasNetwork(getActivity())) {
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_NETWORK,
R.string.permission_title, R.string.permission_network_request,
new String[]{Manifest.permission.INTERNET});
return false;
}
return true;
}
/**
* カメラアクセスのパーミッションがあるかどうかをチェック
* なければ説明ダイアログを表示する
*
* @return true カメラアクセスのパーミッションが有る
*/
protected boolean checkPermissionCamera() {
if (!PermissionCheck.hasCamera(getActivity())) {
MessageDialogFragment.showDialog(this, REQUEST_PERMISSION_CAMERA,
R.string.permission_title, R.string.permission_camera_request,
new String[]{Manifest.permission.CAMERA});
return false;
}
return true;
}
}

View File

@@ -0,0 +1,108 @@
package com.serenegiant.usb.common;
import android.app.Service;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.serenegiant.utils.HandlerThreadHandler;
public abstract class BaseService extends Service {
private static boolean DEBUG = false; // FIXME 実働時はfalseにセットすること
private static final String TAG = BaseService.class.getSimpleName();
/** UI操作のためのHandler */
private final Handler mUIHandler = new Handler(Looper.getMainLooper());
private final Thread mUiThread = mUIHandler.getLooper().getThread();
/** ワーカースレッド上で処理するためのHandler */
private Handler mWorkerHandler;
private long mWorkerThreadID = -1;
@Override
public void onCreate() {
super.onCreate();
// ワーカースレッドを生成
if (mWorkerHandler == null) {
mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
}
}
@Override
public synchronized void onDestroy() {
// ワーカースレッドを破棄
if (mWorkerHandler != null) {
try {
mWorkerHandler.getLooper().quit();
} catch (final Exception e) {
//
}
mWorkerHandler = null;
}
super.onDestroy();
}
//================================================================================
/**
* UIスレッドでRunnableを実行するためのヘルパーメソッド
* @param task
* @param duration
*/
public final void runOnUiThread(final Runnable task, final long duration) {
if (task == null) return;
mUIHandler.removeCallbacks(task);
if ((duration > 0) || Thread.currentThread() != mUiThread) {
mUIHandler.postDelayed(task, duration);
} else {
try {
task.run();
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
/**
* UIスレッド上で指定したRunnableが実行待ちしていれば実行待ちを解除する
* @param task
*/
public final void removeFromUiThread(final Runnable task) {
if (task == null) return;
mUIHandler.removeCallbacks(task);
}
/**
* ワーカースレッド上で指定したRunnableを実行する
* 未実行の同じRunnableがあればキャンセルされる(後から指定した方のみ実行される)
* @param task
* @param delayMillis
*/
protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
if ((task == null) || (mWorkerHandler == null)) return;
try {
mWorkerHandler.removeCallbacks(task);
if (delayMillis > 0) {
mWorkerHandler.postDelayed(task, delayMillis);
} else if (mWorkerThreadID == Thread.currentThread().getId()) {
task.run();
} else {
mWorkerHandler.post(task);
}
} catch (final Exception e) {
// ignore
}
}
/**
* 指定したRunnableをワーカースレッド上で実行予定であればキャンセルする
* @param task
*/
protected final synchronized void removeEvent(final Runnable task) {
if (task == null) return;
try {
mWorkerHandler.removeCallbacks(task);
} catch (final Exception e) {
// ignore
}
}
}

View File

@@ -1,9 +1,30 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.common; package com.serenegiant.usb.common;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import com.serenegiant.usb.ParentPreviewConstraintLayout;
import com.serenegiant.usb.UVCCamera; import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.widget.CameraViewInterface; import com.serenegiant.usb.widget.CameraViewInterface;

View File

@@ -1,10 +1,32 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.common; package com.serenegiant.usb.common;
import android.app.Activity; import android.app.Activity;
import android.view.Surface; import android.view.Surface;
import com.serenegiant.glutils.RendererHolder; import com.serenegiant.glutils.RendererHolder;
import com.serenegiant.usb.ParentPreviewConstraintLayout;
import com.serenegiant.usb.UVCCamera; import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.widget.CameraViewInterface; import com.serenegiant.usb.widget.CameraViewInterface;
@@ -101,7 +123,6 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
mRendererHolder = new RendererHolder(thread.getWidth(), thread.getHeight(), null); mRendererHolder = new RendererHolder(thread.getWidth(), thread.getHeight(), null);
} }
@Override
public synchronized void release() { public synchronized void release() {
if (mRendererHolder != null) { if (mRendererHolder != null) {
mRendererHolder.release(); mRendererHolder.release();
@@ -110,8 +131,7 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
super.release(); super.release();
} }
@Override public synchronized void resize(final int width, final int height) {
public synchronized void resize(final int width, final int height) {
super.resize(width, height); super.resize(width, height);
if (mRendererHolder != null) { if (mRendererHolder != null) {
mRendererHolder.resize(width, height); mRendererHolder.resize(width, height);

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder; package com.serenegiant.usb.encoder;
public interface IAudioEncoder { public interface IAudioEncoder {

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder; package com.serenegiant.usb.encoder;
public interface IVideoEncoder { public interface IVideoEncoder {

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder; package com.serenegiant.usb.encoder;
import android.media.AudioFormat; import android.media.AudioFormat;

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder; package com.serenegiant.usb.encoder;
import android.media.MediaCodec; import android.media.MediaCodec;

View File

@@ -1,3 +1,26 @@
/*
* UVCCamera
* library and sample to access to UVC web camera on non-rooted Android device
*
* Copyright (c) 2014-2017 saki t_saki@serenegiant.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* All files in the folder are under this Apache License, Version 2.0.
* Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
* may have a different license, see the respective files.
*/
package com.serenegiant.usb.encoder; package com.serenegiant.usb.encoder;
import android.media.MediaCodec; import android.media.MediaCodec;

View File

@@ -1,8 +1,10 @@
package com.serenegiant.usb.encoder; package com.serenegiant.usb.encoder;
/** /** 录制参数
* 录制参数 *
* Created by jiangdongguo on 2017/10/19.
*/ */
public class RecordParams { public class RecordParams {
private String recordPath; private String recordPath;
private int recordDuration; private int recordDuration;

View File

@@ -19,10 +19,12 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
/** /**将PCM编码为AAC
* 将PCM编码为AAC *
* Created by jianddongguo on 2017/7/21.
*/ */
public class AACEncodeConsumer extends Thread {
public class AACEncodeConsumer extends Thread{
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final String TAG = "TMPU"; private static final String TAG = "TMPU";
private static final String MIME_TYPE = "audio/mp4a-latm"; private static final String MIME_TYPE = "audio/mp4a-latm";
@@ -38,7 +40,7 @@ public class AACEncodeConsumer extends Thread {
private AudioRecord mAudioRecord; // 音频采集 private AudioRecord mAudioRecord; // 音频采集
private MediaCodec mAudioEncoder; // 音频编码 private MediaCodec mAudioEncoder; // 音频编码
private OnAACEncodeResultListener listener; private OnAACEncodeResultListener listener;
private int mSamplingRateIndex = 0;//ADTS private int mSamplingRateIndex = 0;//ADTS
private boolean isEncoderStart = false; private boolean isEncoderStart = false;
private boolean isRecMp3 = false; private boolean isRecMp3 = false;
private boolean isExit = false; private boolean isExit = false;
@@ -46,7 +48,7 @@ public class AACEncodeConsumer extends Thread {
private WeakReference<Mp4MediaMuxer> mMuxerRef; private WeakReference<Mp4MediaMuxer> mMuxerRef;
private MediaFormat newFormat; private MediaFormat newFormat;
private static final int[] AUDIO_SOURCES = new int[]{ private static final int[] AUDIO_SOURCES = new int[] {
MediaRecorder.AudioSource.DEFAULT, MediaRecorder.AudioSource.DEFAULT,
MediaRecorder.AudioSource.MIC, MediaRecorder.AudioSource.MIC,
MediaRecorder.AudioSource.CAMCORDER, MediaRecorder.AudioSource.CAMCORDER,
@@ -54,7 +56,7 @@ public class AACEncodeConsumer extends Thread {
/** /**
* There are 13 supported frequencies by ADTS. * There are 13 supported frequencies by ADTS.
**/ **/
public static final int[] AUDIO_SAMPLING_RATES = {96000, // 0 public static final int[] AUDIO_SAMPLING_RATES = { 96000, // 0
88200, // 1 88200, // 1
64000, // 2 64000, // 2
48000, // 3 48000, // 3
@@ -75,13 +77,13 @@ public class AACEncodeConsumer extends Thread {
private FileOutputStream fops; private FileOutputStream fops;
// 编码流结果回调接口 // 编码流结果回调接口
public interface OnAACEncodeResultListener { public interface OnAACEncodeResultListener{
void onEncodeResult(byte[] data, int offset, void onEncodeResult(byte[] data, int offset,
int length, long timestamp); int length, long timestamp);
} }
public AACEncodeConsumer() { public AACEncodeConsumer(){
for (int i = 0; i < AUDIO_SAMPLING_RATES.length; i++) { for (int i=0;i < AUDIO_SAMPLING_RATES.length; i++) {
if (AUDIO_SAMPLING_RATES[i] == SAMPLE_RATE) { if (AUDIO_SAMPLING_RATES[i] == SAMPLE_RATE) {
mSamplingRateIndex = i; mSamplingRateIndex = i;
break; break;
@@ -89,16 +91,16 @@ public class AACEncodeConsumer extends Thread {
} }
} }
public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener) { public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener){
this.listener = listener; this.listener = listener;
} }
public void exit() { public void exit(){
isExit = true; isExit = true;
} }
public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer) { public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer){
this.mMuxerRef = new WeakReference<>(mMuxer); this.mMuxerRef = new WeakReference<>(mMuxer);
Mp4MediaMuxer muxer = mMuxerRef.get(); Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null && newFormat != null) { if (muxer != null && newFormat != null) {
muxer.addTrack(newFormat, false); muxer.addTrack(newFormat, false);
@@ -108,7 +110,7 @@ public class AACEncodeConsumer extends Thread {
@Override @Override
public void run() { public void run() {
// 开启音频采集、编码 // 开启音频采集、编码
if (!isEncoderStart) { if(! isEncoderStart){
initAudioRecord(); initAudioRecord();
initMediaCodec(); initMediaCodec();
} }
@@ -116,16 +118,16 @@ public class AACEncodeConsumer extends Thread {
byte[] mp3Buffer = new byte[1024]; byte[] mp3Buffer = new byte[1024];
// 这里有问题,当本地录制结束后,没有写入 // 这里有问题,当本地录制结束后,没有写入
while (!isExit) { while(! isExit){
byte[] audioBuffer = new byte[2048]; byte[] audioBuffer = new byte[2048];
// 采集音频 // 采集音频
int readBytes = mAudioRecord.read(audioBuffer, 0, BUFFER_SIZE); int readBytes = mAudioRecord.read(audioBuffer,0,BUFFER_SIZE);
if (DEBUG) if(DEBUG)
Log.i(TAG, "采集音频readBytes = " + readBytes); Log.i(TAG,"采集音频readBytes = "+readBytes);
// 编码音频 // 编码音频
if (readBytes > 0) { if(readBytes > 0){
encodeBytes(audioBuffer, readBytes); encodeBytes(audioBuffer,readBytes);
} }
} }
// 停止音频采集、编码 // 停止音频采集、编码
@@ -140,81 +142,81 @@ public class AACEncodeConsumer extends Thread {
ByteBuffer[] outputBuffers = mAudioEncoder.getOutputBuffers(); ByteBuffer[] outputBuffers = mAudioEncoder.getOutputBuffers();
//返回编码器的一个输入缓存区句柄,-1表示当前没有可用的输入缓存区 //返回编码器的一个输入缓存区句柄,-1表示当前没有可用的输入缓存区
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(TIMES_OUT); int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(TIMES_OUT);
if (inputBufferIndex >= 0) { if(inputBufferIndex >= 0){
// 绑定一个被空的、可写的输入缓存区inputBuffer到客户端 // 绑定一个被空的、可写的输入缓存区inputBuffer到客户端
ByteBuffer inputBuffer = null; ByteBuffer inputBuffer = null;
if (!isLollipop()) { if(!isLollipop()){
inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer = inputBuffers[inputBufferIndex];
} else { }else{
inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex); inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex);
} }
// 向输入缓存区写入有效原始数据,并提交到编码器中进行编码处理 // 向输入缓存区写入有效原始数据,并提交到编码器中进行编码处理
if (audioBuf == null || readBytes <= 0) { if(audioBuf==null || readBytes<=0){
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, 0, getPTSUs(), MediaCodec.BUFFER_FLAG_END_OF_STREAM); mAudioEncoder.queueInputBuffer(inputBufferIndex,0,0,getPTSUs(),MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else { }else{
inputBuffer.clear(); inputBuffer.clear();
inputBuffer.put(audioBuf); inputBuffer.put(audioBuf);
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, readBytes, getPTSUs(), 0); mAudioEncoder.queueInputBuffer(inputBufferIndex,0,readBytes,getPTSUs(),0);
} }
} }
// 返回一个输出缓存区句柄,当为-1时表示当前没有可用的输出缓存区 // 返回一个输出缓存区句柄,当为-1时表示当前没有可用的输出缓存区
// mBufferInfo参数包含被编码好的数据timesOut参数为超时等待的时间 // mBufferInfo参数包含被编码好的数据timesOut参数为超时等待的时间
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo(); MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = -1; int outputBufferIndex = -1;
do { do{
outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo, TIMES_OUT); outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo,TIMES_OUT);
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { if(outputBufferIndex == MediaCodec. INFO_TRY_AGAIN_LATER){
if (DEBUG) if(DEBUG)
Log.i(TAG, "获得编码器输出缓存区超时"); Log.i(TAG,"获得编码器输出缓存区超时");
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
// 如果API小于21APP需要重新绑定编码器的输入缓存区 // 如果API小于21APP需要重新绑定编码器的输入缓存区
// 如果API大于21则无需处理INFO_OUTPUT_BUFFERS_CHANGED // 如果API大于21则无需处理INFO_OUTPUT_BUFFERS_CHANGED
if (!isLollipop()) { if(!isLollipop()){
outputBuffers = mAudioEncoder.getOutputBuffers(); outputBuffers = mAudioEncoder.getOutputBuffers();
} }
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
// 编码器输出缓存区格式改变,通常在存储数据之前且只会改变一次 // 编码器输出缓存区格式改变,通常在存储数据之前且只会改变一次
// 这里设置混合器视频轨道,如果音频已经添加则启动混合器(保证音视频同步) // 这里设置混合器视频轨道,如果音频已经添加则启动混合器(保证音视频同步)
if (DEBUG) if(DEBUG)
Log.i(TAG, "编码器输出缓存区格式改变,添加视频轨道到混合器"); Log.i(TAG,"编码器输出缓存区格式改变,添加视频轨道到混合器");
synchronized (AACEncodeConsumer.this) { synchronized (AACEncodeConsumer.this) {
newFormat = mAudioEncoder.getOutputFormat(); newFormat = mAudioEncoder.getOutputFormat();
if (mMuxerRef != null) { if(mMuxerRef != null){
Mp4MediaMuxer muxer = mMuxerRef.get(); Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null) { if (muxer != null) {
muxer.addTrack(newFormat, false); muxer.addTrack(newFormat, false);
} }
} }
} }
} else { }else{
// 当flag属性置为BUFFER_FLAG_CODEC_CONFIG后说明输出缓存区的数据已经被消费了 // 当flag属性置为BUFFER_FLAG_CODEC_CONFIG后说明输出缓存区的数据已经被消费了
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0){
if (DEBUG) if(DEBUG)
Log.i(TAG, "编码数据被消费BufferInfo的size属性置0"); Log.i(TAG,"编码数据被消费BufferInfo的size属性置0");
mBufferInfo.size = 0; mBufferInfo.size = 0;
} }
// 数据流结束标志,结束本次循环 // 数据流结束标志,结束本次循环
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
if (DEBUG) if(DEBUG)
Log.i(TAG, "数据流结束,退出循环"); Log.i(TAG,"数据流结束,退出循环");
break; break;
} }
// 获取一个只读的输出缓存区inputBuffer ,它包含被编码好的数据 // 获取一个只读的输出缓存区inputBuffer ,它包含被编码好的数据
ByteBuffer mBuffer = ByteBuffer.allocate(10240); ByteBuffer mBuffer = ByteBuffer.allocate(10240);
ByteBuffer outputBuffer = null; ByteBuffer outputBuffer = null;
if (!isLollipop()) { if(!isLollipop()){
outputBuffer = outputBuffers[outputBufferIndex]; outputBuffer = outputBuffers[outputBufferIndex];
} else { }else{
outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex); outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
} }
if (mBufferInfo.size != 0) { if(mBufferInfo.size != 0){
// 获取输出缓存区失败,抛出异常 // 获取输出缓存区失败,抛出异常
if (outputBuffer == null) { if(outputBuffer == null){
throw new RuntimeException("encodecOutputBuffer" + outputBufferIndex + "was null"); throw new RuntimeException("encodecOutputBuffer"+outputBufferIndex+"was null");
} }
// 添加视频流到混合器 // 添加视频流到混合器
if (mMuxerRef != null) { if(mMuxerRef != null){
Mp4MediaMuxer muxer = mMuxerRef.get(); Mp4MediaMuxer muxer = mMuxerRef.get();
if (muxer != null) { if (muxer != null) {
muxer.pumpStream(outputBuffer, mBufferInfo, false); muxer.pumpStream(outputBuffer, mBufferInfo, false);
@@ -228,25 +230,25 @@ public class AACEncodeConsumer extends Thread {
addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7); addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7);
mBuffer.flip(); mBuffer.flip();
// 将AAC回调给MainModelImpl进行push // 将AAC回调给MainModelImpl进行push
if (listener != null) { if(listener != null){
Log.i(TAG, "----->得到aac数据流<-----"); Log.i(TAG,"----->得到aac数据流<-----");
listener.onEncodeResult(mBuffer.array(), 0, mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000); listener.onEncodeResult(mBuffer.array(),0, mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000);
} }
} }
// 处理结束,释放输出缓存区资源 // 处理结束,释放输出缓存区资源
mAudioEncoder.releaseOutputBuffer(outputBufferIndex, false); mAudioEncoder.releaseOutputBuffer(outputBufferIndex,false);
} }
} while (outputBufferIndex >= 0); }while (outputBufferIndex >= 0);
} }
private void initAudioRecord() { private void initAudioRecord(){
if (DEBUG) if(DEBUG)
Log.d(TAG, "AACEncodeConsumer-->开始采集音频"); Log.d(TAG,"AACEncodeConsumer-->开始采集音频");
// 设置进程优先级 // 设置进程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO); Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT); AudioFormat.ENCODING_PCM_16BIT);
for (final int src : AUDIO_SOURCES) { for (final int src: AUDIO_SOURCES) {
try { try {
mAudioRecord = new AudioRecord(src, mAudioRecord = new AudioRecord(src,
SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
@@ -266,18 +268,18 @@ public class AACEncodeConsumer extends Thread {
mAudioRecord.startRecording(); mAudioRecord.startRecording();
} }
private void initMediaCodec() { private void initMediaCodec(){
if (DEBUG) if(DEBUG)
Log.d(TAG, "AACEncodeConsumer-->开始编码音频"); Log.d(TAG,"AACEncodeConsumer-->开始编码音频");
MediaCodecInfo mCodecInfo = selectSupportCodec(MIME_TYPE); MediaCodecInfo mCodecInfo = selectSupportCodec(MIME_TYPE);
if (mCodecInfo == null) { if(mCodecInfo == null){
Log.e(TAG, "编码器不支持" + MIME_TYPE + "类型"); Log.e(TAG,"编码器不支持"+MIME_TYPE+"类型");
return; return;
} }
try { try{
mAudioEncoder = MediaCodec.createByCodecName(mCodecInfo.getName()); mAudioEncoder = MediaCodec.createByCodecName(mCodecInfo.getName());
} catch (IOException e) { }catch(IOException e){
Log.e(TAG, "创建编码器失败" + e.getMessage()); Log.e(TAG,"创建编码器失败"+e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
MediaFormat format = new MediaFormat(); MediaFormat format = new MediaFormat();
@@ -293,9 +295,9 @@ public class AACEncodeConsumer extends Thread {
} }
private void stopAudioRecord() { private void stopAudioRecord() {
if (DEBUG) if(DEBUG)
Log.d(TAG, "AACEncodeConsumer-->停止采集音频"); Log.d(TAG,"AACEncodeConsumer-->停止采集音频");
if (mAudioRecord != null) { if(mAudioRecord != null){
mAudioRecord.stop(); mAudioRecord.stop();
mAudioRecord.release(); mAudioRecord.release();
mAudioRecord = null; mAudioRecord = null;
@@ -303,9 +305,9 @@ public class AACEncodeConsumer extends Thread {
} }
private void stopMediaCodec() { private void stopMediaCodec() {
if (DEBUG) if(DEBUG)
Log.d(TAG, "AACEncodeConsumer-->停止编码音频"); Log.d(TAG,"AACEncodeConsumer-->停止编码音频");
if (mAudioEncoder != null) { if(mAudioEncoder != null){
mAudioEncoder.stop(); mAudioEncoder.stop();
mAudioEncoder.release(); mAudioEncoder.release();
mAudioEncoder = null; mAudioEncoder = null;
@@ -314,28 +316,28 @@ public class AACEncodeConsumer extends Thread {
} }
// API>=21 // API>=21
private boolean isLollipop() { private boolean isLollipop(){
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
} }
// API<=19 // API<=19
private boolean isKITKAT() { private boolean isKITKAT(){
return Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT; return Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
} }
private long getPTSUs() { private long getPTSUs(){
long result = System.nanoTime() / 1000; long result = System.nanoTime()/1000;
if (result < prevPresentationTimes) { if(result < prevPresentationTimes){
result = (prevPresentationTimes - result) + result; result = (prevPresentationTimes - result ) + result;
} }
return result; return result;
} }
/** /**
* 遍历所有编解码器返回第一个与指定MIME类型匹配的编码器 * 遍历所有编解码器返回第一个与指定MIME类型匹配的编码器
* 判断是否有支持指定mime类型的编码器 * 判断是否有支持指定mime类型的编码器
*/ * */
private MediaCodecInfo selectSupportCodec(String mimeType) { private MediaCodecInfo selectSupportCodec(String mimeType){
int numCodecs = MediaCodecList.getCodecCount(); int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) { for (int i = 0; i < numCodecs; i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
@@ -365,7 +367,7 @@ public class AACEncodeConsumer extends Thread {
} }
private short[] transferByte2Short(byte[] data, int readBytes) { private short[] transferByte2Short(byte[] data,int readBytes){
// byte[] 转 short[],数组长度缩减一半 // byte[] 转 short[],数组长度缩减一半
int shortLen = readBytes / 2; int shortLen = readBytes / 2;
// 将byte[]数组装如ByteBuffer缓冲区 // 将byte[]数组装如ByteBuffer缓冲区

View File

@@ -17,7 +17,9 @@ import java.nio.ByteBuffer;
/** /**
* 对YUV视频流进行编码 * 对YUV视频流进行编码
* Created by jiangdongguo on 2017/5/6.
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class H264EncodeConsumer extends Thread { public class H264EncodeConsumer extends Thread {
private static final boolean DEBUG = false; private static final boolean DEBUG = false;

View File

@@ -10,9 +10,11 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**Mp4封装混合器
* Mp4封装混合器 *
* Created by jianddongguo on 2017/7/28.
*/ */
public class Mp4MediaMuxer { public class Mp4MediaMuxer {
private static final boolean VERBOSE = false; private static final boolean VERBOSE = false;
private static final String TAG = Mp4MediaMuxer.class.getSimpleName(); private static final String TAG = Mp4MediaMuxer.class.getSimpleName();

View File

@@ -0,0 +1,186 @@
package com.serenegiant.utils;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.net.Uri;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class PermissionCheck {
public static final void dumpPermissions(@Nullable final Context context) {
if (context == null) return;
try {
final PackageManager pm = context.getPackageManager();
final List<PermissionGroupInfo> list = pm.getAllPermissionGroups(PackageManager.GET_META_DATA);
for (final PermissionGroupInfo info : list) {
Log.d("PermissionCheck", info.name);
}
} catch (final Exception e) {
Log.w("", e);
}
}
/**
* パーミッションを確認
* @param context
* @param permissionName
* @return 指定したパーミッションがあればtrue
*/
@SuppressLint("NewApi")
public static boolean hasPermission(@Nullable final Context context, final String permissionName) {
if (context == null) return false;
boolean result = false;
try {
final int check;
if (BuildCheck.isMarshmallow()) {
check = context.checkSelfPermission(permissionName);
} else {
final PackageManager pm = context.getPackageManager();
check = pm.checkPermission(permissionName, context.getPackageName());
}
switch (check) {
case PackageManager.PERMISSION_DENIED:
break;
case PackageManager.PERMISSION_GRANTED:
result = true;
break;
}
} catch (final Exception e) {
Log.w("", e);
}
return result;
}
/**
* 録音のミッションがあるかどうかを確認
* @param context
* @return 録音のパーミッションがあればtrue
*/
public static boolean hasAudio(@Nullable final Context context) {
return hasPermission(context, permission.RECORD_AUDIO);
}
/**
* ネットワークへのアクセスパーミッションがあるかどうかを確認
* @param context
* @return ネットワークへのアクセスパーミッションがあればtrue
*/
public static boolean hasNetwork(@Nullable final Context context) {
return hasPermission(context, permission.INTERNET);
}
/**
* 外部ストレージへの書き込みパーミッションがあるかどうかを確認
* @param context
* @return 外部ストレージへの書き込みパーミッションがあればtrue
*/
public static boolean hasWriteExternalStorage(@Nullable final Context context) {
return hasPermission(context, permission.WRITE_EXTERNAL_STORAGE);
}
/**
* 外部ストレージからの読み込みパーミッションがあるかどうかを確認
* @param context
* @return 外部ストレージへの読み込みパーミッションがあればtrue
*/
@SuppressLint("InlinedApi")
public static boolean hasReadExternalStorage(@Nullable final Context context) {
if (BuildCheck.isAndroid4())
return hasPermission(context, permission.READ_EXTERNAL_STORAGE);
else
return hasPermission(context, permission.WRITE_EXTERNAL_STORAGE);
}
/**
* 位置情報アクセスのパーミッションが有るかどうかを確認
* @param context
* @return
*/
public static boolean hasAccessLocation(@Nullable final Context context) {
return hasPermission(context, permission.ACCESS_COARSE_LOCATION)
&& hasPermission(context, permission.ACCESS_FINE_LOCATION);
}
/**
* 低精度位置情報アクセスのパーミッションが有るかどうかを確認
* @param context
* @return
*/
public static boolean hasAccessCoarseLocation(@Nullable final Context context) {
return hasPermission(context, permission.ACCESS_COARSE_LOCATION);
}
/**
* 高精度位置情報アクセスのパーミッションが有るかどうかを確認
* @param context
* @return
*/
public static boolean hasAccessFineLocation(@Nullable final Context context) {
return hasPermission(context, permission.ACCESS_FINE_LOCATION);
}
/**
* カメラへアクセス可能かどうか
* @param context
* @return
*/
public static boolean hasCamera(@Nullable final Context context) {
return hasPermission(context, permission.CAMERA);
}
/**
* アプリの詳細設定へ遷移させる(パーミッションを取得できなかった時など)
* @param context
*/
public static void openSettings(@NonNull final Context context) {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
final Uri uri = Uri.fromParts("package", context.getPackageName(), null);
intent.setData(uri);
context.startActivity(intent);
}
/**
* AndroidManifest.xmlに設定されているはずのパーミッションをチェックする
* @param context
* @param expectations
* @return 空リストなら全てのパーミッションが入っていた,
* @throws IllegalArgumentException
* @throws PackageManager.NameNotFoundException
*/
public static List<String> missingPermissions(@NonNull final Context context, @NonNull final String[] expectations) throws IllegalArgumentException, PackageManager.NameNotFoundException {
return missingPermissions(context, new ArrayList<String>(Arrays.asList(expectations)));
}
/**
* AndroidManifest.xmlに設定されているはずのパーミッションをチェックする
* @param context
* @param expectations
* @return 空リストなら全てのパーミッションが入っていた,
* @throws IllegalArgumentException
* @throws PackageManager.NameNotFoundException
*/
public static List<String> missingPermissions(@NonNull final Context context, @NonNull final List<String> expectations) throws IllegalArgumentException, PackageManager.NameNotFoundException {
final PackageManager pm = context.getPackageManager();
final PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
final String[] info = pi.requestedPermissions;
if (info != null) {
for (String i : info) {
expectations.remove(i);
}
}
return expectations;
}
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="permission_title">Regarding permission</string>
<!-- 録音のパーミッション RECORD_AUDIO -->
<string name="permission_audio">No audio recording permission. \nAll audio function is disabled</string>
<string name="permission_audio_recording_reason">Audio recording permission is necessary for movie capture with audio.</string>
<string name="permission_audio_recording_request">Audio recording permission is necessary for movie capture with audio.</string>
<string name="permission_audio_streaming_reason">Audio recording permission is necessary for streaming with audio.</string>
<string name="permission_audio_streaming_request">Audio recording permission is necessary for streaming with audio.</string>
<!-- 外部ストレージへの書き込みパーミッション WRITE_EXTERNAL_STORAGE -->
<string name="permission_ext_storage">No permission of writing external storage. \nMovie/still image capturing are disabled.</string>
<string name="permission_ext_storage_reason">Permission of writing external storage is necessary for movie/still image capturing.</string>
<string name="permission_ext_storage_request">Permission of writing external storage is necessary for movie/still image capturing.</string>
<string name="permission_ext_storage_finish">No writing external storage permission. \nApp will finish soon</string>
<!-- ネットワークアクセスのパーミッション INTERNET -->
<string name="permission_network">No network access permission.</string>
<string name="permission_network_reason">Network access permission is necessary for streaming.</string>
<string name="permission_network_request">Network access permission is necessary for streaming.</string>
<string name="permission_network_finish">No network access permission. \nApp will finish soon</string>
<!-- 位置情報のパーミッション -->
<string name="permission_location">No location access permission</string>
<string name="permission_location_reason">Location access permission is necessary for %s</string>
<string name="permission_location_request">Location access permission is necessary for %s</string>
<string name="permission_location_finish">No location access permission. \nApp will finish soon</string>
<!-- カメラアクセスのパーミッション -->
<string name="permission_camera">No camera access permission</string>
<string name="permission_camera_finish">No camera access permission. \nApp will finish soon</string>
<string name="permission_camera_reason">Camera access permission is necessary for fallback to built in camera.</string>
<string name="permission_camera_request">Camera access permission is necessary for fallback to built in camera.</string>
<!-- ハードウエアID・通話状態へのアクセスパーミッション -->
<string name="permission_hardware_id_request">Permission to access phone state/hardware id necessary for calling.</string>
</resources>

View File

@@ -62,6 +62,7 @@ dependencies {
implementation rootProject.ext.dependencies.mogo_core_utils implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_data implementation rootProject.ext.dependencies.mogo_core_data
implementation rootProject.ext.dependencies.mogo_core_function_call implementation rootProject.ext.dependencies.mogo_core_function_call
implementation rootProject.ext.dependencies.mogo_core_function_carcorder
} else { } else {
api project(":foudations:mogo-commons") api project(":foudations:mogo-commons")
api project(':modules:mogo-module-common') api project(':modules:mogo-module-common')
@@ -72,6 +73,8 @@ dependencies {
implementation project(':core:mogo-core-utils') implementation project(':core:mogo-core-utils')
implementation project(':core:mogo-core-data') implementation project(':core:mogo-core-data')
implementation project(':core:mogo-core-function-call') implementation project(':core:mogo-core-function-call')
implementation project(':core:function-impl:mogo-core-function-carcorder')
} }
} }

View File

@@ -1,5 +1,7 @@
package com.mogo.module.main; package com.mogo.module.main;
import static com.mogo.module.main.MainPresenter.MOGO_PERMISSION_REQUEST_CODE;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
@@ -22,6 +24,7 @@ import com.mogo.commons.mvp.MvpActivity;
import com.mogo.commons.mvp.MvpFragment; import com.mogo.commons.mvp.MvpFragment;
import com.mogo.eagle.core.data.constants.MoGoFragmentPaths; import com.mogo.eagle.core.data.constants.MoGoFragmentPaths;
import com.mogo.eagle.core.data.map.MogoLocation; import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.carcorder.service.CarcorderService;
import com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils; import com.mogo.eagle.core.utilcode.mogo.AppLaunchTimeUtils;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger; import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.mogo.permissions.PermissionsDialogUtils; import com.mogo.eagle.core.utilcode.mogo.permissions.PermissionsDialogUtils;
@@ -50,8 +53,6 @@ import com.zhidao.autopilot.support.api.AutopilotServiceManage;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static com.mogo.module.main.MainPresenter.MOGO_PERMISSION_REQUEST_CODE;
/** /**
* @author congtaowang * @author congtaowang
* @since 2019-12-23 * @since 2019-12-23
@@ -255,8 +256,12 @@ public class MainActivity extends MvpActivity<MainView, MainPresenter> implement
} }
private void startBaseService() { private void startBaseService() {
Intent intent = new Intent(this, MogoMainService.class); Intent intentMainServicee = new Intent(this, MogoMainService.class);
startService(intent); startService(intentMainServicee);
// USB 摄像头行车记录仪进程
Intent intentCarcorderService = new Intent(this, CarcorderService.class);
startService(intentCarcorderService);
} }
protected void loadContainerModules() { protected void loadContainerModules() {
@@ -272,7 +277,7 @@ public class MainActivity extends MvpActivity<MainView, MainPresenter> implement
@Override @Override
public void loadFunctionFragment() { public void loadFunctionFragment() {
Logger.d(TAG,"loadFunctionFragment……"); Logger.d(TAG, "loadFunctionFragment……");
// 加载 HMI 图层 // 加载 HMI 图层
BaseFragment fragmentHdMap = (BaseFragment) ARouter.getInstance().build(MoGoFragmentPaths.PATH_FRAGMENT_HMI).navigation(); BaseFragment fragmentHdMap = (BaseFragment) ARouter.getInstance().build(MoGoFragmentPaths.PATH_FRAGMENT_HMI).navigation();
addFragment(fragmentHdMap, fragmentHdMap.getTagName(), R.id.module_main_id_waring_fragment); addFragment(fragmentHdMap, fragmentHdMap.getTagName(), R.id.module_main_id_waring_fragment);

View File

@@ -66,17 +66,17 @@ public class ApmCrashReportProvider implements ITestCrashReportProvider {
//设置分配的appid //设置分配的appid
builder.aid(BYTEAMP_APPID); builder.aid(BYTEAMP_APPID);
//是否开启卡顿功能 //是否开启卡顿功能
builder.blockDetect(true); builder.blockDetect(false);
//是否开启严重卡顿功能 //是否开启严重卡顿功能
builder.seriousBlockDetect(true); builder.seriousBlockDetect(false);
//是否开启流畅性和丢帧 //是否开启流畅性和丢帧
builder.fpsMonitor(true); builder.fpsMonitor(false);
//控制是否打开WebVeiw监控 //控制是否打开WebVeiw监控
builder.enableWebViewMonitor(true); builder.enableWebViewMonitor(false);
//控制是否打开内存监控 //控制是否打开内存监控
builder.memoryMonitor(true); builder.memoryMonitor(false);
//控制是否打开电量监控 //控制是否打开电量监控
builder.batteryMonitor(true); builder.batteryMonitor(false);
//是否打印日志线上release版本要配置为false //是否打印日志线上release版本要配置为false
builder.debugMode(true); builder.debugMode(true);
//支持用户自定义user_id把平台数据和自己用户关联起来可以不配置 //支持用户自定义user_id把平台数据和自己用户关联起来可以不配置