[Change]
修改速度取值,更新行车记录仪代码。 DEMO必须依附于预览获取YUV数据,不符合需求,这里先接入直播,后续需要按照源码抽离直接通过USB获取YUV的服务 Signed-off-by: donghongyu <donghongyu@zhidaoauto.com>
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,9 @@ public class UVCCameraHelper {
|
||||
void onConnectDev(UsbDevice device, boolean isConnected);
|
||||
|
||||
void onDisConnectDev(UsbDevice device);
|
||||
|
||||
void onCancelDev(UsbDevice device);
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
mCtrlBlock = ctrlBlock;
|
||||
openCamera(ctrlBlock);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// wait for camera created
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// start previewing
|
||||
startPreview(mCamView);
|
||||
new Thread(() -> {
|
||||
// wait for camera created
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// start previewing
|
||||
startPreview(mCamView);
|
||||
}).start();
|
||||
if (listener != null) {
|
||||
listener.onConnectDev(device, true);
|
||||
@@ -135,6 +135,9 @@ public class UVCCameraHelper {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
if (mCameraHandler != null && mCameraHandler.isOpened()) {
|
||||
|
||||
File file = new File(savePath);
|
||||
if (!Objects.requireNonNull(file.getParentFile()).exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -24,199 +47,192 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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 USBMonitor getUSBMonitor();
|
||||
public interface CameraDialogParent {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */);
|
||||
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 {
|
||||
dialog.show(parent.getFragmentManager(), TAG);
|
||||
} catch (final IllegalStateException e) {
|
||||
dialog = null;
|
||||
mUSBMonitor = ((CameraDialogParent)activity).getUSBMonitor();
|
||||
} catch (final ClassCastException e) {
|
||||
} 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 */) {
|
||||
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
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState == null) {
|
||||
savedInstanceState = getArguments();
|
||||
}
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState == null)
|
||||
savedInstanceState = getArguments();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(final Bundle saveInstanceState) {
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
saveInstanceState.putAll(args);
|
||||
}
|
||||
super.onSaveInstanceState(saveInstanceState);
|
||||
}
|
||||
@Override
|
||||
public void onSaveInstanceState(final Bundle saveInstanceState) {
|
||||
final Bundle args = getArguments();
|
||||
if (args != null)
|
||||
saveInstanceState.putAll(args);
|
||||
super.onSaveInstanceState(saveInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public Dialog onCreateDialog(final Bundle savedInstanceState) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setView(initView());
|
||||
builder.setTitle(R.string.select);
|
||||
builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
|
||||
builder.setNegativeButton(android.R.string.cancel, mOnDialogClickListener);
|
||||
builder.setNeutralButton(R.string.refresh, null);
|
||||
final Dialog dialog = builder.create();
|
||||
dialog.setCancelable(true);
|
||||
dialog.setCanceledOnTouchOutside(true);
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setView(initView());
|
||||
builder.setTitle(R.string.select);
|
||||
builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
|
||||
builder.setNegativeButton(android.R.string.cancel , mOnDialogClickListener);
|
||||
builder.setNeutralButton(R.string.refresh, null);
|
||||
final Dialog dialog = builder.create();
|
||||
dialog.setCancelable(true);
|
||||
dialog.setCanceledOnTouchOutside(true);
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create view that this fragment shows
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private final View initView() {
|
||||
final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
|
||||
mSpinner = (Spinner) rootView.findViewById(R.id.spinner1);
|
||||
final View empty = rootView.findViewById(android.R.id.empty);
|
||||
mSpinner.setEmptyView(empty);
|
||||
return rootView;
|
||||
}
|
||||
/**
|
||||
* create view that this fragment shows
|
||||
* @return
|
||||
*/
|
||||
private final View initView() {
|
||||
final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
|
||||
mSpinner = (Spinner)rootView.findViewById(R.id.spinner1);
|
||||
final View empty = rootView.findViewById(android.R.id.empty);
|
||||
mSpinner.setEmptyView(empty);
|
||||
return rootView;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateDevices();
|
||||
final Button button = (Button) getDialog().findViewById(android.R.id.button3);
|
||||
if (button != null) {
|
||||
button.setOnClickListener(mOnClickListener);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateDevices();
|
||||
final Button button = (Button)getDialog().findViewById(android.R.id.button3);
|
||||
if (button != null) {
|
||||
button.setOnClickListener(mOnClickListener);
|
||||
}
|
||||
}
|
||||
|
||||
private final OnClickListener mOnClickListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
switch (v.getId()) {
|
||||
case android.R.id.button3:
|
||||
updateDevices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
private final OnClickListener mOnClickListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
switch (v.getId()) {
|
||||
case android.R.id.button3:
|
||||
updateDevices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
final Object item = mSpinner.getSelectedItem();
|
||||
if (item instanceof UsbDevice) {
|
||||
mUSBMonitor.requestPermission((UsbDevice) item);
|
||||
((CameraDialogParent) getActivity()).onDialogResult(false);
|
||||
}
|
||||
break;
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
((CameraDialogParent) getActivity()).onDialogResult(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
final Object item = mSpinner.getSelectedItem();
|
||||
if (item instanceof UsbDevice) {
|
||||
mUSBMonitor.requestPermission((UsbDevice)item);
|
||||
((CameraDialogParent)getActivity()).onDialogResult(false);
|
||||
}
|
||||
break;
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
((CameraDialogParent)getActivity()).onDialogResult(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCancel(final DialogInterface dialog) {
|
||||
((CameraDialogParent) getActivity()).onDialogResult(true);
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
@Override
|
||||
public void onCancel(final DialogInterface dialog) {
|
||||
((CameraDialogParent)getActivity()).onDialogResult(true);
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
|
||||
public void updateDevices() {
|
||||
public void updateDevices() {
|
||||
// mUSBMonitor.dumpDevices();
|
||||
final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
|
||||
mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
|
||||
mSpinner.setAdapter(mDeviceListAdapter);
|
||||
}
|
||||
final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
|
||||
mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
|
||||
mSpinner.setAdapter(mDeviceListAdapter);
|
||||
}
|
||||
|
||||
private static final class DeviceListAdapter extends BaseAdapter {
|
||||
private static final class DeviceListAdapter extends BaseAdapter {
|
||||
|
||||
private final LayoutInflater mInflater;
|
||||
private final List<UsbDevice> mList;
|
||||
private final LayoutInflater mInflater;
|
||||
private final List<UsbDevice> mList;
|
||||
|
||||
public DeviceListAdapter(final Context context, final List<UsbDevice> list) {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mList = list != null ? list : new ArrayList<UsbDevice>();
|
||||
}
|
||||
public DeviceListAdapter(final Context context, final List<UsbDevice>list) {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mList = list != null ? list : new ArrayList<UsbDevice>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mList.size();
|
||||
}
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getItem(final int position) {
|
||||
if ((position >= 0) && (position < mList.size())) {
|
||||
return mList.get(position);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public UsbDevice getItem(final int position) {
|
||||
if ((position >= 0) && (position < mList.size()))
|
||||
return mList.get(position);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(final int position) {
|
||||
return position;
|
||||
}
|
||||
@Override
|
||||
public long getItemId(final int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final int position, View convertView, final ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
|
||||
}
|
||||
if (convertView instanceof CheckedTextView) {
|
||||
final UsbDevice device = getItem(position);
|
||||
((CheckedTextView) convertView).setText(
|
||||
String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public View getView(final int position, View convertView, final ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
|
||||
}
|
||||
if (convertView instanceof CheckedTextView) {
|
||||
final UsbDevice device = getItem(position);
|
||||
((CheckedTextView)convertView).setText(
|
||||
String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -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;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -97,20 +120,15 @@ public final class USBMonitor {
|
||||
}
|
||||
|
||||
public USBMonitor(final Context context, final OnDeviceConnectListener listener) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "USBMonitor:Constructor");
|
||||
}
|
||||
if (listener == null) {
|
||||
if (DEBUG) Log.v(TAG, "USBMonitor:Constructor");
|
||||
if (listener == null)
|
||||
throw new IllegalArgumentException("OnDeviceConnectListener should not null.");
|
||||
}
|
||||
mWeakContext = new WeakReference<Context>(context);
|
||||
mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
|
||||
mOnDeviceConnectListener = listener;
|
||||
mAsyncHandler = HandlerThreadHandler.createHandler(TAG);
|
||||
destroyed = false;
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,9 +136,7 @@ public final class USBMonitor {
|
||||
* never reuse again
|
||||
*/
|
||||
public void destroy() {
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "destroy:");
|
||||
}
|
||||
if (DEBUG) Log.i(TAG, "destroy:");
|
||||
unregister();
|
||||
if (!destroyed) {
|
||||
destroyed = true;
|
||||
@@ -153,13 +169,9 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public synchronized void register() throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
if (mPermissionIntent == null) {
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "register:");
|
||||
}
|
||||
if (DEBUG) Log.i(TAG, "register:");
|
||||
final Context context = mWeakContext.get();
|
||||
if (context != null) {
|
||||
mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
|
||||
@@ -209,9 +221,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.clear();
|
||||
mDeviceFilters.add(filter);
|
||||
}
|
||||
@@ -222,9 +232,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.add(filter);
|
||||
}
|
||||
|
||||
@@ -234,9 +242,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.remove(filter);
|
||||
}
|
||||
|
||||
@@ -246,9 +252,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void setDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.clear();
|
||||
mDeviceFilters.addAll(filters);
|
||||
}
|
||||
@@ -259,9 +263,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public void addDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.addAll(filters);
|
||||
}
|
||||
|
||||
@@ -270,9 +272,7 @@ public final class USBMonitor {
|
||||
* @param filters
|
||||
*/
|
||||
public void removeDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
mDeviceFilters.removeAll(filters);
|
||||
}
|
||||
|
||||
@@ -282,9 +282,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public int getDeviceCount() throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
return getDeviceList().size();
|
||||
}
|
||||
|
||||
@@ -294,9 +292,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public List<UsbDevice> getDeviceList() throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
return getDeviceList(mDeviceFilters);
|
||||
}
|
||||
|
||||
@@ -307,9 +303,7 @@ public final class USBMonitor {
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) throws IllegalStateException {
|
||||
if (destroyed) {
|
||||
throw new IllegalStateException("already destroyed");
|
||||
}
|
||||
if (destroyed) throw new IllegalStateException("already destroyed");
|
||||
// get detected devices
|
||||
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
|
||||
// store those devices info before matching filter xml file
|
||||
@@ -353,8 +347,8 @@ public final class USBMonitor {
|
||||
break;
|
||||
} else {
|
||||
// collection failed dev's class and subclass
|
||||
String devModel = android.os.Build.MODEL;
|
||||
String devSystemVersion = android.os.Build.VERSION.RELEASE;
|
||||
String devModel = Build.MODEL;
|
||||
String devSystemVersion = Build.VERSION.RELEASE;
|
||||
String devClass = String.valueOf(device.getDeviceClass());
|
||||
String subClass = String.valueOf(device.getDeviceSubclass());
|
||||
try{
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
|
||||
@@ -2,9 +2,7 @@ package com.serenegiant.usb.common;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.SurfaceTexture;
|
||||
@@ -22,7 +20,6 @@ import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.serenegiant.usb.IFrameCallback;
|
||||
import com.serenegiant.usb.ParentPreviewConstraintLayout;
|
||||
import com.serenegiant.usb.Size;
|
||||
import com.serenegiant.usb.USBMonitor;
|
||||
import com.serenegiant.usb.UVCCamera;
|
||||
@@ -52,7 +49,6 @@ import java.nio.ByteBuffer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
@@ -68,19 +64,19 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
|
||||
// 对外回调接口
|
||||
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;
|
||||
@@ -180,16 +176,12 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "close:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "close:");
|
||||
if (isOpened()) {
|
||||
stopPreview();
|
||||
sendEmptyMessage(MSG_CLOSE);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "close:finished");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "close:finished");
|
||||
}
|
||||
|
||||
// 切换分辨率
|
||||
@@ -214,18 +206,14 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
|
||||
// 关闭Camera预览
|
||||
public void stopPreview() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "stopPreview:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "stopPreview:");
|
||||
removeMessages(MSG_PREVIEW_START);
|
||||
if (isRecording()) {
|
||||
stopRecording();
|
||||
}
|
||||
if (isPreviewing()) {
|
||||
final CameraThread thread = mWeakThread.get();
|
||||
if (thread == null) {
|
||||
return;
|
||||
}
|
||||
if (thread == null) return;
|
||||
synchronized (thread.mSync) {
|
||||
sendEmptyMessage(MSG_PREVIEW_STOP);
|
||||
if (!isCameraThread()) {
|
||||
@@ -239,12 +227,10 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "stopPreview:finished");
|
||||
}
|
||||
if (DEBUG) 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;
|
||||
checkReleased();
|
||||
sendMessage(obtainMessage(MSG_CAPTURE_STILL, path));
|
||||
@@ -366,9 +352,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
@Override
|
||||
public void handleMessage(final Message msg) {
|
||||
final CameraThread thread = mWeakThread.get();
|
||||
if (thread == null) {
|
||||
return;
|
||||
}
|
||||
if (thread == null) return;
|
||||
switch (msg.what) {
|
||||
case MSG_OPEN:
|
||||
thread.handleOpen((USBMonitor.UsbControlBlock) msg.obj);
|
||||
@@ -472,17 +456,13 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public AbstractUVCCameraHandler getHandler() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "getHandler:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "getHandler:");
|
||||
synchronized (mSync) {
|
||||
if (mHandler == null) {
|
||||
if (mHandler == null)
|
||||
try {
|
||||
mSync.wait();
|
||||
} catch (final InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mHandler;
|
||||
}
|
||||
@@ -528,9 +508,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleOpen(final USBMonitor.UsbControlBlock ctrlBlock) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleOpen:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleOpen:");
|
||||
handleClose();
|
||||
try {
|
||||
final UVCCamera camera = new UVCCamera();
|
||||
@@ -542,15 +520,12 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
} catch (final Exception e) {
|
||||
callOnError(e);
|
||||
}
|
||||
if (DEBUG) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "supportedSize:" + (mUVCCamera != null ? mUVCCamera.getSupportedSize() : null));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleClose() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleClose:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleClose:");
|
||||
handleStopPusher();
|
||||
final UVCCamera camera;
|
||||
synchronized (mSync) {
|
||||
@@ -565,12 +540,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleStartPreview(final Object surface) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
}
|
||||
if ((mUVCCamera == null) || mIsPreviewing) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
if ((mUVCCamera == null) || mIsPreviewing) return;
|
||||
try {
|
||||
mUVCCamera.setPreviewSize(mWidth, mHeight, 1, 31, mPreviewMode, mBandwidthFactor);
|
||||
// 获取USB Camera预览数据,使用NV21颜色会失真
|
||||
@@ -603,9 +574,7 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleStopPreview() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStopPreview:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:");
|
||||
if (mIsPreviewing) {
|
||||
if (mUVCCamera != null) {
|
||||
mUVCCamera.stopPreview();
|
||||
@@ -617,20 +586,14 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
callOnStopPreview();
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStopPreview:finished");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStopPreview:finished");
|
||||
}
|
||||
|
||||
// 捕获静态图片
|
||||
public void handleCaptureStill(final String path) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleCaptureStill:");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleCaptureStill:");
|
||||
final Activity parent = mWeakParent.get();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
if (parent == null) return;
|
||||
// mSoundPool.play(mSoundId, 0.2f, 0.2f, 0, 0, 1.0f); // play shutter sound
|
||||
try {
|
||||
final Bitmap bitmap = mWeakCameraView.get().captureStillImage(mWidth, mHeight);
|
||||
@@ -647,7 +610,6 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
os.flush();
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_MEDIA_UPDATE, outputFile.getPath()));
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
os.close();
|
||||
@@ -706,9 +668,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
private H264EncodeConsumer mH264Consumer;
|
||||
|
||||
public void handleStartPusher(RecordParams params) {
|
||||
if ((mUVCCamera == null) || (mH264Consumer != null)) {
|
||||
if ((mUVCCamera == null) || (mH264Consumer != null))
|
||||
return;
|
||||
}
|
||||
// // 获取USB Camera预览数据
|
||||
// mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21);
|
||||
|
||||
@@ -747,9 +708,8 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
// 停止音视频编码线程
|
||||
stopAudioRecord();
|
||||
stopVideoRecord();
|
||||
if(isSupportOverlay) {
|
||||
if(isSupportOverlay)
|
||||
TxtOverlay.getInstance().release();
|
||||
}
|
||||
// // 停止捕获视频数据
|
||||
// if (mUVCCamera != null) {
|
||||
// mUVCCamera.stopCapture();
|
||||
@@ -915,23 +875,18 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public void handleUpdateMedia(final String path) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleUpdateMedia:path=" + path);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleUpdateMedia:path=" + path);
|
||||
final Activity parent = mWeakParent.get();
|
||||
final boolean released = (mHandler == null) || mHandler.mReleased;
|
||||
if (parent != null && parent.getApplicationContext() != null) {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "MediaScannerConnection#scanFile");
|
||||
}
|
||||
if (DEBUG) Log.i(TAG, "MediaScannerConnection#scanFile");
|
||||
MediaScannerConnection.scanFile(parent.getApplicationContext(), new String[]{path}, null, null);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "handleUpdateMedia:", e);
|
||||
}
|
||||
if (released || parent.isDestroyed()) {
|
||||
if (released || parent.isDestroyed())
|
||||
handleRelease();
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "MainActivity already destroyed");
|
||||
// give up to add this movie to MediaStore now.
|
||||
@@ -941,71 +896,57 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
}
|
||||
|
||||
public void handleRelease() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleRelease:mIsRecording=" + mIsRecording);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleRelease:mIsRecording=" + mIsRecording);
|
||||
handleClose();
|
||||
mCallbacks.clear();
|
||||
if (!mIsRecording) {
|
||||
mHandler.mReleased = true;
|
||||
Looper.myLooper().quit();
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleRelease:finished");
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleRelease:finished");
|
||||
}
|
||||
|
||||
// 自动对焦
|
||||
public void handleCameraFoucs() {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
}
|
||||
if ((mUVCCamera == null) || !mIsPreviewing) {
|
||||
if (DEBUG) Log.v(TAG_THREAD, "handleStartPreview:");
|
||||
if ((mUVCCamera == null) || !mIsPreviewing)
|
||||
return;
|
||||
}
|
||||
mUVCCamera.setAutoFocus(true);
|
||||
}
|
||||
|
||||
// 获取支持的分辨率
|
||||
public List<Size> getSupportedSizes() {
|
||||
if ((mUVCCamera == null) || !mIsPreviewing) {
|
||||
if ((mUVCCamera == null) || !mIsPreviewing)
|
||||
return null;
|
||||
}
|
||||
return mUVCCamera.getSupportedSizeList();
|
||||
}
|
||||
|
||||
private final MediaEncoder.MediaEncoderListener mMediaEncoderListener = new MediaEncoder.MediaEncoderListener() {
|
||||
@Override
|
||||
public void onPrepared(final MediaEncoder encoder) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "onPrepared:encoder=" + encoder);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "onPrepared:encoder=" + encoder);
|
||||
mIsRecording = true;
|
||||
if (encoder instanceof MediaVideoEncoder) {
|
||||
if (encoder instanceof MediaVideoEncoder)
|
||||
try {
|
||||
mWeakCameraView.get().setVideoEncoder((MediaVideoEncoder) encoder);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "onPrepared:", e);
|
||||
}
|
||||
}
|
||||
if (encoder instanceof MediaSurfaceEncoder) {
|
||||
if (encoder instanceof MediaSurfaceEncoder)
|
||||
try {
|
||||
mWeakCameraView.get().setVideoEncoder((MediaSurfaceEncoder) encoder);
|
||||
mUVCCamera.startCapture(((MediaSurfaceEncoder) encoder).getInputSurface());
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "onPrepared:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
@Override
|
||||
public void onStopped(final MediaEncoder encoder) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG_THREAD, "onStopped:encoder=" + encoder);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG_THREAD, "onStopped:encoder=" + encoder);
|
||||
if ((encoder instanceof MediaVideoEncoder)
|
||||
|| (encoder instanceof MediaSurfaceEncoder)) {
|
||||
|| (encoder instanceof MediaSurfaceEncoder))
|
||||
try {
|
||||
mIsRecording = false;
|
||||
final Activity parent = mWeakParent.get();
|
||||
@@ -1027,7 +968,6 @@ public abstract class AbstractUVCCameraHandler extends Handler {
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "onPrepared:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import com.serenegiant.usb.ParentPreviewConstraintLayout;
|
||||
import com.serenegiant.usb.UVCCamera;
|
||||
import com.serenegiant.usb.widget.CameraViewInterface;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.serenegiant.glutils.RendererHolder;
|
||||
import com.serenegiant.usb.ParentPreviewConstraintLayout;
|
||||
import com.serenegiant.usb.UVCCamera;
|
||||
import com.serenegiant.usb.widget.CameraViewInterface;
|
||||
|
||||
@@ -101,7 +123,6 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
|
||||
mRendererHolder = new RendererHolder(thread.getWidth(), thread.getHeight(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void release() {
|
||||
if (mRendererHolder != null) {
|
||||
mRendererHolder.release();
|
||||
@@ -110,8 +131,7 @@ public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
|
||||
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);
|
||||
if (mRendererHolder != null) {
|
||||
mRendererHolder.resize(width, height);
|
||||
|
||||
@@ -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;
|
||||
|
||||
public interface IAudioEncoder {
|
||||
|
||||
@@ -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;
|
||||
|
||||
public interface IVideoEncoder {
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
|
||||
@@ -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;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.serenegiant.usb.encoder;
|
||||
|
||||
/**
|
||||
* 录制参数
|
||||
/** 录制参数
|
||||
*
|
||||
* Created by jiangdongguo on 2017/10/19.
|
||||
*/
|
||||
|
||||
public class RecordParams {
|
||||
private String recordPath;
|
||||
private int recordDuration;
|
||||
|
||||
@@ -19,10 +19,12 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
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 String TAG = "TMPU";
|
||||
private static final String MIME_TYPE = "audio/mp4a-latm";
|
||||
@@ -38,7 +40,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
private AudioRecord mAudioRecord; // 音频采集
|
||||
private MediaCodec mAudioEncoder; // 音频编码
|
||||
private OnAACEncodeResultListener listener;
|
||||
private int mSamplingRateIndex = 0;//ADTS
|
||||
private int mSamplingRateIndex = 0;//ADTS
|
||||
private boolean isEncoderStart = false;
|
||||
private boolean isRecMp3 = false;
|
||||
private boolean isExit = false;
|
||||
@@ -46,7 +48,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
private WeakReference<Mp4MediaMuxer> mMuxerRef;
|
||||
private MediaFormat newFormat;
|
||||
|
||||
private static final int[] AUDIO_SOURCES = new int[]{
|
||||
private static final int[] AUDIO_SOURCES = new int[] {
|
||||
MediaRecorder.AudioSource.DEFAULT,
|
||||
MediaRecorder.AudioSource.MIC,
|
||||
MediaRecorder.AudioSource.CAMCORDER,
|
||||
@@ -54,7 +56,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
/**
|
||||
* 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
|
||||
64000, // 2
|
||||
48000, // 3
|
||||
@@ -75,13 +77,13 @@ public class AACEncodeConsumer extends Thread {
|
||||
private FileOutputStream fops;
|
||||
|
||||
// 编码流结果回调接口
|
||||
public interface OnAACEncodeResultListener {
|
||||
public interface OnAACEncodeResultListener{
|
||||
void onEncodeResult(byte[] data, int offset,
|
||||
int length, long timestamp);
|
||||
}
|
||||
|
||||
public AACEncodeConsumer() {
|
||||
for (int i = 0; i < AUDIO_SAMPLING_RATES.length; i++) {
|
||||
public AACEncodeConsumer(){
|
||||
for (int i=0;i < AUDIO_SAMPLING_RATES.length; i++) {
|
||||
if (AUDIO_SAMPLING_RATES[i] == SAMPLE_RATE) {
|
||||
mSamplingRateIndex = i;
|
||||
break;
|
||||
@@ -89,16 +91,16 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener) {
|
||||
public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
public void exit(){
|
||||
isExit = true;
|
||||
}
|
||||
|
||||
public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer) {
|
||||
this.mMuxerRef = new WeakReference<>(mMuxer);
|
||||
public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer){
|
||||
this.mMuxerRef = new WeakReference<>(mMuxer);
|
||||
Mp4MediaMuxer muxer = mMuxerRef.get();
|
||||
if (muxer != null && newFormat != null) {
|
||||
muxer.addTrack(newFormat, false);
|
||||
@@ -108,7 +110,7 @@ public class AACEncodeConsumer extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
// 开启音频采集、编码
|
||||
if (!isEncoderStart) {
|
||||
if(! isEncoderStart){
|
||||
initAudioRecord();
|
||||
initMediaCodec();
|
||||
}
|
||||
@@ -116,16 +118,16 @@ public class AACEncodeConsumer extends Thread {
|
||||
byte[] mp3Buffer = new byte[1024];
|
||||
|
||||
// 这里有问题,当本地录制结束后,没有写入
|
||||
while (!isExit) {
|
||||
while(! isExit){
|
||||
byte[] audioBuffer = new byte[2048];
|
||||
// 采集音频
|
||||
int readBytes = mAudioRecord.read(audioBuffer, 0, BUFFER_SIZE);
|
||||
int readBytes = mAudioRecord.read(audioBuffer,0,BUFFER_SIZE);
|
||||
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "采集音频readBytes = " + readBytes);
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"采集音频readBytes = "+readBytes);
|
||||
// 编码音频
|
||||
if (readBytes > 0) {
|
||||
encodeBytes(audioBuffer, readBytes);
|
||||
if(readBytes > 0){
|
||||
encodeBytes(audioBuffer,readBytes);
|
||||
}
|
||||
}
|
||||
// 停止音频采集、编码
|
||||
@@ -140,81 +142,81 @@ public class AACEncodeConsumer extends Thread {
|
||||
ByteBuffer[] outputBuffers = mAudioEncoder.getOutputBuffers();
|
||||
//返回编码器的一个输入缓存区句柄,-1表示当前没有可用的输入缓存区
|
||||
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(TIMES_OUT);
|
||||
if (inputBufferIndex >= 0) {
|
||||
if(inputBufferIndex >= 0){
|
||||
// 绑定一个被空的、可写的输入缓存区inputBuffer到客户端
|
||||
ByteBuffer inputBuffer = null;
|
||||
if (!isLollipop()) {
|
||||
ByteBuffer inputBuffer = null;
|
||||
if(!isLollipop()){
|
||||
inputBuffer = inputBuffers[inputBufferIndex];
|
||||
} else {
|
||||
}else{
|
||||
inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex);
|
||||
}
|
||||
// 向输入缓存区写入有效原始数据,并提交到编码器中进行编码处理
|
||||
if (audioBuf == null || readBytes <= 0) {
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, 0, getPTSUs(), MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||
} else {
|
||||
if(audioBuf==null || readBytes<=0){
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex,0,0,getPTSUs(),MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||
}else{
|
||||
inputBuffer.clear();
|
||||
inputBuffer.put(audioBuf);
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, readBytes, getPTSUs(), 0);
|
||||
mAudioEncoder.queueInputBuffer(inputBufferIndex,0,readBytes,getPTSUs(),0);
|
||||
}
|
||||
}
|
||||
|
||||
// 返回一个输出缓存区句柄,当为-1时表示当前没有可用的输出缓存区
|
||||
// mBufferInfo参数包含被编码好的数据,timesOut参数为超时等待的时间
|
||||
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
|
||||
MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
|
||||
int outputBufferIndex = -1;
|
||||
do {
|
||||
outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo, TIMES_OUT);
|
||||
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "获得编码器输出缓存区超时");
|
||||
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||
do{
|
||||
outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo,TIMES_OUT);
|
||||
if(outputBufferIndex == MediaCodec. INFO_TRY_AGAIN_LATER){
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"获得编码器输出缓存区超时");
|
||||
}else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
|
||||
// 如果API小于21,APP需要重新绑定编码器的输入缓存区;
|
||||
// 如果API大于21,则无需处理INFO_OUTPUT_BUFFERS_CHANGED
|
||||
if (!isLollipop()) {
|
||||
if(!isLollipop()){
|
||||
outputBuffers = mAudioEncoder.getOutputBuffers();
|
||||
}
|
||||
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
||||
}else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
|
||||
// 编码器输出缓存区格式改变,通常在存储数据之前且只会改变一次
|
||||
// 这里设置混合器视频轨道,如果音频已经添加则启动混合器(保证音视频同步)
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "编码器输出缓存区格式改变,添加视频轨道到混合器");
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"编码器输出缓存区格式改变,添加视频轨道到混合器");
|
||||
synchronized (AACEncodeConsumer.this) {
|
||||
newFormat = mAudioEncoder.getOutputFormat();
|
||||
if (mMuxerRef != null) {
|
||||
if(mMuxerRef != null){
|
||||
Mp4MediaMuxer muxer = mMuxerRef.get();
|
||||
if (muxer != null) {
|
||||
muxer.addTrack(newFormat, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}else{
|
||||
// 当flag属性置为BUFFER_FLAG_CODEC_CONFIG后,说明输出缓存区的数据已经被消费了
|
||||
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "编码数据被消费,BufferInfo的size属性置0");
|
||||
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0){
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"编码数据被消费,BufferInfo的size属性置0");
|
||||
mBufferInfo.size = 0;
|
||||
}
|
||||
// 数据流结束标志,结束本次循环
|
||||
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
||||
if (DEBUG)
|
||||
Log.i(TAG, "数据流结束,退出循环");
|
||||
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
|
||||
if(DEBUG)
|
||||
Log.i(TAG,"数据流结束,退出循环");
|
||||
break;
|
||||
}
|
||||
// 获取一个只读的输出缓存区inputBuffer ,它包含被编码好的数据
|
||||
ByteBuffer mBuffer = ByteBuffer.allocate(10240);
|
||||
ByteBuffer outputBuffer = null;
|
||||
if (!isLollipop()) {
|
||||
outputBuffer = outputBuffers[outputBufferIndex];
|
||||
} else {
|
||||
outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
|
||||
if(!isLollipop()){
|
||||
outputBuffer = outputBuffers[outputBufferIndex];
|
||||
}else{
|
||||
outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
|
||||
}
|
||||
if (mBufferInfo.size != 0) {
|
||||
if(mBufferInfo.size != 0){
|
||||
// 获取输出缓存区失败,抛出异常
|
||||
if (outputBuffer == null) {
|
||||
throw new RuntimeException("encodecOutputBuffer" + outputBufferIndex + "was null");
|
||||
if(outputBuffer == null){
|
||||
throw new RuntimeException("encodecOutputBuffer"+outputBufferIndex+"was null");
|
||||
}
|
||||
// 添加视频流到混合器
|
||||
if (mMuxerRef != null) {
|
||||
if(mMuxerRef != null){
|
||||
Mp4MediaMuxer muxer = mMuxerRef.get();
|
||||
if (muxer != null) {
|
||||
muxer.pumpStream(outputBuffer, mBufferInfo, false);
|
||||
@@ -228,25 +230,25 @@ public class AACEncodeConsumer extends Thread {
|
||||
addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7);
|
||||
mBuffer.flip();
|
||||
// 将AAC回调给MainModelImpl进行push
|
||||
if (listener != null) {
|
||||
Log.i(TAG, "----->得到aac数据流<-----");
|
||||
listener.onEncodeResult(mBuffer.array(), 0, mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000);
|
||||
if(listener != null){
|
||||
Log.i(TAG,"----->得到aac数据流<-----");
|
||||
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() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->开始采集音频");
|
||||
private void initAudioRecord(){
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->开始采集音频");
|
||||
// 设置进程优先级
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
|
||||
int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT);
|
||||
for (final int src : AUDIO_SOURCES) {
|
||||
for (final int src: AUDIO_SOURCES) {
|
||||
try {
|
||||
mAudioRecord = new AudioRecord(src,
|
||||
SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
|
||||
@@ -266,18 +268,18 @@ public class AACEncodeConsumer extends Thread {
|
||||
mAudioRecord.startRecording();
|
||||
}
|
||||
|
||||
private void initMediaCodec() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->开始编码音频");
|
||||
private void initMediaCodec(){
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->开始编码音频");
|
||||
MediaCodecInfo mCodecInfo = selectSupportCodec(MIME_TYPE);
|
||||
if (mCodecInfo == null) {
|
||||
Log.e(TAG, "编码器不支持" + MIME_TYPE + "类型");
|
||||
if(mCodecInfo == null){
|
||||
Log.e(TAG,"编码器不支持"+MIME_TYPE+"类型");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
try{
|
||||
mAudioEncoder = MediaCodec.createByCodecName(mCodecInfo.getName());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "创建编码器失败" + e.getMessage());
|
||||
}catch(IOException e){
|
||||
Log.e(TAG,"创建编码器失败"+e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
MediaFormat format = new MediaFormat();
|
||||
@@ -293,9 +295,9 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
|
||||
private void stopAudioRecord() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->停止采集音频");
|
||||
if (mAudioRecord != null) {
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->停止采集音频");
|
||||
if(mAudioRecord != null){
|
||||
mAudioRecord.stop();
|
||||
mAudioRecord.release();
|
||||
mAudioRecord = null;
|
||||
@@ -303,9 +305,9 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
|
||||
private void stopMediaCodec() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "AACEncodeConsumer-->停止编码音频");
|
||||
if (mAudioEncoder != null) {
|
||||
if(DEBUG)
|
||||
Log.d(TAG,"AACEncodeConsumer-->停止编码音频");
|
||||
if(mAudioEncoder != null){
|
||||
mAudioEncoder.stop();
|
||||
mAudioEncoder.release();
|
||||
mAudioEncoder = null;
|
||||
@@ -314,28 +316,28 @@ public class AACEncodeConsumer extends Thread {
|
||||
}
|
||||
|
||||
// API>=21
|
||||
private boolean isLollipop() {
|
||||
private boolean isLollipop(){
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
}
|
||||
|
||||
// API<=19
|
||||
private boolean isKITKAT() {
|
||||
private boolean isKITKAT(){
|
||||
return Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
|
||||
}
|
||||
|
||||
private long getPTSUs() {
|
||||
long result = System.nanoTime() / 1000;
|
||||
if (result < prevPresentationTimes) {
|
||||
result = (prevPresentationTimes - result) + result;
|
||||
private long getPTSUs(){
|
||||
long result = System.nanoTime()/1000;
|
||||
if(result < prevPresentationTimes){
|
||||
result = (prevPresentationTimes - result ) + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历所有编解码器,返回第一个与指定MIME类型匹配的编码器
|
||||
* 判断是否有支持指定mime类型的编码器
|
||||
*/
|
||||
private MediaCodecInfo selectSupportCodec(String mimeType) {
|
||||
* 判断是否有支持指定mime类型的编码器
|
||||
* */
|
||||
private MediaCodecInfo selectSupportCodec(String mimeType){
|
||||
int numCodecs = MediaCodecList.getCodecCount();
|
||||
for (int i = 0; i < numCodecs; 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[],数组长度缩减一半
|
||||
int shortLen = readBytes / 2;
|
||||
// 将byte[]数组装如ByteBuffer缓冲区
|
||||
|
||||
@@ -17,7 +17,9 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* 对YUV视频流进行编码
|
||||
* Created by jiangdongguo on 2017/5/6.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class H264EncodeConsumer extends Thread {
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@@ -10,9 +10,11 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Mp4封装混合器
|
||||
/**Mp4封装混合器
|
||||
*
|
||||
* Created by jianddongguo on 2017/7/28.
|
||||
*/
|
||||
|
||||
public class Mp4MediaMuxer {
|
||||
private static final boolean VERBOSE = false;
|
||||
private static final String TAG = Mp4MediaMuxer.class.getSimpleName();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user