containers) {
+ clear();
+ addAll(containers);
+ notifyDataSetChanged();
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
+ ItemUvcDeviceBinding binding;
if (convertView == null) {
- convertView = mInflater.inflate(R.layout.item_uvc_device, null);
- }
-
- final DeviceContainer device = getItem(position);
-
- String name = device.getName();
- TextView nameView = convertView.findViewById(R.id.device_name);
- nameView.setText(name);
-
- Button btn = convertView.findViewById(R.id.btn_connect_device);
- if (device.isRegisterFlag()) {
- btn.setBackgroundResource(R.drawable.button_red);
- btn.setText(R.string.uvc_settings_disconnect);
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ binding = DataBindingUtil.inflate(inflater, R.layout.item_uvc_device, parent, false);
+ convertView = binding.getRoot();
+ convertView.setTag(binding);
} else {
- btn.setBackgroundResource(R.drawable.button_blue);
- btn.setText(R.string.uvc_settings_connect);
+ binding = (ItemUvcDeviceBinding) convertView.getTag();
}
- btn.setOnClickListener((v) -> {
- if (device.isRegisterFlag()) {
- disconnectDevice(device);
- } else {
- connectDevice(device);
- }
- });
-
+ binding.setDeviceContainer(getItem(position));
return convertView;
}
}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCDevicePluginBindFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCDevicePluginBindFragment.java
new file mode 100644
index 0000000000..00342dc3f6
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCDevicePluginBindFragment.java
@@ -0,0 +1,168 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.widget.Toast;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.fragment.app.Fragment;
+
+import org.deviceconnect.android.deviceplugin.uvc.UVCDeviceService;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCDevicePluginBindActivity;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCSettingsActivity;
+import org.deviceconnect.android.deviceplugin.uvc.service.UVCService;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+public class UVCDevicePluginBindFragment extends Fragment implements UVCDevicePluginBindActivity.OnUVCDevicePluginListener {
+ /**
+ * UI 操作用の Handler.
+ */
+ private final Handler mUIHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (isBound()) {
+ // すでに接続されている場合には即座に呼び出す
+ onBindService();
+ }
+ }
+
+ @Override
+ public void onBindService() {
+ }
+
+ @Override
+ public void onUnbindService() {
+ }
+
+ @Override
+ public void onUvcConnected(UVCService service) {
+ }
+
+ @Override
+ public void onUvcDisconnected(UVCService service) {
+ }
+
+ /**
+ * 前の画面に戻ります.
+ *
+ * 前の画面がない場合には Activity を終了します。
+ */
+ public void popBackFragment() {
+ int entryCount = getParentFragmentManager().getBackStackEntryCount();
+ if (entryCount == 0) {
+ getActivity().finish();
+ } else {
+ findNavController(this).popBackStack();
+ }
+ }
+
+ /**
+ * ActionBar にタイトルを設定します.
+ *
+ * @param title タイトル
+ */
+ public void setTitle(String title) {
+ Activity activity = getActivity();
+ if (activity instanceof UVCDevicePluginBindActivity) {
+ ActionBar actionBar = ((UVCDevicePluginBindActivity) activity).getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(title);
+ }
+ }
+ }
+
+ /**
+ * UVCDeviceService との接続状態を確認します.
+ *
+ * @return 接続中の場合はtrue、それ以外はfalse
+ */
+ public boolean isBound() {
+ Activity activity = getActivity();
+ if (activity instanceof UVCDevicePluginBindActivity) {
+ return ((UVCDevicePluginBindActivity) activity).isBound();
+ }
+ return false;
+ }
+
+ /**
+ * 接続されている UVCDeviceService のインスタンスを取得します.
+ *
+ * 接続されていない場合には null を返却します。
+ *
+ * @return UVCDeviceService のインスタンス
+ */
+ public UVCDeviceService getUVCDeviceService() {
+ Activity activity = getActivity();
+ if (activity instanceof UVCDevicePluginBindActivity) {
+ return ((UVCDevicePluginBindActivity) activity).getUVCDeviceService();
+ }
+ return null;
+ }
+
+ /**
+ * 接続されている UVC サービスを取得します.
+ *
+ * 接続されていない場合には null を返却します。
+ *
+ * @return UVCService のインスタンス
+ */
+ public UVCService getUVCService() {
+ UVCDeviceService deviceService = getUVCDeviceService();
+ if (deviceService != null) {
+ return deviceService.getActiveUVCService();
+ }
+ return null;
+ }
+
+ /**
+ * 画面の向き設定を取得します.
+ *
+ * @return 画面の向き設定
+ */
+ public int getDisplayOrientation() {
+ Activity activity = getActivity();
+ if (activity != null) {
+ return activity.getRequestedOrientation();
+ }
+ return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ /**
+ * トーストを表示します.
+ *
+ * @param resId リソースID
+ */
+ public void showToast(int resId) {
+ runOnUiThread(() -> {
+ Context context = getContext();
+ if (context != null) {
+ Toast.makeText(context, resId, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ /**
+ * UI スレッドで Runnable を実行します.
+ *
+ * @param run 実行する Runnable
+ */
+ public void runOnUiThread(Runnable run) {
+ mUIHandler.post(run);
+ }
+
+ /**
+ * Runnable を指定された delay の分だけ後に実行します.
+ *
+ * @param run 実行する Runnable
+ * @param delay 遅延する時間(ミリ秒)
+ */
+ public void postDelay(Runnable run, long delay) {
+ mUIHandler.postDelayed(run, delay);
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCDevicePluginBindPreferenceFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCDevicePluginBindPreferenceFragment.java
new file mode 100644
index 0000000000..4e746c533e
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCDevicePluginBindPreferenceFragment.java
@@ -0,0 +1,150 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.InputType;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBar;
+import androidx.preference.EditTextPreference;
+import androidx.preference.PreferenceFragmentCompat;
+
+import org.deviceconnect.android.deviceplugin.uvc.UVCDeviceService;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCDevicePluginBindActivity;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCSettingsActivity;
+import org.deviceconnect.android.deviceplugin.uvc.service.UVCService;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+public abstract class UVCDevicePluginBindPreferenceFragment extends PreferenceFragmentCompat implements UVCDevicePluginBindActivity.OnUVCDevicePluginListener {
+
+ private final Handler mUIHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ if (view != null) {
+ view.setBackgroundColor(getResources().getColor(android.R.color.white));
+ }
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isBound()) {
+ onBindService();
+ }
+ }
+
+ @Override
+ public void onBindService() {
+ }
+
+ @Override
+ public void onUnbindService() {
+ }
+
+ @Override
+ public void onUvcConnected(UVCService service) {
+ }
+
+ @Override
+ public void onUvcDisconnected(UVCService service) {
+ }
+
+ /**
+ * 前の画面に戻ります.
+ *
+ * 前の画面がない場合には Activity を終了します。
+ */
+ public void popBackFragment() {
+ int entryCount = getParentFragmentManager().getBackStackEntryCount();
+ if (entryCount == 0) {
+ getActivity().finish();
+ } else {
+ findNavController(this).popBackStack();
+ }
+ }
+
+ /**
+ * ActionBar にタイトルを設定します.
+ *
+ * @param title タイトル
+ */
+ public void setTitle(String title) {
+ Activity activity = getActivity();
+ if (activity instanceof UVCDevicePluginBindActivity) {
+ ActionBar actionBar = ((UVCDevicePluginBindActivity) activity).getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(title);
+ }
+ }
+ }
+
+ /**
+ * UVCDeviceService との接続を確認します.
+ *
+ * @return 接続されている場合はtrue、それ以外はfalse
+ */
+ public boolean isBound() {
+ Activity activity = getActivity();
+ if (activity instanceof UVCDevicePluginBindActivity) {
+ return ((UVCDevicePluginBindActivity) activity).isBound();
+ }
+ return false;
+ }
+
+ /**
+ * 接続されている UVCDeviceService のインスタンスを取得します.
+ *
+ * 接続されていない場合には null を返却します。
+ *
+ * @return UVCDeviceService のインスタンス
+ */
+ public UVCDeviceService getUVCDeviceService() {
+ Activity activity = getActivity();
+ if (activity instanceof UVCDevicePluginBindActivity) {
+ return ((UVCDevicePluginBindActivity) activity).getUVCDeviceService();
+ }
+ return null;
+ }
+
+ /**
+ * 指定されたキーに対応する入力フォームを数値のみ入力可能に設定します.
+ *
+ * @param key キー
+ */
+ public void setInputTypeNumber(String key) {
+ EditTextPreference editTextPreference = findPreference(key);
+ if (editTextPreference != null) {
+ editTextPreference.setOnBindEditTextListener((editText) ->
+ editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED));
+ }
+ }
+
+ /**
+ * トーストを表示します.
+ *
+ * @param resId リソースID
+ */
+ public void showToast(int resId) {
+ runOnUiThread(() -> Toast.makeText(getContext(), resId, Toast.LENGTH_SHORT).show());
+ }
+
+ /**
+ * UI スレッドで Runnable を実行します.
+ *
+ * @param run 実行する Runnable
+ */
+ public void runOnUiThread(Runnable run) {
+ mUIHandler.post(run);
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCPermissionConfirmationFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCPermissionConfirmationFragment.java
new file mode 100644
index 0000000000..cc249b3cb7
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCPermissionConfirmationFragment.java
@@ -0,0 +1,17 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+public class UVCPermissionConfirmationFragment extends PermissionConfirmationFragment {
+ @Override
+ public void onNextFragment() {
+ findNavController(this).navigate(R.id.action_permission_to_plugin);
+ }
+
+ @Override
+ public void onPermissionDeny() {
+ findNavController(this).navigate(R.id.action_permission_error_dialog);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCPluginSettingsFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCPluginSettingsFragment.java
new file mode 100644
index 0000000000..0ec5088751
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCPluginSettingsFragment.java
@@ -0,0 +1,50 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreferenceCompat;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+import org.deviceconnect.android.deviceplugin.uvc.UVCDeviceService;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCSettingsActivity;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+public class UVCPluginSettingsFragment extends UVCDevicePluginBindPreferenceFragment {
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.settings_uvc_plugin, rootKey);
+ }
+
+ @Override
+ public void onBindService() {
+ UVCDeviceService deviceService = getUVCDeviceService();
+ if (deviceService != null) {
+ setAuth(deviceService);
+ }
+ setTitle(getString(R.string.uvc_settings_uvc_title));
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(final Preference preference) {
+ if ("uvc_settings_service_list".equals(preference.getKey())) {
+ findNavController(this).navigate(R.id.action_plugin_to_service);
+ } else if ("uvc_settings_service_instruction".equals(preference.getKey())) {
+ findNavController(this).navigate(R.id.action_plugin_to_instruction);
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ private void setAuth(UVCDeviceService deviceService) {
+ SwitchPreferenceCompat pref = findPreference("uvc_settings_auth");
+ if (pref != null) {
+ pref.setChecked(deviceService.isUseLocalOAuth());
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ deviceService.setUseLocalOAuth((Boolean) newValue);
+ return true;
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCRecorderListFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCRecorderListFragment.java
new file mode 100644
index 0000000000..680f42933f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCRecorderListFragment.java
@@ -0,0 +1,176 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.annotation.NonNull;
+import androidx.databinding.DataBindingUtil;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+import org.deviceconnect.android.deviceplugin.uvc.UVCDeviceService;
+import org.deviceconnect.android.deviceplugin.uvc.databinding.FragmentUvcRecorderListBinding;
+import org.deviceconnect.android.deviceplugin.uvc.databinding.ItemUvcRecorderBinding;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.service.UVCService;
+import org.deviceconnect.android.service.DConnectService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+public class UVCRecorderListFragment extends UVCDevicePluginBindFragment {
+ private RecorderAdapter mAdapter;
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ FragmentUvcRecorderListBinding binding = DataBindingUtil.inflate(inflater,
+ R.layout.fragment_uvc_recorder_list, container, false);
+ binding.setPresenter(this);
+
+ mAdapter = new RecorderAdapter(getContext(), new ArrayList<>());
+
+ View rootView = binding.getRoot();
+ ListView listView = rootView.findViewById(R.id.recorder_list_view);
+ listView.setAdapter(mAdapter);
+ listView.setItemsCanFocus(true);
+ return rootView;
+ }
+
+ @Override
+ public void onBindService() {
+ UVCDeviceService deviceService = getUVCDeviceService();
+ if (deviceService != null) {
+ String serviceId = getServiceId();
+ if (serviceId != null) {
+ UVCService service = deviceService.findUVCServiceById(serviceId);
+ if (service != null) {
+ setTitle(service.getName());
+ }
+ }
+ }
+ mAdapter.setContainers(createRecorderContainerList());
+
+ if (!isOnlineUVCService()) {
+ // UVC が接続されていない場合には前の画面に戻ります
+ popBackFragment();
+ }
+ }
+
+ @Override
+ public void onUvcDisconnected(UVCService service) {
+ popBackFragment();
+ }
+
+ public boolean isOnlineUVCService() {
+ UVCService service = getUVCService();
+ if (service != null) {
+ return service.isOnline();
+ }
+ return false;
+ }
+
+ private String getServiceId() {
+ Bundle args = getArguments();
+ if (args != null) {
+ return args.getString("service_id");
+ }
+ return null;
+ }
+
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ gotoRecorderSettings(mAdapter.getItem(position));
+ }
+
+ private void gotoRecorderSettings(RecorderContainer container) {
+ Bundle bundle = new Bundle();
+ bundle.putString("service_id", container.getServiceId());
+ bundle.putString("recorder_id", container.getId());
+ bundle.putString("settings_name", container.getSettingsName());
+ findNavController(this).navigate(R.id.action_recorder_to_main, bundle);
+ }
+
+ private List createRecorderContainerList() {
+ List containers = new ArrayList<>();
+
+ UVCDeviceService deviceService = getUVCDeviceService();
+ if (deviceService != null) {
+ String serviceId = getServiceId();
+ if (serviceId != null) {
+ UVCService service = deviceService.findUVCServiceById(serviceId);
+ if (service != null) {
+ for (MediaRecorder recorder : service.getUvcRecorderList()) {
+ if (recorder instanceof UvcRecorder) {
+ containers.add(new RecorderContainer(service, (UvcRecorder) recorder));
+ }
+ }
+ }
+ }
+ }
+
+ return containers;
+ }
+
+ public static class RecorderContainer {
+ private final DConnectService mService;
+ private final MediaRecorder mRecorder;
+ private final String mSettingsName;
+
+ RecorderContainer(DConnectService service, UvcRecorder recorder) {
+ mService = service;
+ mRecorder = recorder;
+ mSettingsName = recorder.getSettingsName();
+ }
+
+ public String getName() {
+ return mRecorder.getName();
+ }
+
+ public String getId() {
+ return mRecorder.getId();
+ }
+
+ public String getSettingsName() {
+ return mSettingsName;
+ }
+
+ public String getServiceId() {
+ return mService.getId();
+ }
+ }
+
+ private static class RecorderAdapter extends ArrayAdapter {
+ RecorderAdapter(final Context context, final List objects) {
+ super(context, 0, objects);
+ }
+
+ void setContainers(List containers) {
+ clear();
+ addAll(containers);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+ ItemUvcRecorderBinding binding;
+ if (convertView == null) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ binding = DataBindingUtil.inflate(inflater, R.layout.item_uvc_recorder, parent, false);
+ convertView = binding.getRoot();
+ convertView.setTag(binding);
+ } else {
+ binding = (ItemUvcRecorderBinding) convertView.getTag();
+ }
+ binding.setContainer(getItem(position));
+ return convertView;
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsBaseFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsBaseFragment.java
new file mode 100644
index 0000000000..85dbd5e56d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsBaseFragment.java
@@ -0,0 +1,118 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+import org.deviceconnect.android.deviceplugin.uvc.UVCDeviceService;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCDevicePluginBindActivity;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCSettingsActivity;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.service.UVCService;
+
+public abstract class UVCSettingsBaseFragment extends UVCDevicePluginBindPreferenceFragment {
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ if (view != null) {
+ view.setBackgroundColor(getResources().getColor(android.R.color.white));
+ }
+ return view;
+ }
+
+ @Override
+ public void onBindService() {
+ String title = createTitle();
+ if (title != null) {
+ setTitle(title);
+ }
+ }
+
+ @Override
+ public void onUvcDisconnected(UVCService service) {
+ popBackFragment();
+ }
+
+ private String createTitle() {
+ UVCService service = getUVCService();
+ if (service != null) {
+ String title = service.getName();
+ UvcRecorder recorder = getRecorder();
+ if (recorder != null) {
+ title += " - " + recorder.getName();
+ }
+ return title;
+ }
+ return null;
+ }
+
+ public UVCService getUVCService() {
+ UVCDeviceService deviceService = getUVCDeviceService();
+ if (deviceService != null) {
+ return deviceService.findUVCServiceById(getServiceId());
+ }
+ return null;
+ }
+
+ public boolean isOnlineUVCService() {
+ UVCService service = getUVCService();
+ if (service != null) {
+ return service.isOnline();
+ }
+ return false;
+ }
+
+ public UvcRecorder getRecorder() {
+ UVCService service = getUVCService();
+ if (service != null) {
+ return service.findUvcRecorderById(getRecorderId());
+ }
+ return null;
+ }
+
+ public String getServiceId() {
+ return getArgs("service_id");
+ }
+
+ public String getRecorderId() {
+ return getArgs("recorder_id");
+ }
+
+ public String getSettingsName() {
+ return getArgs("settings_name");
+ }
+
+ private String getArgs(String key) {
+ String serviceId = getBundleArgs(key);
+ if (serviceId == null) {
+ serviceId = getIntentArgs(key);
+ }
+ return serviceId;
+ }
+
+ private String getBundleArgs(String key) {
+ Bundle args = getArguments();
+ if (args != null) {
+ return args.getString(key);
+ }
+ return null;
+ }
+
+ private String getIntentArgs(String key) {
+ Activity a = getActivity();
+ if (a != null) {
+ Intent args = a.getIntent();
+ if (args != null) {
+ return args.getStringExtra(key);
+ }
+ }
+ return null;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsBroadcastFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsBroadcastFragment.java
new file mode 100644
index 0000000000..ddfbdc3eb7
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsBroadcastFragment.java
@@ -0,0 +1,13 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.os.Bundle;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+public class UVCSettingsBroadcastFragment extends UVCSettingsBaseFragment {
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ getPreferenceManager().setSharedPreferencesName(getSettingsName());
+ setPreferencesFromResource(R.xml.settings_uvc_recorder_broadcast, rootKey);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsMainFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsMainFragment.java
new file mode 100644
index 0000000000..f368fb233e
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsMainFragment.java
@@ -0,0 +1,57 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.os.Bundle;
+
+import androidx.preference.Preference;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+public class UVCSettingsMainFragment extends UVCSettingsBaseFragment {
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.settings_uvc_recorder_main, rootKey);
+ }
+
+ @Override
+ public void onBindService() {
+ super.onBindService();
+
+ if (!isOnlineUVCService()) {
+ // UVC が接続されていない場合には前の画面に戻ります
+ popBackFragment();
+ }
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(final Preference preference) {
+ Bundle arguments = createArguments();
+ if (arguments != null) {
+ if ("recorder_settings_video".equals(preference.getKey())) {
+ findNavController(this).navigate(R.id.action_main_to_video, arguments);
+ } else if ("recorder_settings_srt".equals(preference.getKey())) {
+ findNavController(this).navigate(R.id.action_main_to_srt, arguments);
+ } else if ("recorder_settings_broadcast".equals(preference.getKey())) {
+ findNavController(this).navigate(R.id.action_main_to_broadcast, arguments);
+ } else if ("recorder_settings_port".equals(preference.getKey())) {
+ findNavController(this).navigate(R.id.action_main_to_port, arguments);
+ }
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ private Bundle createArguments() {
+ String serviceId = getServiceId();
+ String recorderId = getRecorderId();
+ String settingsName = getSettingsName();
+ if (serviceId != null && recorderId != null && settingsName != null) {
+ Bundle bundle = new Bundle();
+ bundle.putString("service_id", serviceId);
+ bundle.putString("recorder_id", recorderId);
+ bundle.putString("settings_name", settingsName);
+ return bundle;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsParameterFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsParameterFragment.java
new file mode 100644
index 0000000000..84c9e8fb3a
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsParameterFragment.java
@@ -0,0 +1,66 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.content.SharedPreferences;
+
+import androidx.fragment.app.DialogFragment;
+import androidx.preference.Preference;
+
+import org.deviceconnect.android.deviceplugin.uvc.fragment.preference.SeekBarDialogFragment;
+import org.deviceconnect.android.deviceplugin.uvc.fragment.preference.SeekBarDialogPreference;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+
+public abstract class UVCSettingsParameterFragment extends UVCSettingsBaseFragment {
+ /**
+ * カスタム Preference を判別するためのタグ.
+ */
+ private static final String DIALOG_FRAGMENT_TAG = "CustomPreference";
+
+ /**
+ * 設定が変更されたことを保持するフラグ.
+ */
+ private boolean mChangedValue;
+
+ /**
+ * 設定が変更されてことを受信するリスナー.
+ */
+ private final SharedPreferences.OnSharedPreferenceChangeListener mListener =
+ (sharedPreferences, key) -> mChangedValue = true;
+
+ @Override
+ public void onDisplayPreferenceDialog(Preference preference) {
+ if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
+ return;
+ }
+
+ if (preference instanceof SeekBarDialogPreference) {
+ DialogFragment f = SeekBarDialogFragment.newInstance(preference.getKey());
+ f.setTargetFragment(this, 0);
+ f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+ } else {
+ super.onDisplayPreferenceDialog(preference);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ getPreferenceManager().getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(mListener);
+
+ MediaRecorder recorder = getRecorder();
+
+ // 設定が変更されていた場合には、レコーダに通知を行う
+ if (mChangedValue && recorder != null) {
+ recorder.onConfigChange();
+ }
+
+ super.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mChangedValue = false;
+ getPreferenceManager().getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(mListener);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsPortFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsPortFragment.java
new file mode 100644
index 0000000000..054d60227f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsPortFragment.java
@@ -0,0 +1,22 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.os.Bundle;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+public class UVCSettingsPortFragment extends UVCSettingsBaseFragment {
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ getPreferenceManager().setSharedPreferencesName(getSettingsName());
+ setPreferencesFromResource(R.xml.settings_uvc_recorder_port, rootKey);
+ }
+
+ @Override
+ public void onBindService() {
+ super.onBindService();
+
+ setInputTypeNumber("mjpeg_port");
+ setInputTypeNumber("rtsp_port");
+ setInputTypeNumber("srt_port");
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsSRTFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsSRTFragment.java
new file mode 100644
index 0000000000..a313efc1d9
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsSRTFragment.java
@@ -0,0 +1,43 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.preference.EditTextPreference;
+import androidx.preference.Preference;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+public class UVCSettingsSRTFragment extends UVCSettingsBaseFragment {
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ getPreferenceManager().setSharedPreferencesName(getSettingsName());
+ setPreferencesFromResource(R.xml.settings_uvc_recorder_srt, rootKey);
+
+ setSummaryOptionAuto(getString(R.string.pref_key_settings_srt_inputbw));
+ setSummaryOptionAuto(getString(R.string.pref_key_settings_srt_oheadbw));
+
+ setInputTypeNumber(getString(R.string.pref_key_settings_srt_peerlatency));
+ setInputTypeNumber(getString(R.string.pref_key_settings_srt_lossmaxttl));
+ setInputTypeNumber(getString(R.string.pref_key_settings_srt_oheadbw));
+ setInputTypeNumber(getString(R.string.pref_key_settings_srt_inputbw));
+ setInputTypeNumber(getString(R.string.pref_key_settings_srt_conntimeo));
+ setInputTypeNumber(getString(R.string.pref_key_settings_srt_peeridletimeo));
+ setInputTypeNumber(getString(R.string.pref_key_settings_srt_packetfilter));
+ }
+
+ private void setSummaryOptionAuto(String name) {
+ EditTextPreference inputBwPref = findPreference(name);
+ if (inputBwPref != null) {
+ inputBwPref.setSummaryProvider(summaryOptionAuto);
+ }
+ }
+
+ private final Preference.SummaryProvider summaryOptionAuto = (preference) -> {
+ String value = preference.getText();
+ if (TextUtils.isEmpty(value)) {
+ return getString(R.string.uvc_setting_srt_option_auto);
+ }
+ return value;
+ };
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsVideoFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsVideoFragment.java
new file mode 100644
index 0000000000..0357c157b0
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/UVCSettingsVideoFragment.java
@@ -0,0 +1,480 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.preference.EditTextPreference;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+import org.deviceconnect.android.deviceplugin.uvc.fragment.preference.SeekBarDialogPreference;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H264Level;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H264Profile;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H265Level;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H265Profile;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.util.CapabilityUtil;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class UVCSettingsVideoFragment extends UVCSettingsParameterFragment {
+ private MediaRecorder mMediaRecorder;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ getPreferenceManager().setSharedPreferencesName(getSettingsName());
+ setPreferencesFromResource(R.xml.settings_uvc_recorder_video, rootKey);
+ }
+
+ @Override
+ public void onBindService() {
+ super.onBindService();
+
+ mMediaRecorder = getRecorder();
+
+ if (mMediaRecorder == null) {
+ return;
+ }
+
+ MediaRecorder.Settings settings = mMediaRecorder.getSettings();
+
+ setPictureSizePreference(settings);
+ setPreviewSizePreference(settings);
+ setPreviewVideoEncoderPreference(settings);
+ setPreviewProfileLevelPreference(settings);
+ setPreviewJpegQuality();
+
+ setInputTypeNumber("preview_framerate");
+ setInputTypeNumber("preview_bitrate");
+ setInputTypeNumber("preview_i_frame_interval");
+ setInputTypeNumber("preview_intra_refresh");
+
+ setPreviewClipPreference("preview_clip_left");
+ setPreviewClipPreference("preview_clip_top");
+ setPreviewClipPreference("preview_clip_right");
+ setPreviewClipPreference("preview_clip_bottom");
+ setPreviewCutOutReset();
+ }
+
+ /**
+ * 静止画の解像度の Preference を作成します.
+ *
+ * @param settings レコーダの設定
+ */
+ private void setPictureSizePreference(MediaRecorder.Settings settings) {
+ ListPreference pref = findPreference("camera_picture_size");
+ if (pref != null) {
+ List pictureSizes = getSupportedPictureSizes(settings);
+ if (!pictureSizes.isEmpty()) {
+ List entryValues = new ArrayList<>();
+ for (Size preview : pictureSizes) {
+ entryValues.add(getValueFromSize(preview));
+ }
+ pref.setEntries(entryValues.toArray(new String[0]));
+ pref.setEntryValues(entryValues.toArray(new String[0]));
+ pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener);
+
+ Size pictureSize = settings.getPictureSize();
+ if (pictureSize != null) {
+ pref.setValue(getValueFromSize(pictureSize));
+ }
+ pref.setVisible(true);
+ } else {
+ pref.setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * プレビューの解像度 Preference を作成します.
+ *
+ * @param settings レコーダの設定
+ */
+ private void setPreviewSizePreference(MediaRecorder.Settings settings) {
+ ListPreference pref = findPreference("camera_preview_size");
+ if (pref != null) {
+ List previewSizes = getSupportedPreviewSizes(settings);
+ if (!previewSizes.isEmpty()) {
+ List entryValues = new ArrayList<>();
+ for (Size preview : previewSizes) {
+ entryValues.add(getValueFromSize(preview));
+ }
+
+ pref.setEntries(entryValues.toArray(new String[0]));
+ pref.setEntryValues(entryValues.toArray(new String[0]));
+ pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener);
+
+ Size previewSize = settings.getPreviewSize();
+ if (previewSize != null) {
+ pref.setValue(getValueFromSize(previewSize));
+ }
+ pref.setVisible(true);
+ } else {
+ pref.setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * エンコーダの設定を行います.
+ *
+ * @param settings レコーダ設定
+ */
+ private void setPreviewVideoEncoderPreference(MediaRecorder.Settings settings) {
+ ListPreference pref = findPreference("preview_encoder");
+ if (pref != null) {
+ List list = settings.getSupportedVideoEncoders();
+ if (!list.isEmpty()) {
+ List entryValues = new ArrayList<>(list);
+ pref.setEntries(entryValues.toArray(new String[0]));
+ pref.setEntryValues(entryValues.toArray(new String[0]));
+ pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener);
+ pref.setVisible(true);
+ } else {
+ pref.setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * エンコーダのプロファイルとレベルを設定します.
+ *
+ * @param settings レコーダ設定
+ */
+ private void setPreviewProfileLevelPreference(MediaRecorder.Settings settings) {
+ setPreviewProfileLevelPreference(settings, settings.getPreviewEncoderName(), false);
+ }
+
+ /**
+ * エンコーダのプロファイルとレベルを設定します.
+ *
+ * @param settings レコーダ設定
+ * @param encoderName エンコーダ
+ * @param reset リセットフラグ
+ */
+ private void setPreviewProfileLevelPreference(MediaRecorder.Settings settings, MediaRecorder.VideoEncoderName encoderName, boolean reset) {
+ ListPreference pref = findPreference("preview_profile_level");
+ if (pref != null) {
+ List list = CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType());
+ if (!list.isEmpty()) {
+ List entryValues = new ArrayList<>();
+ entryValues.add("none");
+
+ for (MediaRecorder.ProfileLevel pl : list) {
+ String value = getProfileLevel(encoderName, pl);
+ if (value != null) {
+ entryValues.add(value);
+ }
+ }
+
+ pref.setEntries(entryValues.toArray(new String[0]));
+ pref.setEntryValues(entryValues.toArray(new String[0]));
+ pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener);
+
+ if (reset) {
+ pref.setValue("none");
+ } else {
+ MediaRecorder.ProfileLevel pl = settings.getProfileLevel();
+ if (pl != null) {
+ pref.setValue(getProfileLevel(encoderName, pl));
+ }
+ }
+
+ pref.setVisible(true);
+ } else {
+ pref.setEnabled(false);
+ }
+ }
+ }
+
+ /**
+ * JPEG クオリティを設定します.
+ */
+ private void setPreviewJpegQuality() {
+ SeekBarDialogPreference pref = findPreference("preview_jpeg_quality");
+ if (pref != null) {
+ pref.setMinValue(0);
+ pref.setMaxValue(100);
+ pref.setEnabled(true);
+ }
+ }
+
+ /**
+ * 切り抜き範囲の設定にリスナーを設定します.
+ *
+ * @param key キー
+ */
+ public void setPreviewClipPreference(String key) {
+ EditTextPreference pref = findPreference(key);
+ if (pref != null) {
+ pref.setOnPreferenceChangeListener(mOnPreferenceChangeListener);
+ setInputTypeNumber(key);
+ }
+ }
+
+ /**
+ * EditText を未設定に戻します.
+ *
+ * @param key キー
+ */
+ private void resetEditText(String key) {
+ EditTextPreference pref = findPreference(key);
+ if (pref != null) {
+ pref.setText(null);
+ }
+ }
+
+ /**
+ * 切り抜き範囲のリセットボタンのリスナーを設定します.
+ */
+ private void setPreviewCutOutReset() {
+ PreferenceScreen pref = findPreference("preview_clip_reset");
+ if (pref != null) {
+ pref.setOnPreferenceClickListener(preference -> {
+ resetEditText("preview_clip_left");
+ resetEditText("preview_clip_top");
+ resetEditText("preview_clip_right");
+ resetEditText("preview_clip_bottom");
+ mMediaRecorder.getSettings().setDrawingRange(null);
+ return false;
+ });
+ }
+ }
+ /**
+ * サイズの小さい方からソートを行うための比較演算子.
+ */
+ private static final Comparator SIZE_COMPARATOR = (lhs, rhs) -> {
+ // We cast here to ensure the multiplications won't overflow
+ return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
+ (long) rhs.getWidth() * rhs.getHeight());
+ };
+
+ /**
+ * カメラID に対応したカメラデバイスがサポートしている写真サイズのリストを取得します.
+ *
+ * @param settings レコーダ
+ * @return サポートしているプレビューサイズのリスト
+ */
+ @NonNull
+ private static List getSupportedPictureSizes(MediaRecorder.Settings settings) {
+ List previewSizes = new ArrayList<>();
+ if (settings != null) {
+ previewSizes.addAll(settings.getSupportedPictureSizes());
+ Collections.sort(previewSizes, SIZE_COMPARATOR);
+ }
+ return previewSizes;
+ }
+
+ /**
+ * カメラID に対応したカメラデバイスがサポートしているプレビューサイズのリストを取得します.
+ *
+ * @param settings レコーダ
+ * @return サポートしているプレビューサイズのリスト
+ */
+ @NonNull
+ private static List getSupportedPreviewSizes(MediaRecorder.Settings settings) {
+ List previewSizes = new ArrayList<>();
+ if (settings != null) {
+ previewSizes.addAll(settings.getSupportedPreviewSizes());
+ Collections.sort(previewSizes, SIZE_COMPARATOR);
+ }
+ return previewSizes;
+ }
+
+ /**
+ * プレビューのサイズを文字列に変換します.
+ *
+ * @param previewSize プレビューサイズ
+ * @return 文字列
+ */
+ private String getValueFromSize(Size previewSize) {
+ return previewSize.getWidth() + " x " + previewSize.getHeight();
+ }
+
+ /**
+ * 文字列を Size に変換します.
+ *
+ * Size に変換できなかった場合には null を返却します。
+ *
+ * @param value 文字列のサイズ
+ * @return サイズ
+ */
+ private Size getSizeFromValue(String value) {
+ String[] t = value.split("x");
+ if (t.length == 2) {
+ try {
+ int w = Integer.parseInt(t[0].trim());
+ int h = Integer.parseInt(t[1].trim());
+ return new Size(w, h);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * プロファイルとレベルを文字列に変換します.
+ *
+ * @param encoderName エンコーダ
+ * @param pl プロファイルとレベル
+ * @return 文字列
+ */
+ private String getProfileLevel(MediaRecorder.VideoEncoderName encoderName, MediaRecorder.ProfileLevel pl) {
+ switch (encoderName) {
+ case H264: {
+ H264Profile p = H264Profile.valueOf(pl.getProfile());
+ H264Level l = H264Level.valueOf(pl.getLevel());
+ if (p != null && l != null) {
+ return p.getName() + " - " + l.getName();
+ }
+ }
+ case H265: {
+ H265Profile p = H265Profile.valueOf(pl.getProfile());
+ H265Level l = H265Level.valueOf(pl.getLevel());
+ if (p != null && l != null) {
+ return p.getName() + " - " + l.getName();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 文字列をプロファイルとレベルに変換します.
+ *
+ * プロファイルとレベルに変換できなかった場合には、null を返却します。
+ *
+ * @param encoderName エンコーダ
+ * @param value 変換する文字列
+ * @return プロファイルとレベル
+ */
+ private MediaRecorder.ProfileLevel getProfileLevel(MediaRecorder.VideoEncoderName encoderName, String value) {
+ String[] t = value.split("-");
+ if (t.length == 2) {
+ try {
+ String profile = t[0].trim();
+ String level = t[1].trim();
+ switch (encoderName) {
+ case H264: {
+ H264Profile p = H264Profile.nameOf(profile);
+ H264Level l = H264Level.nameOf(level);
+ if (p != null && l != null) {
+ return new MediaRecorder.ProfileLevel(p.getValue(), l.getValue());
+ }
+ }
+ case H265: {
+ H265Profile p = H265Profile.nameOf(profile);
+ H265Level l = H265Level.nameOf(level);
+ if (p != null && l != null) {
+ return new MediaRecorder.ProfileLevel(p.getValue(), l.getValue());
+ }
+ }
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 切り抜き範囲の値を取得します.
+ *
+ * 未設定の場合には null を返却します。
+ *
+ * @param key キー
+ * @return 切り抜き範囲
+ */
+ private Integer getDrawingRange(String key) {
+ EditTextPreference pref = findPreference(key);
+ if (pref != null) {
+ try {
+ return Integer.parseInt(pref.getText());
+ } catch (NumberFormatException e) {
+ // ignore.
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 設定が変更された時に呼び出されるリスナー.
+ */
+ private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener = (preference, newValue) -> {
+ if (mMediaRecorder == null) {
+ return false;
+ }
+
+ MediaRecorder.Settings settings = mMediaRecorder.getSettings();
+
+ String key = preference.getKey();
+ if ("camera_picture_size".equals(key)) {
+ Size size = getSizeFromValue((String) newValue);
+ if (size != null) {
+ settings.setPictureSize(size);
+ }
+ } else if ("camera_preview_size".equals(key)) {
+ Size size = getSizeFromValue((String) newValue);
+ if (size != null) {
+ settings.setPreviewSize(size);
+ }
+ } else if ("preview_encoder".equals(key)) {
+ // エンコーダが切り替えられたので、プロファイル・レベルは一旦削除しておく
+ settings.setProfileLevel(null);
+ MediaRecorder.VideoEncoderName encoderName =
+ MediaRecorder.VideoEncoderName.nameOf((String) newValue);
+ setPreviewProfileLevelPreference(settings, encoderName, true);
+ } else if ("preview_profile_level".equalsIgnoreCase(key)) {
+ settings.setProfileLevel(getProfileLevel(settings.getPreviewEncoderName(), (String) newValue));
+ } else if ("preview_clip_left".equalsIgnoreCase(key)) {
+ try {
+ int clipLeft = Integer.parseInt((String) newValue);
+ Integer clipRight = getDrawingRange("preview_clip_right");
+ if (clipRight != null && clipRight <= clipLeft) {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ } else if ("preview_clip_top".equalsIgnoreCase(key)) {
+ try {
+ int clipTop = Integer.parseInt((String) newValue);
+ Integer clipBottom = getDrawingRange("preview_clip_bottom");
+ if (clipBottom != null && clipBottom <= clipTop) {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ } else if ("preview_clip_right".equalsIgnoreCase(key)) {
+ try {
+ int clipRight = Integer.parseInt((String) newValue);
+ Integer clipLeft = getDrawingRange("preview_clip_left");
+ if (clipLeft != null && clipRight <= clipLeft) {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ } else if ("preview_clip_bottom".equalsIgnoreCase(key)) {
+ try {
+ int clipBottom = Integer.parseInt((String) newValue);
+ Integer clipTop = getDrawingRange("preview_clip_top");
+ if (clipTop != null && clipBottom <= clipTop) {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ return true;
+ };
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/ErrorDialogFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/ErrorDialogFragment.java
deleted file mode 100644
index 93134e3a84..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/ErrorDialogFragment.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- ErrorDialogFragment
- Copyright (c) 2015 NTT DOCOMO,INC.
- Released under the MIT license
- http://opensource.org/licenses/mit-license.php
- */
-package org.deviceconnect.android.deviceplugin.uvc.fragment.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-
-import androidx.fragment.app.DialogFragment;
-
-import org.deviceconnect.android.deviceplugin.uvc.R;
-
-/**
- * This fragment displays a dialog of error.
- * @author NTT DOCOMO, INC.
- */
-public class ErrorDialogFragment extends DialogFragment {
- private static final String PARAM_TITLE = "title";
- private static final String PARAM_MESSAGE = "message";
- private AlertDialog mDialog;
- private DialogInterface.OnDismissListener mListener;
-
- public static ErrorDialogFragment newInstance(final String title, final String message) {
- ErrorDialogFragment instance = new ErrorDialogFragment();
-
- Bundle arguments = new Bundle();
- arguments.putString(PARAM_TITLE, title);
- arguments.putString(PARAM_MESSAGE, message);
-
- instance.setArguments(arguments);
-
- return instance;
- }
-
- @Override
- public Dialog onCreateDialog(final Bundle savedInstanceState) {
- if (mDialog != null) {
- return mDialog;
- }
-
- String title = getArguments().getString(PARAM_TITLE);
- String message = getArguments().getString(PARAM_MESSAGE);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setTitle(title);
- builder.setMessage(message);
- builder.setPositiveButton(R.string.uvc_settings_dialog_positive,
- (dialog, which) -> {
- dismiss();
- });
- mDialog = builder.create();
- return mDialog;
- }
-
- @Override
- public Dialog getDialog() {
- return mDialog;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mDialog = null;
- }
-
- @Override
- public void onDismiss(final DialogInterface dialog) {
- super.onDismiss(dialog);
- if (mListener != null) {
- mListener.onDismiss(dialog);
- }
- }
-
- public void setOnDismissListener(final DialogInterface.OnDismissListener listener) {
- mListener = listener;
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/PermissionErrorDialogFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/PermissionErrorDialogFragment.java
new file mode 100644
index 0000000000..c298db7c18
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/PermissionErrorDialogFragment.java
@@ -0,0 +1,27 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment.dialog;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.os.Bundle;
+
+import androidx.fragment.app.DialogFragment;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+public class PermissionErrorDialogFragment extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
+ builder.setTitle(R.string.uvc_error_permission_title);
+ builder.setMessage(R.string.uvc_error_permission_message);
+ builder.setPositiveButton(R.string.uvc_error_positive, (dialog, which) -> {
+ Activity a = getActivity();
+ if (a != null) {
+ a.finish();
+ }
+ });
+ setCancelable(false);
+ return builder.create();
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/ProgressDialogFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/ProgressDialogFragment.java
deleted file mode 100644
index c6ea17e4d5..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/dialog/ProgressDialogFragment.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- ProgressDialogFragment
- Copyright (c) 2015 NTT DOCOMO,INC.
- Released under the MIT license
- http://opensource.org/licenses/mit-license.php
- */
-package org.deviceconnect.android.deviceplugin.uvc.fragment.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.fragment.app.DialogFragment;
-
-import org.deviceconnect.android.deviceplugin.uvc.R;
-
-/**
- * This fragment displays a dialog of Progress.
- * @author NTT DOCOMO, INC.
- */
-public class ProgressDialogFragment extends DialogFragment {
- private static final String PARAM_TITLE = "title";
- private static final String PARAM_MESSAGE = "message";
- public static ProgressDialogFragment newInstance(final String title, final String message) {
- ProgressDialogFragment instance = new ProgressDialogFragment();
-
- Bundle arguments = new Bundle();
- arguments.putString(PARAM_TITLE, title);
- arguments.putString(PARAM_MESSAGE, message);
-
- instance.setArguments(arguments);
-
- return instance;
- }
-
- @Override
- public Dialog onCreateDialog(final Bundle savedInstanceState) {
- String title = getArguments().getString(PARAM_TITLE);
- String message = getArguments().getString(PARAM_MESSAGE);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- LayoutInflater inflater = getActivity().getLayoutInflater();
- View v = inflater.inflate(R.layout.dialog_progress, null);
- TextView titleView = v.findViewById(R.id.title);
- TextView messageView = v.findViewById(R.id.message);
- titleView.setText(title);
- messageView.setText(message);
- builder.setView(v);
-
- return builder.create();
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/preference/SeekBarDialogFragment.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/preference/SeekBarDialogFragment.java
new file mode 100644
index 0000000000..142c0e9073
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/preference/SeekBarDialogFragment.java
@@ -0,0 +1,74 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment.preference;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.SeekBar;
+
+import androidx.preference.EditTextPreference;
+import androidx.preference.PreferenceDialogFragmentCompat;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+public class SeekBarDialogFragment extends PreferenceDialogFragmentCompat {
+ private static final int MAX = 1000;
+ private EditText mValueEditText;
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ SeekBarDialogPreference pref = (SeekBarDialogPreference) getPreference();
+ int min = pref.getMinValue();
+ int max = pref.getMaxValue();
+ long value = min;
+ try {
+ value = Long.parseLong(pref.getText());
+ } catch (Exception e) {
+ // ignore.
+ }
+
+ mValueEditText = view.findViewById(R.id.number_edit_text);
+ if (mValueEditText != null) {
+ mValueEditText.setText(String.valueOf(value));
+ }
+
+ SeekBar seekBar = view.findViewById(R.id.seekbar);
+ if (seekBar != null) {
+ seekBar.setMax(MAX);
+ seekBar.setProgress((int) (MAX * ((value - min) / (float) (max - min))));
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ long value = min + (long) ((max - min) * (progress / (float) MAX));
+ mValueEditText.setText(String.valueOf(value));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ EditTextPreference f = (EditTextPreference) getPreference();
+ if (positiveResult) {
+ f.setText(mValueEditText.getText().toString());
+ }
+ }
+
+ public static SeekBarDialogFragment newInstance(String key) {
+ Bundle bundle = new Bundle();
+ bundle.putString(ARG_KEY, key);
+
+ SeekBarDialogFragment fragment = new SeekBarDialogFragment();
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/preference/SeekBarDialogPreference.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/preference/SeekBarDialogPreference.java
new file mode 100644
index 0000000000..f6f41d0557
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/fragment/preference/SeekBarDialogPreference.java
@@ -0,0 +1,50 @@
+package org.deviceconnect.android.deviceplugin.uvc.fragment.preference;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.preference.EditTextPreference;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+public class SeekBarDialogPreference extends EditTextPreference {
+ private int mMinValue;
+ private int mMaxValue;
+
+ public SeekBarDialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public SeekBarDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public SeekBarDialogPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SeekBarDialogPreference(Context context) {
+ super(context);
+ }
+
+ @Override
+ public int getDialogLayoutResource() {
+ return R.layout.dialog_preference_seek_bar;
+ }
+
+ public void setMinValue(int minValue) {
+ mMinValue = minValue;
+ }
+
+ public int getMinValue() {
+ return mMinValue;
+ }
+
+ public void setMaxValue(int maxValue) {
+ mMaxValue = maxValue;
+ }
+
+ public int getMaxValue() {
+ return mMaxValue;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCMediaStreamRecordingProfile.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCMediaStreamRecordingProfile.java
index 2bc05f8e56..49e525d955 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCMediaStreamRecordingProfile.java
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCMediaStreamRecordingProfile.java
@@ -6,19 +6,24 @@
*/
package org.deviceconnect.android.deviceplugin.uvc.profile;
-
import android.content.Intent;
+import android.graphics.Rect;
import android.os.Bundle;
-import android.util.Log;
+import android.util.Size;
-import org.deviceconnect.android.deviceplugin.uvc.UVCDeviceService;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H264Level;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H264Profile;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H265Level;
+import org.deviceconnect.android.deviceplugin.uvc.profile.utils.H265Profile;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.Broadcaster;
import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
-import org.deviceconnect.android.deviceplugin.uvc.recorder.UVCRecorder;
-import org.deviceconnect.android.deviceplugin.uvc.recorder.preview.PreviewServer;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorderManager;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.PreviewServer;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
import org.deviceconnect.android.deviceplugin.uvc.service.UVCService;
+import org.deviceconnect.android.deviceplugin.uvc.util.CapabilityUtil;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.MediaStreamRecordingProfile;
-import org.deviceconnect.android.profile.api.DConnectApi;
import org.deviceconnect.android.profile.api.DeleteApi;
import org.deviceconnect.android.profile.api.GetApi;
import org.deviceconnect.android.profile.api.PutApi;
@@ -26,10 +31,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import javax.net.ssl.SSLContext;
/**
* UVC MediaStream Recording Profile.
@@ -37,287 +38,708 @@
* @author NTT DOCOMO, INC.
*/
public class UVCMediaStreamRecordingProfile extends MediaStreamRecordingProfile {
- private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
- private final DConnectApi mGetMediaRecorderApi = new GetApi() {
+ public UVCMediaStreamRecordingProfile() {
+ // GET /gotapi/mediaStreamRecording/mediaRecorder
+ addApi(new GetApi() {
+ @Override
+ public String getAttribute() {
+ return ATTRIBUTE_MEDIARECORDER;
+ }
- @Override
- public String getAttribute() {
- return ATTRIBUTE_MEDIARECORDER;
- }
+ @Override
+ public boolean onRequest(final Intent request, final Intent response) {
+ UVCService service = (UVCService) getService();
- @Override
- public boolean onRequest(final Intent request, final Intent response) {
- mExecutor.execute(() -> {
- try {
- UVCRecorder recorder = getUVCRecorder();
- if (recorder == null) {
- MessageUtils.setNotFoundServiceError(response);
- return;
- }
+ if (service == null) {
+ MessageUtils.setNotFoundServiceError(response, "service is not found.");
+ return true;
+ }
- if (!getService().isOnline()) {
- MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
- return;
- }
+ if (!getService().isOnline()) {
+ MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
+ return true;
+ }
+
+ setResult(response, DConnectMessage.RESULT_OK);
+ setMediaRecorders(response, service.getUvcRecorderList());
+ return true;
+ }
+ });
+
+ // GET /gotapi/mediaStreamRecording/options
+ addApi(new GetApi() {
+ @Override
+ public String getAttribute() {
+ return ATTRIBUTE_OPTIONS;
+ }
- setMediaRecorders(response, recorder);
- setResult(response, DConnectMessage.RESULT_OK);
- } finally {
- sendResponse(response);
+ @Override
+ public boolean onRequest(final Intent request, final Intent response) {
+ String target = getTarget(request);
+
+ UVCService service = (UVCService) getService();
+ if (service == null) {
+ MessageUtils.setNotFoundServiceError(response, "service is not found.");
+ return true;
}
- });
- return false;
- }
- };
- private final DConnectApi mGetOptionsApi = new GetApi() {
+ if (!service.isOnline()) {
+ MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
+ return true;
+ }
- @Override
- public String getAttribute() {
- return ATTRIBUTE_OPTIONS;
- }
+ UvcRecorder recorder = getUvcRecorderByTarget(target);
+ if (recorder == null) {
+ MessageUtils.setNotFoundServiceError(response, target + " is not found.");
+ return true;
+ }
- @Override
- public boolean onRequest(final Intent request, final Intent response) {
- mExecutor.execute(() -> {
- try {
- if (!getService().isOnline()) {
- MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
- return;
+ setResult(response, DConnectMessage.RESULT_OK);
+ setOptions(response, recorder);
+ return true;
+ }
+ });
+
+ // PUT /gotapi/mediaStreamRecording/options
+ addApi(new PutApi() {
+ @Override
+ public String getAttribute() {
+ return ATTRIBUTE_OPTIONS;
+ }
+
+ @Override
+ public boolean onRequest(final Intent request, final Intent response) {
+ String target = getTarget(request);
+ Integer imageWidth = getImageWidth(request);
+ Integer imageHeight = getImageHeight(request);
+ Integer previewWidth = getPreviewWidth(request);
+ Integer previewHeight = getPreviewHeight(request);
+ Double previewMaxFrameRate = getPreviewMaxFrameRate(request);
+ Integer previewBitRate = parseInteger(request, "previewBitRate");
+ Integer previewKeyFrameInterval = parseInteger(request, "previewKeyFrameInterval");
+ String previewEncoder = request.getStringExtra("previewEncoder");
+ String previewProfile = request.getStringExtra("previewProfile");
+ String previewLevel = request.getStringExtra("previewLevel");
+ Integer previewIntraRefresh = parseInteger("previewIntraRefresh");
+ Double previewJpegQuality = parseDouble(request, "previewJpegQuality");
+ Integer previewClipLeft = parseInteger(request, "previewClipLeft");
+ Integer previewClipTop = parseInteger(request, "previewClipTop");
+ Integer previewClipRight = parseInteger(request, "previewClipRight");
+ Integer previewClipBottom = parseInteger(request, "previewClipBottom");
+ Boolean previewClipReset = parseBoolean(request, "previewClipReset");
+ MediaRecorder.ProfileLevel profileLevel = null;
+ Rect drawingRect = null;
+
+ UVCService service = (UVCService) getService();
+ if (service == null) {
+ MessageUtils.setNotFoundServiceError(response, "service is not found.");
+ return true;
+ }
+
+ if (!service.isOnline()) {
+ MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
+ return true;
+ }
+
+ UvcRecorder recorder = getUvcRecorderByTarget(target);
+ if (recorder == null) {
+ MessageUtils.setNotFoundServiceError(response, target + " is not found.");
+ return true;
+ }
+
+ MediaRecorder.Settings settings = recorder.getSettings();
+
+ // 値の妥当性チェック
+
+ if (imageWidth == null && imageHeight != null ||
+ imageWidth != null && imageHeight == null) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "imageWidth or imageHeight is not set.");
+ return true;
+ }
+
+ if (previewWidth == null && previewHeight != null ||
+ previewWidth != null && previewHeight == null) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewWidth or previewHeight is not set.");
+ return true;
+ }
+
+ if (imageWidth != null && !settings.isSupportedPictureSize(imageWidth, imageHeight)) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "imageWidth or imageHeight is not support value.");
+ return true;
+ }
+
+ if (previewWidth != null && !settings.isSupportedPreviewSize(previewWidth, previewHeight)) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewWidth or previewHeight is not support value.");
+ return true;
+ }
+
+ if (previewEncoder != null && !settings.isSupportedVideoEncoder(previewEncoder)) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "Unsupported preview encoder: " + previewEncoder);
+ return true;
+ }
+
+ if (previewProfile != null || previewLevel != null) {
+ if (previewProfile == null) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewProfile is not set.");
+ return true;
}
- UVCRecorder recorder = getUVCRecorder();
- if (recorder == null) {
- MessageUtils.setNotFoundServiceError(response);
- return;
+ if (previewLevel == null) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewLevel is not set.");
+ return true;
}
- setOptions(response, recorder);
- setResult(response, DConnectMessage.RESULT_OK);
- } finally {
- sendResponse(response);
+ MediaRecorder.VideoEncoderName encoderName = settings.getPreviewEncoderName();
+ if (previewEncoder != null) {
+ encoderName = MediaRecorder.VideoEncoderName.nameOf(previewEncoder);
+ }
+ switch (encoderName) {
+ case H264: {
+ H264Profile p = H264Profile.nameOf(previewProfile);
+ H264Level l = H264Level.nameOf(previewLevel);
+ if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel);
+ return true;
+ }
+ profileLevel = new MediaRecorder.ProfileLevel(p.getValue(), l.getValue());
+ } break;
+ case H265: {
+ H265Profile p = H265Profile.nameOf(previewProfile);
+ H265Level l = H265Level.nameOf(previewLevel);
+ if (p == null || l == null || !settings.isSupportedProfileLevel(p.getValue(), l.getValue())) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "Unsupported preview profile and level: " + previewProfile + " - " + previewLevel);
+ return true;
+ }
+ profileLevel = new MediaRecorder.ProfileLevel(p.getValue(), l.getValue());
+ } break;
+ }
}
- });
- return false;
- }
- };
- private final DConnectApi mPutOptionsApi = new PutApi() {
+ if (previewJpegQuality != null && (previewJpegQuality < 0.0 || previewJpegQuality > 1.0)) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewJpegQuality is invalid. value=" + previewJpegQuality);
+ return true;
+ }
- @Override
- public String getAttribute() {
- return ATTRIBUTE_OPTIONS;
- }
+ if (previewClipLeft != null || previewClipTop != null
+ || previewClipRight != null || previewClipBottom != null) {
- @Override
- public boolean onRequest(final Intent request, final Intent response) {
- mExecutor.execute(() -> {
- try {
- Integer imageWidth = getImageWidth(request);
- Integer imageHeight = getImageHeight(request);
- Integer previewWidth = getPreviewWidth(request);
- Integer previewHeight = getPreviewHeight(request);
- Double previewMaxFrameRate = getPreviewMaxFrameRate(request);
-
- UVCRecorder recorder = getUVCRecorder();
- if (recorder == null) {
- MessageUtils.setNotFoundServiceError(response);
- return;
+ if (previewClipLeft == null) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewClipLeft is not set.");
+ return true;
}
- if (!getService().isOnline()) {
- MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
- return;
+ if (previewClipTop == null) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewClipTop is not set.");
+ return true;
}
- if (imageWidth == null && imageHeight != null ||
- imageWidth != null && imageHeight == null) {
+ if (previewClipRight == null) {
MessageUtils.setInvalidRequestParameterError(response,
- "imageWidth or imageHeight is not set.");
- return;
+ "previewClipRight is not set.");
+ return true;
}
- if (previewWidth == null && previewHeight != null ||
- previewWidth != null && previewHeight == null) {
+ if (previewClipBottom == null) {
MessageUtils.setInvalidRequestParameterError(response,
- "previewWidth or previewHeight is not set.");
- return;
+ "previewClipBottom is not set.");
+ return true;
}
- if (imageWidth != null && imageHeight != null) {
- recorder.setPictureSize(new MediaRecorder.Size(imageWidth, imageHeight));
+ if (previewClipLeft < 0) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewClipLeft cannot set a negative value.");
+ return true;
}
- if (previewWidth != null && previewHeight != null) {
- recorder.setPreviewSize(new MediaRecorder.Size(previewWidth, previewHeight));
+ if (previewClipBottom < 0) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewClipBottom cannot set a negative value.");
+ return true;
}
- if (previewMaxFrameRate != null) {
- recorder.setMaxFrameRate(previewMaxFrameRate);
+ if (previewClipLeft >= previewClipRight) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewClipLeft is larger than previewClipRight.");
+ return true;
}
-
- setResult(response, DConnectMessage.RESULT_OK);
- } finally {
- sendResponse(response);
+
+ if (previewClipTop >= previewClipBottom) {
+ MessageUtils.setInvalidRequestParameterError(response,
+ "previewClipTop is larger than previewClipBottom.");
+ return true;
+ }
+
+ drawingRect = new Rect(previewClipLeft, previewClipTop, previewClipRight, previewClipBottom);
}
- });
- return false;
- }
- };
- private final DConnectApi mPutPreviewApi = new PutApi() {
+ // 設定
- @Override
- public String getAttribute() {
- return ATTRIBUTE_PREVIEW;
- }
+ if (imageWidth != null) {
+ settings.setPictureSize(new Size(imageWidth, imageHeight));
+ }
+
+ if (previewWidth != null) {
+ settings.setPreviewSize(new Size(previewWidth, previewHeight));
+ }
+
+ if (previewMaxFrameRate != null) {
+ settings.setPreviewMaxFrameRate(previewMaxFrameRate.intValue());
+ }
+
+ if (previewBitRate != null) {
+ settings.setPreviewBitRate(previewBitRate * 1024);
+ }
+
+ if (previewKeyFrameInterval != null) {
+ settings.setPreviewKeyFrameInterval(previewKeyFrameInterval);
+ }
+
+ if (previewEncoder != null) {
+ settings.setPreviewEncoder(previewEncoder);
+ // エンコーダが切り替えられた場合は、プロファイル・レベルは設定無しにする
+ settings.setProfileLevel(null);
+ }
+
+ if (profileLevel != null) {
+ settings.setProfileLevel(profileLevel);
+ }
+
+ if (previewIntraRefresh != null) {
+ settings.setIntraRefresh(previewIntraRefresh);
+ }
+
+ if (previewJpegQuality != null) {
+ settings.setPreviewQuality((int) (previewJpegQuality * 100));
+ }
+
+ if (previewClipReset != null && previewClipReset) {
+ settings.setDrawingRange(null);
+ } else if (drawingRect != null) {
+ settings.setDrawingRange(drawingRect);
+ }
- @Override
- public boolean onRequest(final Intent request, final Intent response) {
- mExecutor.execute(() -> {
try {
- if (!getService().isOnline()) {
- MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
- return;
+ recorder.onConfigChange();
+ } catch (Exception e) {
+ MessageUtils.setIllegalDeviceStateError(response, "Failed to change a config.");
+ return true;
+ }
+
+ setResult(response, DConnectMessage.RESULT_OK);
+ return true;
+ }
+ });
+
+ // PUT /gotapi/mediaStreamRecording/preview
+ addApi(new PutApi() {
+ @Override
+ public String getAttribute() {
+ return ATTRIBUTE_PREVIEW;
+ }
+
+ @Override
+ public boolean onRequest(final Intent request, final Intent response) {
+ String target = getTarget(request);
+
+ UVCService service = (UVCService) getService();
+ if (service == null) {
+ MessageUtils.setNotFoundServiceError(response, "service is not found.");
+ return true;
+ }
+
+ if (!service.isOnline()) {
+ MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
+ return true;
+ }
+
+ UvcRecorder recorder = getUvcRecorderByTarget(target);
+ if (recorder == null) {
+ MessageUtils.setNotFoundServiceError(response, target + " is not found.");
+ return true;
+ }
+
+ MediaRecorderManager mrm = service.getMediaRecorderManager();
+ if (!mrm.canUseRecorder(recorder)) {
+ // 他のカメラが使用中の場合はエラーを返却
+ MessageUtils.setIllegalDeviceStateError(response, "Other recorder are being used.");
+ return true;
+ }
+
+ recorder.requestPermission(new MediaRecorder.PermissionCallback() {
+ @Override
+ public void onAllowed() {
+ List servers = recorder.startPreview();
+ if (servers.isEmpty()) {
+ MessageUtils.setIllegalDeviceStateError(response,
+ "Failed to start a preview server.");
+ } else {
+ setResult(response, DConnectMessage.RESULT_OK);
+ setUri(response, getDefaultUri(servers));
+ setStreams(response, servers);
+ }
+ sendResponse(response);
}
- UVCRecorder recorder = getUVCRecorder();
- List servers = recorder.startPreview();
-
- if (servers.isEmpty()) {
- MessageUtils.setIllegalDeviceStateError(response, "Failed to start a preview server.");
- } else {
- UVCDeviceService plugin = (UVCDeviceService) getContext();
- plugin.getSSLContext(new UVCDeviceService.SSLContextCallback() {
- @Override
- public void onGet(final SSLContext sslContext) {
- startPreviewServers(sslContext, response);
- }
- @Override
- public void onError() {
- startPreviewServers(null, response);
- }
- });
+ @Override
+ public void onDisallowed() {
+ MessageUtils.setUnknownError(response,
+ "Permission for camera is not granted.");
+ sendResponse(response);
}
- } finally {
- sendResponse(response);
+ });
+
+ return false;
+ }
+ });
+
+ // DELETE /gotapi/mediaStreamRecording/preview
+ addApi(new DeleteApi() {
+
+ @Override
+ public String getAttribute() {
+ return ATTRIBUTE_PREVIEW;
+ }
+
+ @Override
+ public boolean onRequest(final Intent request, final Intent response) {
+ String target = getTarget(request);
+
+ UVCService service = (UVCService) getService();
+ if (service == null) {
+ MessageUtils.setNotFoundServiceError(response, "service is not found.");
+ return true;
}
- });
- return false;
- }
- };
- private final DConnectApi mDeletePreviewApi = new DeleteApi() {
+ if (!service.isOnline()) {
+ MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
+ return true;
+ }
- @Override
- public String getAttribute() {
- return ATTRIBUTE_PREVIEW;
- }
+ UvcRecorder recorder = getUvcRecorderByTarget(target);
+ if (recorder == null) {
+ MessageUtils.setNotFoundServiceError(response, target + " is not found.");
+ return true;
+ }
- @Override
- public boolean onRequest(final Intent request, final Intent response) {
- mExecutor.execute(() -> {
- try {
- UVCRecorder recorder = getUVCRecorder();
- if (recorder != null) {
+ recorder.requestPermission(new MediaRecorder.PermissionCallback() {
+ @Override
+ public void onAllowed() {
recorder.stopPreview();
+ setResult(response, DConnectMessage.RESULT_OK);
+ sendResponse(response);
}
- if (!getService().isOnline()) {
- MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
- return;
+ @Override
+ public void onDisallowed() {
+ MessageUtils.setUnknownError(response,
+ "Permission for camera is not granted.");
+ sendResponse(response);
}
+ });
+ return false;
+ }
+ });
+
+ // PUT /gotapi/mediaStreamRecording/broadcast
+ addApi(new PutApi() {
+ @Override
+ public String getAttribute() {
+ return "broadcast";
+ }
- setResult(response, DConnectMessage.RESULT_OK);
- } finally {
- sendResponse(response);
+ @Override
+ public boolean onRequest(final Intent request, final Intent response) {
+ String target = getTarget(request);
+ String broadcastURI = request.getStringExtra("uri");
+
+ if (broadcastURI == null) {
+ MessageUtils.setInvalidRequestParameterError(response, "broadcastURI ");
+ return true;
}
- });
- return false;
- }
- };
- public UVCMediaStreamRecordingProfile() {
- addApi(mGetMediaRecorderApi);
- addApi(mGetOptionsApi);
- addApi(mPutOptionsApi);
- addApi(mPutPreviewApi);
- addApi(mDeletePreviewApi);
+ UVCService service = (UVCService) getService();
+ if (service == null) {
+ MessageUtils.setNotFoundServiceError(response, "service is not found.");
+ return true;
+ }
+
+ if (!service.isOnline()) {
+ MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
+ return true;
+ }
+
+ UvcRecorder recorder = getUvcRecorderByTarget(target);
+ if (recorder == null) {
+ MessageUtils.setNotFoundServiceError(response, target + " is not found.");
+ return true;
+ }
+
+ if (recorder.isBroadcasterRunning()) {
+ MessageUtils.setIllegalDeviceStateError(response, "broadcast is already running.");
+ return true;
+ }
+
+ MediaRecorderManager mrm = service.getMediaRecorderManager();
+ if (!mrm.canUseRecorder(recorder)) {
+ // 他のカメラが使用中の場合はエラーを返却
+ MessageUtils.setIllegalDeviceStateError(response, "Other recorder are being used.");
+ return true;
+ }
+
+ recorder.requestPermission(new MediaRecorder.PermissionCallback() {
+ @Override
+ public void onAllowed() {
+ // TODO 排他的に処理を行うようにします。
+ if (recorder instanceof UvcRecorder) {
+ // 使用していない場合は停止する
+ mrm.stopCameraRecorder(recorder);
+ }
+
+ Broadcaster b = recorder.startBroadcaster(broadcastURI);
+ if (b != null) {
+ setResult(response, DConnectMessage.RESULT_OK);
+ } else {
+ MessageUtils.setIllegalServerStateError(response,
+ "Failed to start a broadcast.");
+ }
+ sendResponse(response);
+ }
+
+ @Override
+ public void onDisallowed() {
+ MessageUtils.setUnknownError(response,
+ "Permission for camera is not granted.");
+ sendResponse(response);
+ }
+ });
+ return false;
+ }
+ });
+
+ // DELETE /gotapi/mediaStreamRecording/broadcast
+ addApi(new DeleteApi() {
+ @Override
+ public String getAttribute() {
+ return "broadcast";
+ }
+
+ @Override
+ public boolean onRequest(final Intent request, final Intent response) {
+ String target = getTarget(request);
+
+ UVCService service = (UVCService) getService();
+ if (service == null) {
+ MessageUtils.setNotFoundServiceError(response, "service is not found.");
+ return true;
+ }
+
+ if (!service.isOnline()) {
+ MessageUtils.setIllegalDeviceStateError(response, "device is not connected.");
+ return true;
+ }
+
+ UvcRecorder recorder = getUvcRecorderByTarget(target);
+ if (recorder == null) {
+ MessageUtils.setNotFoundServiceError(response, target + " is not found.");
+ return true;
+ }
+
+ recorder.requestPermission(new MediaRecorder.PermissionCallback() {
+ @Override
+ public void onAllowed() {
+ recorder.stopBroadcaster();
+
+ setResult(response, DConnectMessage.RESULT_OK);
+ sendResponse(response);
+ }
+
+ @Override
+ public void onDisallowed() {
+ MessageUtils.setUnknownError(response,
+ "Permission for camera is not granted.");
+ sendResponse(response);
+ }
+ });
+ return false;
+ }
+ });
}
- private UVCRecorder getUVCRecorder() {
+ private UvcRecorder getUvcRecorder() {
UVCService service = (UVCService) getService();
- return service != null ? service.getUVCRecorder() : null;
+ if (service != null) {
+ return service.getDefaultRecorder();
+ }
+ return null;
}
- private static void setMediaRecorders(final Intent response, final UVCRecorder uvcRecorder) {
+ private UvcRecorder getUvcRecorderByTarget(String target) {
+ if (target == null) {
+ return getUvcRecorder();
+ }
+
+ UVCService service = (UVCService) getService();
+ if (service != null) {
+ return service.findUvcRecorderById(target);
+ }
+ return null;
+ }
+
+ private static void setMediaRecorders(final Intent response, final List uvcRecorders) {
List recorderList = new ArrayList<>();
- Bundle recorder = new Bundle();
- setMediaRecorder(recorder, uvcRecorder);
- recorderList.add(recorder);
+ for (UvcRecorder uvcRecorder : uvcRecorders) {
+ Bundle recorder = new Bundle();
+ setMediaRecorder(recorder, uvcRecorder);
+ recorderList.add(recorder);
+ }
setRecorders(response, recorderList);
}
- private static void setMediaRecorder(final Bundle recorder, final UVCRecorder uvcRecorder) {
- MediaRecorder.Size previewSize = uvcRecorder.getPreviewSize();
-
- setRecorderId(recorder, uvcRecorder.getId());
- setRecorderName(recorder, uvcRecorder.getName());
- setRecorderState(recorder, uvcRecorder.isStartedPreview() ? RecorderState.RECORDING
- : RecorderState.INACTIVE);
- setRecorderPreviewWidth(recorder, previewSize.getWidth());
- setRecorderPreviewHeight(recorder, previewSize.getHeight());
- setRecorderPreviewMaxFrameRate(recorder, uvcRecorder.getMaxFrameRate());
- setRecorderMIMEType(recorder, uvcRecorder.getMimeType());
- setRecorderConfig(recorder, "");
+ private static void setMediaRecorder(final Bundle info, final UvcRecorder recorder) {
+ MediaRecorder.Settings settings = recorder.getSettings();
+ Size previewSize = settings.getPreviewSize();
+
+ setRecorderId(info, recorder.getId());
+ setRecorderName(info, recorder.getName());
+ setRecorderState(info, recorder.isPreviewRunning() ? RecorderState.RECORDING : RecorderState.INACTIVE);
+ setRecorderPreviewWidth(info, previewSize.getWidth());
+ setRecorderPreviewHeight(info, previewSize.getHeight());
+ setRecorderPreviewMaxFrameRate(info, settings.getPreviewMaxFrameRate());
+ setRecorderMIMEType(info, recorder.getMimeType());
+ setRecorderConfig(info, "");
+ info.putInt("previewBitRate", settings.getPreviewBitRate() / 1024);
+ info.putInt("previewKeyFrameInterval", settings.getPreviewKeyFrameInterval());
+ info.putString("previewEncoder", settings.getPreviewEncoder());
+ info.putString("previewEncoder", settings.getPreviewEncoder());
+ info.putFloat("previewJpegQuality", settings.getPreviewQuality() / 100.0f);
+ MediaRecorder.ProfileLevel pl = settings.getProfileLevel();
+ if (pl != null) {
+ switch (MediaRecorder.VideoEncoderName.nameOf(settings.getPreviewEncoder())) {
+ case H264:
+ info.putString("previewProfile", H264Profile.valueOf(pl.getProfile()).getName());
+ info.putString("previewLevel", H264Level.valueOf(pl.getLevel()).getName());
+ break;
+ case H265:
+ info.putString("previewProfile", H265Profile.valueOf(pl.getProfile()).getName());
+ info.putString("previewLevel", H265Level.valueOf(pl.getLevel()).getName());
+ break;
+ }
+ }
+ Bundle status = new Bundle();
+ status.putBoolean("preview", recorder.isPreviewRunning());
+ status.putBoolean("broadcast", recorder.isBroadcasterRunning());
+// status.putBoolean("recording", recorder.getState() == MediaRecorder.State.RECORDING);
+ info.putParcelable("status", status);
+
+ // 切り抜き設定
+ Rect rect = settings.getDrawingRange();
+ if (rect != null) {
+ Bundle drawingRect = new Bundle();
+ drawingRect.putInt("left", rect.left);
+ drawingRect.putInt("top", rect.top);
+ drawingRect.putInt("right", rect.right);
+ drawingRect.putInt("bottom", rect.bottom);
+ info.putBundle("previewClip", drawingRect);
+ }
}
- private static void setOptions(final Intent response, final UVCRecorder recorder) {
- List options = recorder.getSupportedPreviewSizes();
+ private static void setOptions(final Intent response, final UvcRecorder recorder) {
+ List options = recorder.getSettings().getSupportedPreviewSizes();
List previewSizes = new ArrayList<>();
- for (MediaRecorder.Size option : options) {
+ for (Size option : options) {
Bundle size = new Bundle();
setWidth(size, option.getWidth());
setHeight(size, option.getHeight());
previewSizes.add(size);
}
setPreviewSizes(response, previewSizes);
- setMIMEType(response, recorder.getSupportedMimeTypes());
+ setMIMEType(response, recorder.getServerProvider().getSupportedMimeType());
+ List encoders = new ArrayList<>();
+ for (String name : recorder.getSettings().getSupportedVideoEncoders()) {
+ MediaRecorder.VideoEncoderName encoderName = MediaRecorder.VideoEncoderName.nameOf(name);
+ Bundle encoder = new Bundle();
+ encoder.putString("name", name);
+ encoder.putParcelableArray("profileLevel", getProfileLevels(encoderName));
+ encoders.add(encoder);
+ }
+ response.putExtra("encoder", encoders.toArray(new Bundle[0]));
}
private static void setMIMEType(final Intent response, final List mimeTypes) {
response.putExtra("mimeType", mimeTypes.toArray(new String[0]));
}
- private void startPreviewServers(final SSLContext sslContext,
- final Intent response) {
- // SSLContext を設定
- for (PreviewServer server : getUVCRecorder().getServers()) {
- if (sslContext != null && server.usesSSLContext()) {
- server.setSSLContext(sslContext);
+ private static String getDefaultUri(List servers) {
+ String defaultUri = null;
+ for (PreviewServer server : servers) {
+ // Motion-JPEG をデフォルトの値として使用します
+ if (defaultUri == null && "video/x-mjpeg".equals(server.getMimeType())) {
+ defaultUri = server.getUri();
}
}
- String defaultUri = null;
+ return defaultUri != null ? defaultUri : "";
+ }
+
+ private static void setStreams(Intent response, List servers) {
+ response.putExtra("streams", createStreams(servers));
+ }
+
+ private static Bundle[] createStreams(List servers) {
List streams = new ArrayList<>();
- List servers = getUVCRecorder().startPreview();
- if (servers.isEmpty()) {
- MessageUtils.setIllegalServerStateError(response, "Failed to start web server.");
- } else {
- for (PreviewServer server : servers) {
- // Motion-JPEG をデフォルトの値として使用します
- if (defaultUri == null && "video/x-mjpeg".equals(server.getMimeType())) {
- defaultUri = server.getUrl();
- }
-
- Bundle stream = new Bundle();
- stream.putString("mimeType", server.getMimeType());
- stream.putString("uri", server.getUrl());
- streams.add(stream);
+ for (PreviewServer server : servers) {
+ Bundle stream = new Bundle();
+ stream.putString("mimeType", server.getMimeType());
+ stream.putString("uri", server.getUri());
+ streams.add(stream);
+ }
+ return streams.toArray(new Bundle[0]);
+ }
+
+ /**
+ * エンコーダがサポートしているプロファイルとレベルを格納した Bundle の配列を取得します.
+ *
+ * @param encoderName エンコーダ
+ * @return プロファイルとレベルを格納した Bundle の配列
+ */
+ private static Bundle[] getProfileLevels(MediaRecorder.VideoEncoderName encoderName) {
+ List list = new ArrayList<>();
+ for (MediaRecorder.ProfileLevel pl : CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType())) {
+ switch (encoderName) {
+ case H264: {
+ H264Profile p = H264Profile.valueOf(pl.getProfile());
+ H264Level l = H264Level.valueOf(pl.getLevel());
+ if (p != null && l != null) {
+ Bundle encoder = new Bundle();
+ encoder.putString("profile", p.getName());
+ encoder.putString("level", l.getName());
+ list.add(encoder);
+ }
+ } break;
+ case H265: {
+ H265Profile p = H265Profile.valueOf(pl.getProfile());
+ H265Level l = H265Level.valueOf(pl.getLevel());
+ if (p != null && l != null) {
+ Bundle encoder = new Bundle();
+ encoder.putString("profile", p.getName());
+ encoder.putString("level", l.getName());
+ list.add(encoder);
+ }
+ } break;
}
- setResult(response, DConnectMessage.RESULT_OK);
- setUri(response, defaultUri != null ? defaultUri : "");
- response.putExtra("streams", streams.toArray(new Bundle[streams.size()]));
}
+ return list.toArray(new Bundle[0]);
}
}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCSystemProfile.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCSystemProfile.java
index 9ccdc22bf9..6b8d67aac5 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCSystemProfile.java
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/UVCSystemProfile.java
@@ -10,7 +10,7 @@
import android.content.Intent;
import android.os.Bundle;
-import org.deviceconnect.android.deviceplugin.uvc.activity.UVCServiceListActivity;
+import org.deviceconnect.android.deviceplugin.uvc.activity.UVCSettingsActivity;
import org.deviceconnect.android.profile.SystemProfile;
/**
@@ -19,11 +19,8 @@
* @author NTT DOCOMO, INC.
*/
public class UVCSystemProfile extends SystemProfile {
-
@Override
- protected Class extends Activity> getSettingPageActivity(final Intent request,
- final Bundle param) {
- return UVCServiceListActivity.class;
+ protected Class extends Activity> getSettingPageActivity(Intent request, Bundle param) {
+ return UVCSettingsActivity.class;
}
-
}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H264Level.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H264Level.java
new file mode 100644
index 0000000000..5925ee40ca
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H264Level.java
@@ -0,0 +1,60 @@
+package org.deviceconnect.android.deviceplugin.uvc.profile.utils;
+
+import android.media.MediaCodecInfo;
+
+public enum H264Level {
+ AVCLevel1("AVCLevel1", MediaCodecInfo.CodecProfileLevel.AVCLevel1),
+ AVCLevel11("AVCLevel1", MediaCodecInfo.CodecProfileLevel.AVCLevel1),
+ AVCLevel12("AVCLevel12", MediaCodecInfo.CodecProfileLevel.AVCLevel12),
+ AVCLevel13("AVCLevel13", MediaCodecInfo.CodecProfileLevel.AVCLevel13),
+ AVCLevel1b("AVCLevel1b", MediaCodecInfo.CodecProfileLevel.AVCLevel1b),
+ AVCLevel2("AVCLevel2", MediaCodecInfo.CodecProfileLevel.AVCLevel2),
+ AVCLevel21("AVCLevel21", MediaCodecInfo.CodecProfileLevel.AVCLevel21),
+ AVCLevel22("AVCLevel22", MediaCodecInfo.CodecProfileLevel.AVCLevel22),
+ AVCLevel3("AVCLevel3", MediaCodecInfo.CodecProfileLevel.AVCLevel3),
+ AVCLevel31("AVCLevel31", MediaCodecInfo.CodecProfileLevel.AVCLevel31),
+ AVCLevel32("AVCLevel32", MediaCodecInfo.CodecProfileLevel.AVCLevel32),
+ AVCLevel4("AVCLevel4", MediaCodecInfo.CodecProfileLevel.AVCLevel4),
+ AVCLevel41("AVCLevel41", MediaCodecInfo.CodecProfileLevel.AVCLevel41),
+ AVCLevel42("AVCLevel42", MediaCodecInfo.CodecProfileLevel.AVCLevel42),
+ AVCLevel5("AVCLevel5", MediaCodecInfo.CodecProfileLevel.AVCLevel5),
+ AVCLevel51("AVCLevel51", MediaCodecInfo.CodecProfileLevel.AVCLevel51),
+ AVCLevel52("AVCLevel52", MediaCodecInfo.CodecProfileLevel.AVCLevel52),
+ AVCLevel6("AVCLevel6", MediaCodecInfo.CodecProfileLevel.AVCLevel6),
+ AVCLevel61("AVCLevel61", MediaCodecInfo.CodecProfileLevel.AVCLevel61),
+ AVCLevel62("AVCLevel62", MediaCodecInfo.CodecProfileLevel.AVCLevel62);
+
+ private final String mName;
+ private final int mValue;
+
+ H264Level(String name, int value) {
+ mName = name;
+ mValue = value;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public static H264Level nameOf(String name) {
+ for (H264Level l : values()) {
+ if (l.mName.equalsIgnoreCase(name)) {
+ return l;
+ }
+ }
+ return null;
+ }
+
+ public static H264Level valueOf(int value) {
+ for (H264Level l : values()) {
+ if (l.mValue == value) {
+ return l;
+ }
+ }
+ return null;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H264Profile.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H264Profile.java
new file mode 100644
index 0000000000..6f8d8cd54f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H264Profile.java
@@ -0,0 +1,49 @@
+package org.deviceconnect.android.deviceplugin.uvc.profile.utils;
+
+import android.media.MediaCodecInfo;
+
+public enum H264Profile {
+ AVCProfileBaseline("AVCProfileBaseline", MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline),
+ AVCProfileConstrainedBaseline("AVCProfileConstrainedBaseline", MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedBaseline),
+ AVCProfileConstrainedHigh("AVCProfileConstrainedHigh", MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedHigh),
+ AVCProfileExtended("AVCProfileExtended", MediaCodecInfo.CodecProfileLevel.AVCProfileExtended),
+ AVCProfileHigh("AVCProfileHigh", MediaCodecInfo.CodecProfileLevel.AVCProfileHigh),
+ AVCProfileHigh10("AVCProfileHigh10", MediaCodecInfo.CodecProfileLevel.AVCProfileHigh10),
+ AVCProfileHigh422("AVCProfileHigh422", MediaCodecInfo.CodecProfileLevel.AVCProfileHigh422),
+ AVCProfileHigh444("AVCProfileHigh444", MediaCodecInfo.CodecProfileLevel.AVCProfileHigh444),
+ AVCProfileMain("AVCProfileMain", MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
+
+ private final String mName;
+ private final int mValue;
+
+ H264Profile(String name, int value) {
+ mName = name;
+ mValue = value;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public static H264Profile nameOf(String name) {
+ for (H264Profile p : values()) {
+ if (p.mName.equalsIgnoreCase(name)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ public static H264Profile valueOf(int value) {
+ for (H264Profile l : values()) {
+ if (l.mValue == value) {
+ return l;
+ }
+ }
+ return null;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H265Level.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H265Level.java
new file mode 100644
index 0000000000..2421c31571
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H265Level.java
@@ -0,0 +1,66 @@
+package org.deviceconnect.android.deviceplugin.uvc.profile.utils;
+
+import android.media.MediaCodecInfo;
+
+public enum H265Level {
+ HEVCHighTierLevel1("HEVCHighTierLevel1", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel1),
+ HEVCHighTierLevel2("HEVCHighTierLevel2", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel2),
+ HEVCHighTierLevel21("HEVCHighTierLevel21", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel21),
+ HEVCHighTierLevel3("HEVCHighTierLevel3", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel3),
+ HEVCHighTierLevel31("HEVCHighTierLevel31", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel31),
+ HEVCHighTierLevel4("HEVCHighTierLevel4", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel4),
+ HEVCHighTierLevel41("HEVCHighTierLevel41", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel41),
+ HEVCHighTierLevel5("HEVCHighTierLevel5", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel5),
+ HEVCHighTierLevel51("HEVCHighTierLevel51", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel51),
+ HEVCHighTierLevel52("HEVCHighTierLevel52", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel52),
+ HEVCHighTierLevel6("HEVCHighTierLevel6", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel6),
+ HEVCHighTierLevel61("HEVCHighTierLevel61", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel61),
+ HEVCHighTierLevel62("HEVCHighTierLevel62", MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel62),
+ HEVCMainTierLevel1("HEVCMainTierLevel1", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel1),
+ HEVCMainTierLevel2("HEVCMainTierLevel2", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel2),
+ HEVCMainTierLevel21("HEVCMainTierLevel21", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel21),
+ HEVCMainTierLevel3("HEVCMainTierLevel3", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel3),
+ HEVCMainTierLevel31("HEVCMainTierLevel31", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel31),
+ HEVCMainTierLevel4("HEVCMainTierLevel4", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel4),
+ HEVCMainTierLevel41("HEVCMainTierLevel41", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel41),
+ HEVCMainTierLevel5("HEVCMainTierLevel5", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel5),
+ HEVCMainTierLevel51("HEVCMainTierLevel51", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel51),
+ HEVCMainTierLevel52("HEVCMainTierLevel52", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel52),
+ HEVCMainTierLevel6("HEVCMainTierLevel6", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel6),
+ HEVCMainTierLevel61("HEVCMainTierLevel61", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel61),
+ HEVCMainTierLevel62("HEVCMainTierLevel62", MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel62);
+
+ private final String mName;
+ private final int mValue;
+
+ H265Level(String name, int value) {
+ mName = name;
+ mValue = value;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public static H265Level nameOf(String name) {
+ for (H265Level l : values()) {
+ if (l.mName.equalsIgnoreCase(name)) {
+ return l;
+ }
+ }
+ return null;
+ }
+
+ public static H265Level valueOf(int value) {
+ for (H265Level l : values()) {
+ if (l.mValue == value) {
+ return l;
+ }
+ }
+ return null;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H265Profile.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H265Profile.java
new file mode 100644
index 0000000000..2ffdaaebbd
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/profile/utils/H265Profile.java
@@ -0,0 +1,45 @@
+package org.deviceconnect.android.deviceplugin.uvc.profile.utils;
+
+import android.media.MediaCodecInfo;
+
+public enum H265Profile {
+ HEVCProfileMain("HEVCProfileMain", MediaCodecInfo.CodecProfileLevel.HEVCProfileMain),
+ HEVCProfileMain10("HEVCProfileMain10", MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10),
+ HEVCProfileMain10HDR10("HEVCProfileMain10HDR10", MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10),
+ HEVCProfileMain10HDR10Plus("HEVCProfileMain10HDR10Plus", MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus),
+ HEVCProfileMainStill("HEVCProfileMainStill", MediaCodecInfo.CodecProfileLevel.HEVCProfileMainStill);
+
+ private final String mName;
+ private final int mValue;
+
+ H265Profile(String name, int value) {
+ mName = name;
+ mValue = value;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public static H265Profile nameOf(String name) {
+ for (H265Profile p : values()) {
+ if (p.mName.equalsIgnoreCase(name)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ public static H265Profile valueOf(int value) {
+ for (H265Profile l : values()) {
+ if (l.mValue == value) {
+ return l;
+ }
+ }
+ return null;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractBroadcastProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractBroadcastProvider.java
new file mode 100644
index 0000000000..63a7abba67
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractBroadcastProvider.java
@@ -0,0 +1,269 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+
+import androidx.core.app.NotificationCompat;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public abstract class AbstractBroadcastProvider implements BroadcasterProvider {
+ /**
+ * 映像を配信するクラス.
+ */
+ private Broadcaster mBroadcaster;
+
+ /**
+ * イベントを通知するリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ private final Context mContext;
+ private final MediaRecorder mRecorder;
+
+ public AbstractBroadcastProvider(Context context, MediaRecorder recorder) {
+ mContext = context;
+ mRecorder = recorder;
+ }
+
+ @Override
+ public void setOnEventListener(OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ @Override
+ public Broadcaster getBroadcaster() {
+ return mBroadcaster;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mBroadcaster != null && mBroadcaster.isRunning();
+ }
+
+ @Override
+ public Broadcaster startBroadcaster(String broadcastURI) {
+ if (broadcastURI == null) {
+ return null;
+ }
+
+ if (mBroadcaster != null && mBroadcaster.isRunning()) {
+ return mBroadcaster;
+ }
+
+ CountDownLatch latch = new CountDownLatch(1);
+ AtomicBoolean result = new AtomicBoolean(false);
+
+ mBroadcaster = createBroadcaster(broadcastURI);
+ if (mBroadcaster == null) {
+ return null;
+ }
+ mBroadcaster.setOnEventListener(new Broadcaster.OnEventListener() {
+ @Override
+ public void onStarted() {
+ postBroadcastStarted(mBroadcaster);
+ }
+
+ @Override
+ public void onStopped() {
+ hideNotification(mRecorder.getId());
+ postBroadcastStopped(mBroadcaster);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ postBroadcastError(mBroadcaster, e);
+ }
+ });
+
+ mBroadcaster.start(new Broadcaster.OnStartCallback() {
+ @Override
+ public void onSuccess() {
+ result.set(true);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFailed(Exception e) {
+ result.set(false);
+ latch.countDown();
+ }
+ });
+
+ try {
+ latch.await(10, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return null;
+ }
+
+ if (!result.get()) {
+ mBroadcaster.stop();
+ mBroadcaster = null;
+ } else {
+ sendNotification(mRecorder.getId(), mRecorder.getName());
+ }
+
+ return mBroadcaster;
+ }
+
+ @Override
+ public void stopBroadcaster() {
+ hideNotification(mRecorder.getId());
+
+ if (mBroadcaster != null) {
+ mBroadcaster.stop();
+ mBroadcaster = null;
+ }
+ }
+
+ @Override
+ public void onConfigChange() {
+ if (mBroadcaster != null) {
+ mBroadcaster.onConfigChange();
+ }
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ if (mBroadcaster != null) {
+ mBroadcaster.setMute(mute);
+ }
+ }
+
+ /**
+ * Broadcaster のインスタンスを作成します.
+ *
+ * @param broadcastURI 配信先の URI
+ * @return Broadcaster のインスタンス
+ */
+ public abstract Broadcaster createBroadcaster(String broadcastURI);
+
+ private void postBroadcastStarted(Broadcaster broadcaster) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStarted(broadcaster);
+ }
+ }
+
+ private void postBroadcastStopped(Broadcaster broadcaster) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStopped(broadcaster);
+ }
+ }
+
+ private void postBroadcastError(Broadcaster broadcaster, Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(broadcaster, e);
+ }
+ }
+
+ /**
+ * Notification の Id を取得します.
+ *
+ * @return Notification の Id
+ */
+ private int getNotificationId() {
+ return 1000 + mRecorder.getId().hashCode();
+ }
+
+ /**
+ * プレビュー配信サーバ停止用の Notification を削除します.
+ *
+ * @param id notification を識別する ID
+ */
+ private void hideNotification(String id) {
+ NotificationManager manager = (NotificationManager) mContext
+ .getSystemService(Service.NOTIFICATION_SERVICE);
+ if (manager != null) {
+ manager.cancel(id, getNotificationId());
+ }
+ }
+
+ /**
+ * プレビュー配信サーバ停止用の Notification を送信します.
+ *
+ * @param id notification を識別する ID
+ * @param name 名前
+ */
+ private void sendNotification(String id, String name) {
+ PendingIntent contentIntent = createPendingIntent(id);
+ Notification notification = createNotification(contentIntent, null, name);
+ NotificationManager manager = (NotificationManager) mContext
+ .getSystemService(Service.NOTIFICATION_SERVICE);
+ if (manager != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ String channelId = mContext.getResources().getString(R.string.uvc_notification_preview_channel_id);
+ NotificationChannel channel = new NotificationChannel(
+ channelId,
+ mContext.getResources().getString(R.string.uvc_notification_recorder_broadcast),
+ NotificationManager.IMPORTANCE_LOW);
+ channel.setDescription(mContext.getResources().getString(R.string.uvc_notification_recorder_broadcast_content));
+ manager.createNotificationChannel(channel);
+ notification = createNotification(contentIntent, channelId, name);
+ }
+ manager.notify(id, getNotificationId(), notification);
+ }
+ }
+
+ /**
+ * Notificationを作成する.
+ *
+ * @param pendingIntent Notificationがクリックされたときに起動する Intent
+ * @param channelId チャンネルID
+ * @param name 名前
+ * @return Notification
+ */
+ private Notification createNotification(final PendingIntent pendingIntent, final String channelId, String name) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext.getApplicationContext());
+ builder.setContentIntent(pendingIntent);
+ builder.setTicker(mContext.getString(R.string.uvc_notification_recorder_broadcast_ticker));
+ builder.setSmallIcon(R.drawable.dconnect_icon);
+ builder.setContentTitle(mContext.getString(R.string.uvc_notification_recorder_broadcast, name));
+ builder.setContentText(mContext.getString(R.string.uvc_notification_recorder_broadcast_content));
+ builder.setWhen(System.currentTimeMillis());
+ builder.setAutoCancel(true);
+ builder.setOngoing(true);
+ return builder.build();
+ } else {
+ Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext());
+ builder.setContentIntent(pendingIntent);
+ builder.setTicker(mContext.getString(R.string.uvc_notification_preview_ticker));
+ int iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ?
+ R.drawable.dconnect_icon : R.drawable.dconnect_icon_lollipop;
+ builder.setSmallIcon(iconType);
+ builder.setContentTitle(mContext.getString(R.string.uvc_notification_recorder_broadcast, name));
+ builder.setContentText(mContext.getString(R.string.uvc_notification_recorder_broadcast_content));
+ builder.setWhen(System.currentTimeMillis());
+ builder.setAutoCancel(true);
+ builder.setOngoing(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && channelId != null) {
+ builder.setChannelId(channelId);
+ }
+ return builder.build();
+ }
+ }
+
+ /**
+ * PendingIntent を作成する.
+ *
+ * @param id レコーダ ID
+ *
+ * @return PendingIntent
+ */
+ private PendingIntent createPendingIntent(String id) {
+ Intent intent = new Intent();
+ intent.setAction(MediaRecorderManager.ACTION_STOP_BROADCAST);
+ intent.putExtra(MediaRecorderManager.KEY_RECORDER_ID, id);
+ return PendingIntent.getBroadcast(mContext, getNotificationId(), intent, 0);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractBroadcaster.java
new file mode 100644
index 0000000000..8300311a08
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractBroadcaster.java
@@ -0,0 +1,117 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.graphics.Rect;
+import android.util.Size;
+
+import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality;
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
+
+public abstract class AbstractBroadcaster implements Broadcaster {
+ /**
+ * 配信先の URI.
+ */
+ private final String mBroadcastURI;
+
+ /**
+ * カメラを操作するレコーダ.
+ */
+ private final MediaRecorder mRecorder;
+
+ public AbstractBroadcaster(MediaRecorder recorder, String broadcastURI) {
+ mRecorder = recorder;
+ mBroadcastURI = broadcastURI;
+ }
+
+ @Override
+ public String getMimeType() {
+ return "";
+ }
+
+ @Override
+ public String getBroadcastURI() {
+ return mBroadcastURI;
+ }
+
+ @Override
+ public void onConfigChange() {
+ VideoQuality videoQuality = getVideoQuality();
+ if (videoQuality != null) {
+ setVideoQuality(videoQuality);
+ }
+
+ AudioQuality audioQuality = getAudioQuality();
+ if (audioQuality != null) {
+ setAudioQuality(audioQuality);
+
+ MediaRecorder.Settings settings = getRecorder().getSettings();
+ setMute(settings.isMute());
+ }
+ }
+
+ /**
+ * 映像の設定を取得します.
+ *
+ * 映像が使用されていない場合は null を返却すること。
+ *
+ * @return 映像の設定
+ */
+ protected VideoQuality getVideoQuality() {
+ return null;
+ }
+
+ /**
+ * 音声の設定を取得します.
+ *
+ * 音声が使用されていない場合は null を返却すること。
+ *
+ * @return 音声の設定
+ */
+ protected AudioQuality getAudioQuality() {
+ return null;
+ }
+
+ /**
+ * Broadcaster で使用するレコーダを取得します.
+ *
+ * @return Broadcaster で使用するレコーダ
+ */
+ public MediaRecorder getRecorder() {
+ return mRecorder;
+ }
+
+ /**
+ * VideoEncoder の設定に、MediaRecorder の設定を反映します.
+ *
+ * @param videoQuality 設定を行う VideoEncoder の VideoQuality
+ */
+ public void setVideoQuality(VideoQuality videoQuality) {
+ MediaRecorder recorder = getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+
+ Rect rect = settings.getDrawingRange();
+ if (rect != null) {
+ videoQuality.setVideoWidth(rect.width());
+ videoQuality.setVideoHeight(rect.height());
+ } else {
+ Size previewSize = settings.getPreviewSize();
+ videoQuality.setVideoWidth(previewSize.getWidth());
+ videoQuality.setVideoHeight(previewSize.getHeight());
+ }
+ videoQuality.setBitRate(settings.getPreviewBitRate());
+ videoQuality.setFrameRate(settings.getPreviewMaxFrameRate());
+ videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval());
+ videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder());
+ videoQuality.setIntraRefresh(settings.getIntraRefresh());
+ videoQuality.setProfile(settings.getProfile());
+ videoQuality.setLevel(settings.getLevel());
+ }
+
+ /**
+ * AudioEncoder の設定に、MediaRecorder の設定を反映します.
+ *
+ * @param audioQuality 設定を行う AudioEncoder の AudioQuality
+ */
+ public void setAudioQuality(AudioQuality audioQuality) {
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractMJPEGPreviewServer.java
new file mode 100644
index 0000000000..f732132a9e
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractMJPEGPreviewServer.java
@@ -0,0 +1,195 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.util.Size;
+
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGEncoder;
+import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGQuality;
+import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGServer;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import javax.net.ssl.SSLContext;
+
+public abstract class AbstractMJPEGPreviewServer extends AbstractPreviewServer {
+ /**
+ * Motion JPEG のマイムタイプを定義します.
+ */
+ protected static final String MIME_TYPE = "video/x-mjpeg";
+
+ /**
+ * サーバー名を定義します.
+ */
+ private static final String SERVER_NAME = "Android Host Camera2 MJPEG Server";
+
+ /**
+ * MotionJPEG 配信サーバ.
+ */
+ private MJPEGServer mMJPEGServer;
+
+ public AbstractMJPEGPreviewServer(Context context, MediaRecorder recorder, boolean useSSL) {
+ super(context, recorder, useSSL);
+ }
+
+ // PreviewServer
+
+ @Override
+ public String getUri() {
+ return mMJPEGServer == null ? null : mMJPEGServer.getUri();
+ }
+
+ @Override
+ public String getMimeType() {
+ return MIME_TYPE;
+ }
+
+ @Override
+ public void startWebServer(final OnWebServerStartCallback callback) {
+ if (mMJPEGServer == null) {
+ SSLContext sslContext = getSSLContext();
+ if (useSSLContext() && sslContext == null) {
+ callback.onFail();
+ return;
+ }
+
+ mMJPEGServer = new MJPEGServer();
+ mMJPEGServer.setServerName(SERVER_NAME);
+ mMJPEGServer.setServerPort(getPort());
+ mMJPEGServer.setCallback(mCallback);
+ if (useSSLContext()) {
+ mMJPEGServer.setSSLContext(sslContext);
+ }
+ try {
+ mMJPEGServer.start();
+ } catch (Exception e) {
+ callback.onFail();
+ return;
+ }
+ }
+ callback.onStart(getUri());
+ }
+
+ @Override
+ public void stopWebServer() {
+ if (mMJPEGServer != null) {
+ mMJPEGServer.stop();
+ mMJPEGServer = null;
+ }
+ }
+
+ @Override
+ public boolean requestSyncFrame() {
+ // 何もしない
+ return false;
+ }
+
+ @Override
+ public long getBPS() {
+ return mMJPEGServer != null ? mMJPEGServer.getBPS() : 0;
+ }
+
+ @Override
+ public void onConfigChange() {
+ setEncoderQuality();
+ restartEncoder();
+ }
+
+ /**
+ * エンコーダの設定を行います.
+ */
+ private void setEncoderQuality() {
+ if (mMJPEGServer != null) {
+ MJPEGEncoder encoder = mMJPEGServer.getMJPEGEncoder();
+ if (encoder != null) {
+ setMJPEGQuality(encoder.getMJPEGQuality());
+ }
+ }
+ }
+
+ /**
+ * エンコーダを再スタートさせて、設定を反映します.
+ */
+ private void restartEncoder() {
+ if (mMJPEGServer != null) {
+ new Thread(() -> {
+ if (mMJPEGServer != null) {
+ mMJPEGServer.restartEncoder();
+ }
+ }).start();
+ }
+ }
+
+ /**
+ * MJPEG の設定を行います.
+ *
+ * @param quality 設定を行う MJPEGQuality
+ */
+ private void setMJPEGQuality(MJPEGQuality quality) {
+ MediaRecorder recorder = getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+
+ Rect rect = settings.getDrawingRange();
+ if (rect != null) {
+ quality.setWidth(rect.width());
+ quality.setHeight(rect.height());
+ } else {
+ Size previewSize = settings.getPreviewSize();
+ quality.setWidth(previewSize.getWidth());
+ quality.setHeight(previewSize.getHeight());
+ }
+ quality.setFrameRate(settings.getPreviewMaxFrameRate());
+ quality.setQuality(settings.getPreviewQuality());
+ }
+
+ /**
+ * MJPEG 用のエンコーダを作成します.
+ *
+ * @return MJPEG 用のエンコーダ
+ */
+ protected abstract MJPEGEncoder createSurfaceMJPEGEncoder();
+
+ /**
+ * MJPEGServer からのイベントを受け取るためのコールバック.
+ */
+ private final MJPEGServer.Callback mCallback = new MJPEGServer.Callback() {
+ @Override
+ public boolean onAccept(Socket socket) {
+ if (DEBUG) {
+ Log.d(TAG, "MJPEGServer.Callback#onAccept: ");
+ Log.d(TAG, " socket: " + socket);
+ }
+ // 特に制限を付けないので、常に true を返却
+ return true;
+ }
+
+ @Override
+ public void onClosed(Socket socket) {
+ if (DEBUG) {
+ Log.d(TAG, "MJPEGServer.Callback#onClosed: ");
+ Log.d(TAG, " socket: " + socket);
+ }
+ }
+
+ @Override
+ public MJPEGEncoder createMJPEGEncoder() {
+ if (DEBUG) {
+ Log.d(TAG, "MJPEGServer.Callback#createMJPEGEncoder: ");
+ }
+
+ MJPEGEncoder encoder = createSurfaceMJPEGEncoder();
+ setMJPEGQuality(encoder.getMJPEGQuality());
+ return encoder;
+ }
+
+ @Override
+ public void releaseMJPEGEncoder(MJPEGEncoder encoder) {
+ if (DEBUG) {
+ Log.d(TAG, "MJPEGServer.Callback#releaseMJPEGEncoder: ");
+ }
+ }
+ };
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractMediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractMediaRecorder.java
new file mode 100644
index 0000000000..417772cb11
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractMediaRecorder.java
@@ -0,0 +1,463 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+
+import org.deviceconnect.android.activity.PermissionUtility;
+import org.deviceconnect.android.deviceplugin.uvc.R;
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+
+public abstract class AbstractMediaRecorder implements MediaRecorder {
+ /**
+ * コンテキスト.
+ */
+ private final Context mContext;
+
+ /**
+ * リクエストの処理を実行するハンドラ.
+ */
+ private final Handler mRequestHandler;
+
+ /**
+ * イベント通知用リスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ /**
+ * レコーダの状態.
+ */
+ private State mState = State.INACTIVE;
+
+ /**
+ * コンストラクタ.
+ */
+ public AbstractMediaRecorder(Context context) {
+ if (context == null) {
+ throw new IllegalArgumentException("context is null.");
+ }
+
+ mContext = context;
+
+ HandlerThread requestThread = new HandlerThread("uvc-media-recorder");
+ requestThread.start();
+ mRequestHandler = new Handler(requestThread.getLooper());
+ }
+
+ // Implements MediaRecorder methods.
+
+ @Override
+ public void initialize() {
+ BroadcasterProvider broadcasterProvider = getBroadcasterProvider();
+ if (broadcasterProvider != null) {
+ broadcasterProvider.setOnEventListener(new BroadcasterProvider.OnEventListener() {
+ @Override
+ public void onStarted(Broadcaster broadcaster) {
+ postOnBroadcasterStarted(broadcaster);
+ }
+
+ @Override
+ public void onStopped(Broadcaster broadcaster) {
+ postOnBroadcasterStopped(broadcaster);
+ }
+
+ @Override
+ public void onError(Broadcaster broadcaster, Exception e) {
+ postOnBroadcasterError(broadcaster, e);
+ }
+ });
+ }
+
+ PreviewServerProvider previewProvider = getServerProvider();
+ if (previewProvider != null) {
+ previewProvider.setOnEventListener(new PreviewServerProvider.OnEventListener() {
+ @Override
+ public void onStarted(List servers) {
+ postOnPreviewStarted(servers);
+ }
+
+ @Override
+ public void onStopped() {
+ postOnPreviewStopped();
+ }
+
+ @Override
+ public void onError(PreviewServer server, Exception e) {
+ postOnPreviewError(e);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void clean() {
+ try {
+ BroadcasterProvider broadcasterProvider = getBroadcasterProvider();
+ if (broadcasterProvider != null) {
+ broadcasterProvider.stopBroadcaster();
+ }
+
+ PreviewServerProvider serverProvider = getServerProvider();
+ if (serverProvider != null) {
+ serverProvider.stopServers();;
+ }
+
+ EGLSurfaceDrawingThread thread = getSurfaceDrawingThread();
+ if (thread != null) {
+ thread.stop();
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ @Override
+ public void destroy() {
+ clean();
+ mRequestHandler.getLooper().quit();
+ }
+
+ @Override
+ public void onConfigChange() {
+ PreviewServerProvider serverProvider = getServerProvider();
+ if (serverProvider != null) {
+ serverProvider.onConfigChange();
+ }
+
+ BroadcasterProvider broadcasterProvider = getBroadcasterProvider();
+ if (broadcasterProvider != null) {
+ broadcasterProvider.onConfigChange();
+ }
+
+ postOnConfigChanged();
+ }
+
+ @Override
+ public State getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean isPreviewRunning() {
+ PreviewServerProvider provider = getServerProvider();
+ return provider != null && provider.isRunning();
+ }
+
+ @Override
+ public List startPreview() {
+ PreviewServerProvider provider = getServerProvider();
+ if (provider == null) {
+ return new ArrayList<>();
+ }
+
+ List servers = provider.startServers();
+ if (!servers.isEmpty()) {
+ provider.setMute(getSettings().isMute());
+ }
+ return servers;
+ }
+
+ @Override
+ public void stopPreview() {
+ PreviewServerProvider provider = getServerProvider();
+ if (provider != null) {
+ provider.stopServers();
+ }
+ }
+
+ @Override
+ public boolean isBroadcasterRunning() {
+ BroadcasterProvider provider = getBroadcasterProvider();
+ return provider != null && provider.isRunning();
+ }
+
+ @Override
+ public Broadcaster startBroadcaster(String uri) {
+ if (uri == null) {
+ return null;
+ }
+
+ BroadcasterProvider provider = getBroadcasterProvider();
+ if (provider == null) {
+ return null;
+ }
+
+ Broadcaster broadcaster = provider.startBroadcaster(uri);
+ if (broadcaster != null) {
+ broadcaster.setMute(getSettings().isMute());
+ }
+ return broadcaster;
+ }
+
+ @Override
+ public void stopBroadcaster() {
+ BroadcasterProvider provider = getBroadcasterProvider();
+ if (provider != null) {
+ provider.stopBroadcaster();
+ }
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ Settings settings = getSettings();
+ settings.setMute(mute);
+
+ PreviewServerProvider previewProvider = getServerProvider();
+ if (previewProvider != null) {
+ previewProvider.setMute(mute);
+ }
+
+ BroadcasterProvider broadcasterProvider = getBroadcasterProvider();
+ if (broadcasterProvider != null) {
+ broadcasterProvider.setMute(mute);
+ }
+ }
+
+ @Override
+ public boolean isMute() {
+ return getSettings().isMute();
+ }
+
+ @Override
+ public void setSSLContext(SSLContext sslContext) {
+ PreviewServerProvider previewProvider = getServerProvider();
+ if (previewProvider != null) {
+ for (PreviewServer server : previewProvider.getServers()) {
+ server.setSSLContext(sslContext);
+ }
+ }
+ }
+
+ @Override
+ public void setOnEventListener(OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ @Override
+ public long getBPS() {
+ PreviewServerProvider previewProvider = getServerProvider();
+ if (previewProvider == null) {
+ return 0;
+ }
+
+ long bps = 0;
+ List servers = previewProvider.getServers();
+ for (PreviewServer previewServer : servers) {
+ bps += previewServer.getBPS();
+ }
+ return bps;
+ }
+
+ /**
+ * Runnable を順番に実行します.
+ *
+ * @param run 実行する Runnable
+ */
+ protected void postRequestHandler(Runnable run) {
+ mRequestHandler.post(run);
+ }
+
+ /**
+ * レコーダの状態を設定します.
+ *
+ * @param state レコーダの状態
+ */
+ protected void setState(State state) {
+ mState = state;
+ }
+
+ /**
+ * コンテキストを取得します.
+ *
+ * @return コンテキスト
+ */
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * パーミッション要求します.
+ *
+ * @param permissions パーミッションのリスト
+ * @param callback パーミションの結果を受け取るコールバック
+ */
+ protected void requestPermission(String[] permissions, PermissionCallback callback) {
+ Handler handler = new Handler(Looper.getMainLooper());
+ PermissionUtility.requestPermissions(getContext(), handler, permissions, new PermissionUtility.PermissionRequestCallback() {
+ @Override
+ public void onSuccess() {
+ callback.onAllowed();
+ }
+
+ @Override
+ public void onFail(@NonNull String deniedPermission) {
+ callback.onDisallowed();
+ }
+ });
+ }
+
+ /**
+ * NotificationId を取得します.
+ *
+ * @return NotificationId
+ */
+ private int getNotificationId() {
+ return 2000 + getId().hashCode();
+ }
+
+ /**
+ * レコーディング停止用の Notification を削除します.
+ *
+ * @param id notification を識別する ID
+ */
+ private void hideNotification(String id) {
+ NotificationManager manager = (NotificationManager) mContext
+ .getSystemService(Service.NOTIFICATION_SERVICE);
+ if (manager != null) {
+ manager.cancel(id, getNotificationId());
+ }
+ }
+
+ /**
+ * 録音・録画停止用の Notification を表示します.
+ *
+ * @param id notification を識別する ID
+ * @param name 名前
+ */
+ private void showNotificationForStopRecording(String id, String name) {
+ PendingIntent contentIntent = createPendingIntentForStopRecording(id);
+ Notification notification = createNotificationForStopRecording(contentIntent, null, name);
+ NotificationManager manager = (NotificationManager) mContext
+ .getSystemService(Service.NOTIFICATION_SERVICE);
+ if (manager != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ String channelId = mContext.getResources().getString(R.string.uvc_notification_preview_channel_id);
+ NotificationChannel channel = new NotificationChannel(
+ channelId,
+ mContext.getResources().getString(R.string.uvc_notification_recorder_recording),
+ NotificationManager.IMPORTANCE_LOW);
+ channel.setDescription(mContext.getResources().getString(R.string.uvc_notification_recorder_recording_content));
+ manager.createNotificationChannel(channel);
+ notification = createNotificationForStopRecording(contentIntent, channelId, name);
+ }
+ manager.notify(id, getNotificationId(), notification);
+ }
+ }
+
+ /**
+ * 録音・録画停止用の Notification を作成する.
+ *
+ * @param pendingIntent Notificationがクリックされたときに起動する Intent
+ * @param channelId チャンネルID
+ * @param name 名前
+ * @return Notification
+ */
+ protected Notification createNotificationForStopRecording(final PendingIntent pendingIntent, final String channelId, String name) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext.getApplicationContext());
+ builder.setContentIntent(pendingIntent);
+ builder.setTicker(mContext.getString(R.string.uvc_notification_recorder_recording_ticker));
+ builder.setSmallIcon(R.drawable.dconnect_icon);
+ builder.setContentTitle(mContext.getString(R.string.uvc_notification_recorder_recording, name));
+ builder.setContentText(mContext.getString(R.string.uvc_notification_recorder_recording_content));
+ builder.setWhen(System.currentTimeMillis());
+ builder.setAutoCancel(true);
+ builder.setOngoing(true);
+ return builder.build();
+ } else {
+ Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext());
+ builder.setContentIntent(pendingIntent);
+ builder.setTicker(mContext.getString(R.string.uvc_notification_preview_ticker));
+ int iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ?
+ R.drawable.dconnect_icon : R.drawable.dconnect_icon_lollipop;
+ builder.setSmallIcon(iconType);
+ builder.setContentTitle(mContext.getString(R.string.uvc_notification_recorder_recording, name));
+ builder.setContentText(mContext.getString(R.string.uvc_notification_recorder_recording_content));
+ builder.setWhen(System.currentTimeMillis());
+ builder.setAutoCancel(true);
+ builder.setOngoing(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && channelId != null) {
+ builder.setChannelId(channelId);
+ }
+ return builder.build();
+ }
+ }
+
+ /**
+ * PendingIntent を作成する.
+ *
+ * @param id カメラ ID
+ *
+ * @return PendingIntent
+ */
+ private PendingIntent createPendingIntentForStopRecording(String id) {
+ Intent intent = new Intent();
+ intent.setAction(MediaRecorderManager.ACTION_STOP_RECORDING);
+ intent.putExtra(MediaRecorderManager.KEY_RECORDER_ID, id);
+ return PendingIntent.getBroadcast(mContext, getNotificationId(), intent, 0);
+ }
+
+ protected void postOnConfigChanged() {
+ if (mOnEventListener != null) {
+ mOnEventListener.onConfigChanged();
+ }
+ }
+
+ protected void postOnPreviewStarted(List servers) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onPreviewStarted(servers);
+ }
+ }
+
+ protected void postOnPreviewStopped() {
+ if (mOnEventListener != null) {
+ mOnEventListener.onPreviewStopped();
+ }
+ }
+
+ protected void postOnPreviewError(Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onPreviewError(e);
+ }
+ }
+
+ protected void postOnBroadcasterStarted(Broadcaster broadcaster) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onBroadcasterStarted(broadcaster);
+ }
+ }
+
+ protected void postOnBroadcasterStopped(Broadcaster broadcaster) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onBroadcasterStopped(broadcaster);
+ }
+ }
+
+ protected void postOnBroadcasterError(Broadcaster broadcaster, Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onBroadcasterError(broadcaster, e);
+ }
+ }
+
+ protected void postOnError(Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractPreviewServer.java
new file mode 100644
index 0000000000..ef79f607d1
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractPreviewServer.java
@@ -0,0 +1,212 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Size;
+
+import org.deviceconnect.android.deviceplugin.uvc.BuildConfig;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality;
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
+
+import javax.net.ssl.SSLContext;
+
+/**
+ * プレビュー配信サーバ.
+ */
+public abstract class AbstractPreviewServer implements PreviewServer {
+ protected static final boolean DEBUG = BuildConfig.DEBUG;
+ protected static final String TAG = "host.dplugin";
+
+ /**
+ * コンテキスト.
+ */
+ private final Context mContext;
+
+ /**
+ * プレビュー再生を行うレコーダ.
+ */
+ private final MediaRecorder mMediaRecorder;
+
+ /**
+ * プレビュー配信サーバのポート番号.
+ */
+ private int mPort;
+
+ /**
+ * ミュート設定.
+ */
+ private boolean mMute;
+
+ /**
+ * SSLContext のインスタンス.
+ */
+ private SSLContext mSSLContext;
+
+ /**
+ * SSL の使用フラグ.
+ */
+ private final boolean mUseSSL;
+
+ /**
+ * コンストラクタ.
+ *
+ *
+ * デフォルトでは、mute は true に設定しています。
+ * デフォルトでは、mUseSSL は false に設定します。
+ *
+ *
+ * @param context コンテキスト
+ * @param recorder プレビューで表示するレコーダ
+ */
+ public AbstractPreviewServer(Context context, MediaRecorder recorder) {
+ this(context, recorder, false);
+ }
+
+ /**
+ * コンストラクタ.
+ *
+ *
+ * デフォルトでは、mute は true に設定しています。
+ *
+ *
+ * @param context コンテキスト
+ * @param recorder プレビューで表示するレコーダ
+ * @param useSSL SSL使用フラグ
+ */
+ public AbstractPreviewServer(Context context, MediaRecorder recorder, boolean useSSL) {
+ mContext = context;
+ mMediaRecorder = recorder;
+ mUseSSL = useSSL;
+ mMute = true;
+ }
+
+ // Implements PreviewServer methods.
+
+ @Override
+ public int getPort() {
+ return mPort;
+ }
+
+ @Override
+ public void setPort(int port) {
+ mPort = port;
+ }
+
+ @Override
+ public void onConfigChange() {
+ VideoQuality videoQuality = getVideoQuality();
+ if (videoQuality != null) {
+ setVideoQuality(videoQuality);
+ }
+
+ AudioQuality audioQuality = getAudioQuality();
+ if (audioQuality != null) {
+ setAudioQuality(audioQuality);
+
+ MediaRecorder.Settings settings = getRecorder().getSettings();
+ setMute(settings.isMute());
+ }
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ mMute = mute;
+ }
+
+ @Override
+ public boolean isMuted() {
+ return mMute;
+ }
+
+ @Override
+ public boolean useSSLContext() {
+ return mUseSSL;
+ }
+
+ @Override
+ public void setSSLContext(final SSLContext sslContext) {
+ mSSLContext = sslContext;
+ }
+
+ @Override
+ public SSLContext getSSLContext() {
+ return mSSLContext;
+ }
+
+ /**
+ * コンテキストを取得します.
+ *
+ * @return コンテキスト
+ */
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * プレビューを表示するレコーダー.
+ *
+ * @return レコーダー
+ */
+ public MediaRecorder getRecorder() {
+ return mMediaRecorder;
+ }
+
+ /**
+ * 映像の設定を取得します.
+ *
+ * 映像が使用されていない場合は null を返却すること。
+ *
+ * @return 映像の設定
+ */
+ protected VideoQuality getVideoQuality() {
+ return null;
+ }
+
+ /**
+ * 音声の設定を取得します.
+ *
+ * 音声が使用されていない場合は null を返却すること。
+ *
+ * @return 音声の設定
+ */
+ protected AudioQuality getAudioQuality() {
+ return null;
+ }
+
+ /**
+ * VideoEncoder の設定に、MediaRecorder の設定を反映します.
+ *
+ * @param videoQuality 設定を行う VideoEncoder の VideoQuality
+ */
+ public void setVideoQuality(VideoQuality videoQuality) {
+ MediaRecorder recorder = getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+
+ Rect rect = settings.getDrawingRange();
+ if (rect != null) {
+ videoQuality.setVideoWidth(rect.width());
+ videoQuality.setVideoHeight(rect.height());
+ } else {
+ Size previewSize = settings.getPreviewSize();
+ videoQuality.setVideoWidth(previewSize.getWidth());
+ videoQuality.setVideoHeight(previewSize.getHeight());
+ }
+ videoQuality.setBitRate(settings.getPreviewBitRate());
+ videoQuality.setFrameRate(settings.getPreviewMaxFrameRate());
+ videoQuality.setIFrameInterval(settings.getPreviewKeyFrameInterval());
+ videoQuality.setUseSoftwareEncoder(settings.isUseSoftwareEncoder());
+ videoQuality.setIntraRefresh(settings.getIntraRefresh());
+ videoQuality.setProfile(settings.getProfile());
+ videoQuality.setLevel(settings.getLevel());
+ }
+
+ /**
+ * AudioEncoder の設定に、MediaRecorder の設定を反映します.
+ *
+ * @param audioQuality 設定を行う AudioEncoder の AudioQuality
+ */
+ public void setAudioQuality(AudioQuality audioQuality) {
+
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractPreviewServerProvider.java
new file mode 100644
index 0000000000..59819bf46d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractPreviewServerProvider.java
@@ -0,0 +1,300 @@
+/*
+ HostDevicePreviewServer.java
+ Copyright (c) 2014 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+
+import org.deviceconnect.android.deviceplugin.uvc.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Host Device Preview Server.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public abstract class AbstractPreviewServerProvider implements PreviewServerProvider {
+ /**
+ * コンテキスト.
+ */
+ private final Context mContext;
+
+ /**
+ * プレビュー配信サーバーのリスト.
+ */
+ private final List mPreviewServers = new ArrayList<>();
+
+ /**
+ * プレビュー配信を行うレコーダ.
+ */
+ private final MediaRecorder mRecorder;
+
+ /**
+ * Notification 表示フラグ.
+ */
+ private boolean mIsRunning;
+
+ /**
+ * プレビュー配信サーバのイベントを通知するリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ /**
+ * コンストラクタ.
+ * @param context コンテキスト
+ */
+ public AbstractPreviewServerProvider(final Context context, final MediaRecorder recorder) {
+ mContext = context;
+ mRecorder = recorder;
+ mIsRunning = false;
+ }
+
+ // PreviewServerProvider
+
+ @Override
+ public List getSupportedMimeType() {
+ List mimeType = new ArrayList<>();
+ for (PreviewServer server : getServers()) {
+ mimeType.add(server.getMimeType());
+ }
+ return mimeType;
+ }
+
+ @Override
+ public void addServer(PreviewServer server) {
+ mPreviewServers.add(server);
+ }
+
+ @Override
+ public List getServers() {
+ return mPreviewServers;
+ }
+
+ @Override
+ public PreviewServer getServerByMimeType(String mimeType) {
+ for (PreviewServer server : getServers()) {
+ if (server.getMimeType().equalsIgnoreCase(mimeType)) {
+ return server;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mIsRunning;
+ }
+
+ @Override
+ public List startServers() {
+ List results = new ArrayList<>();
+
+ CountDownLatch latch = new CountDownLatch(mPreviewServers.size());
+ for (PreviewServer server : mPreviewServers) {
+ server.startWebServer(new PreviewServer.OnWebServerStartCallback() {
+ @Override
+ public void onStart(@NonNull String uri) {
+ results.add(server);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFail() {
+ latch.countDown();
+ }
+ });
+ }
+
+ try {
+ latch.await(5, TimeUnit.SECONDS);
+ if (results.size() > 0) {
+ mIsRunning = true;
+ sendNotification(mRecorder.getId(), mRecorder.getName());
+ postPreviewStarted(results);
+ }
+ } catch (InterruptedException e) {
+ // ignore.
+ }
+ return results;
+ }
+
+ @Override
+ public void stopServers() {
+ hideNotification(mRecorder.getId());
+
+ for (PreviewServer server : getServers()) {
+ server.stopWebServer();
+ }
+
+ if (mIsRunning) {
+ mIsRunning = false;
+ postPreviewStopped();
+ }
+ }
+
+ @Override
+ public List requestSyncFrame() {
+ List result = new ArrayList<>();
+ for (PreviewServer server : getServers()) {
+ if (server.requestSyncFrame()) {
+ result.add(server);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void onConfigChange() {
+ for (PreviewServer server : getServers()) {
+ server.onConfigChange();
+ }
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ for (PreviewServer server : getServers()) {
+ server.setMute(mute);
+ }
+ }
+
+ @Override
+ public void setOnEventListener(OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ protected void postPreviewStarted(List servers) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStarted(servers);
+ }
+ }
+
+ protected void postPreviewStopped() {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStopped();
+ }
+ }
+
+ protected void postPreviewError(PreviewServer server, Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(server, e);
+ }
+ }
+
+ /**
+ * Notification の Id を取得します.
+ *
+ * @return Notification の Id
+ */
+ protected int getNotificationId() {
+ return 100 + mRecorder.getId().hashCode();
+ }
+
+ /**
+ * プレビュー配信サーバ停止用の Notification を削除します.
+ *
+ * @param id notification を識別する ID
+ */
+ private void hideNotification(String id) {
+ NotificationManager manager = (NotificationManager) mContext
+ .getSystemService(Service.NOTIFICATION_SERVICE);
+ if (manager != null) {
+ manager.cancel(id, getNotificationId());
+ }
+ }
+
+ /**
+ * プレビュー配信サーバ停止用の Notification を送信します.
+ *
+ * @param id notification を識別する ID
+ * @param name 名前
+ */
+ private void sendNotification(String id, String name) {
+ PendingIntent contentIntent = createPendingIntent(id);
+ Notification notification = createNotification(contentIntent, null, name);
+ NotificationManager manager = (NotificationManager) mContext
+ .getSystemService(Service.NOTIFICATION_SERVICE);
+ if (manager != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ String channelId = mContext.getResources().getString(R.string.uvc_notification_preview_channel_id);
+ NotificationChannel channel = new NotificationChannel(
+ channelId,
+ mContext.getResources().getString(R.string.uvc_notification_recorder_preview),
+ NotificationManager.IMPORTANCE_LOW);
+ channel.setDescription(mContext.getResources().getString(R.string.uvc_notification_recorder_preview_content));
+ manager.createNotificationChannel(channel);
+ notification = createNotification(contentIntent, channelId, name);
+ }
+ manager.notify(id, getNotificationId(), notification);
+ }
+ }
+
+ /**
+ * Notificationを作成する.
+ *
+ * @param pendingIntent Notificationがクリックされたときに起動する Intent
+ * @param channelId チャンネルID
+ * @param name 名前
+ * @return Notification
+ */
+ protected Notification createNotification(final PendingIntent pendingIntent, final String channelId, String name) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext.getApplicationContext());
+ builder.setContentIntent(pendingIntent);
+ builder.setTicker(mContext.getString(R.string.uvc_notification_recorder_preview_ticker));
+ builder.setSmallIcon(R.drawable.dconnect_icon);
+ builder.setContentTitle(mContext.getString(R.string.uvc_notification_recorder_preview, name));
+ builder.setContentText(mContext.getString(R.string.uvc_notification_recorder_preview_content));
+ builder.setWhen(System.currentTimeMillis());
+ builder.setAutoCancel(true);
+ builder.setOngoing(true);
+ return builder.build();
+ } else {
+ Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext());
+ builder.setContentIntent(pendingIntent);
+ builder.setTicker(mContext.getString(R.string.uvc_notification_preview_ticker));
+ int iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ?
+ R.drawable.dconnect_icon : R.drawable.dconnect_icon_lollipop;
+ builder.setSmallIcon(iconType);
+ builder.setContentTitle(mContext.getString(R.string.uvc_notification_recorder_preview, name));
+ builder.setContentText(mContext.getString(R.string.uvc_notification_recorder_preview_content));
+ builder.setWhen(System.currentTimeMillis());
+ builder.setAutoCancel(true);
+ builder.setOngoing(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && channelId != null) {
+ builder.setChannelId(channelId);
+ }
+ return builder.build();
+ }
+ }
+
+ /**
+ * PendingIntent を作成する.
+ *
+ * @param id レコーダ ID
+ *
+ * @return PendingIntent
+ */
+ private PendingIntent createPendingIntent(String id) {
+ Intent intent = new Intent();
+ intent.setAction(MediaRecorderManager.ACTION_STOP_PREVIEW);
+ intent.putExtra(MediaRecorderManager.KEY_RECORDER_ID, id);
+ return PendingIntent.getBroadcast(mContext, getNotificationId(), intent, 0);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractRTMPBroadcaster.java
new file mode 100644
index 0000000000..f615294be9
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractRTMPBroadcaster.java
@@ -0,0 +1,177 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.util.Log;
+
+import org.deviceconnect.android.libmedia.streaming.MediaEncoderException;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality;
+import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder;
+import org.deviceconnect.android.libmedia.streaming.rtmp.RtmpClient;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
+
+public abstract class AbstractRTMPBroadcaster extends AbstractBroadcaster {
+
+ /**
+ * RTMP 配信クライアント.
+ */
+ private RtmpClient mRtmpClient;
+
+ /**
+ * イベントを通知するためのリスナー.
+ */
+ private OnEventListener mOnBroadcasterEventListener;
+
+ public AbstractRTMPBroadcaster(MediaRecorder recorder, String broadcastURI) {
+ super(recorder, broadcastURI);
+ }
+
+ /**
+ * RTMP で配信するための映像用エンコーダを取得します.
+ *
+ * @return RTMP で配信するための映像用エンコーダ
+ */
+ protected VideoEncoder createVideoEncoder() {
+ return null;
+ }
+
+ /**
+ * RTMP で配信するための音声用エンコーダを取得します.
+ *
+ * @return RTMP で配信するための音声用エンコーダ
+ */
+ protected AudioEncoder createAudioEncoder() {
+ MediaRecorder.Settings settings = getRecorder().getSettings();
+ if (settings.isAudioEnabled()) {
+ return new MicAACLATMEncoder();
+ }
+ return null;
+ }
+
+ @Override
+ public void setOnEventListener(OnEventListener listener) {
+ mOnBroadcasterEventListener = listener;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRtmpClient != null && mRtmpClient.isRunning();
+ }
+
+ @Override
+ public void start(OnStartCallback callback) {
+ VideoEncoder videoEncoder = createVideoEncoder();
+ if (videoEncoder != null) {
+ setVideoQuality(videoEncoder.getVideoQuality());
+ }
+
+ AudioEncoder audioEncoder = createAudioEncoder();
+ if (audioEncoder != null) {
+ setAudioQuality(audioEncoder.getAudioQuality());
+ }
+
+ MediaRecorder.Settings settings = getRecorder().getSettings();
+
+ mRtmpClient = new RtmpClient(getBroadcastURI());
+ mRtmpClient.setMaxRetryCount(settings.getRetryCount());
+ mRtmpClient.setRetryInterval(settings.getRetryInterval());
+ mRtmpClient.setVideoEncoder(videoEncoder);
+ mRtmpClient.setAudioEncoder(audioEncoder);
+ mRtmpClient.setOnEventListener(new RtmpClient.OnEventListener() {
+ @Override
+ public void onStarted() {
+ if (callback != null) {
+ callback.onSuccess();
+ }
+
+ if (mOnBroadcasterEventListener != null) {
+ mOnBroadcasterEventListener.onStarted();
+ }
+ }
+
+ @Override
+ public void onStopped() {
+ if (mOnBroadcasterEventListener != null) {
+ mOnBroadcasterEventListener.onStopped();
+ }
+ }
+
+ @Override
+ public void onError(MediaEncoderException e) {
+ if (callback != null) {
+ callback.onFailed(e);
+ }
+
+ if (mOnBroadcasterEventListener != null) {
+ mOnBroadcasterEventListener.onError(e);
+ }
+
+ AbstractRTMPBroadcaster.this.stop();
+ }
+
+ @Override
+ public void onConnected() {
+ }
+
+ @Override
+ public void onDisconnected() {
+ }
+
+ @Override
+ public void onNewBitrate(long bitrate) {
+ }
+ });
+ mRtmpClient.start();
+ }
+
+ @Override
+ public void stop() {
+ if (mRtmpClient != null) {
+ mRtmpClient.stop();
+ mRtmpClient = null;
+ }
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ if (mRtmpClient != null) {
+ mRtmpClient.setMute(mute);
+ }
+ }
+
+ @Override
+ public boolean isMute() {
+ return mRtmpClient != null && mRtmpClient.isMute();
+ }
+
+ @Override
+ public void onConfigChange() {
+ super.onConfigChange();
+
+ if (mRtmpClient != null) {
+ mRtmpClient.restartVideoEncoder();
+ }
+ }
+
+ @Override
+ protected VideoQuality getVideoQuality() {
+ if (mRtmpClient != null) {
+ VideoEncoder videoEncoder = mRtmpClient.getVideoEncoder();
+ if (videoEncoder != null) {
+ return videoEncoder.getVideoQuality();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected AudioQuality getAudioQuality() {
+ if (mRtmpClient != null) {
+ AudioEncoder audioEncoder = mRtmpClient.getAudioEncoder();
+ if (audioEncoder != null) {
+ return audioEncoder.getAudioQuality();
+ }
+ }
+ return null;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractRTSPPreviewServer.java
new file mode 100644
index 0000000000..bcbeb0b4bc
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractRTSPPreviewServer.java
@@ -0,0 +1,219 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.content.Context;
+import android.util.Log;
+
+import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality;
+import org.deviceconnect.android.libmedia.streaming.rtsp.RtspServer;
+import org.deviceconnect.android.libmedia.streaming.rtsp.session.RtspSession;
+import org.deviceconnect.android.libmedia.streaming.rtsp.session.audio.AudioStream;
+import org.deviceconnect.android.libmedia.streaming.rtsp.session.audio.MicAACLATMStream;
+import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.VideoStream;
+import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
+
+import java.io.IOException;
+
+public abstract class AbstractRTSPPreviewServer extends AbstractPreviewServer {
+
+ /**
+ * マイムタイプを定義します.
+ */
+ private static final String MIME_TYPE = "video/x-rtp";
+
+ /**
+ * サーバー名を定義します.
+ */
+ private static final String SERVER_NAME = "Android Host RTSP Server";
+
+ /**
+ * RTSP 配信サーバ.d
+ */
+ private RtspServer mRtspServer;
+
+ public AbstractRTSPPreviewServer(Context context, MediaRecorder recorder) {
+ this(context, recorder, false);
+ }
+
+ public AbstractRTSPPreviewServer(Context context, MediaRecorder recorder, boolean useSSL) {
+ super(context, recorder, useSSL);
+ }
+
+ @Override
+ public String getUri() {
+ return "rtsp://localhost:" + getPort();
+ }
+
+ @Override
+ public String getMimeType() {
+ return MIME_TYPE;
+ }
+
+ @Override
+ public void startWebServer(final OnWebServerStartCallback callback) {
+ if (mRtspServer == null) {
+ mRtspServer = new RtspServer();
+ mRtspServer.setServerName(SERVER_NAME);
+ mRtspServer.setServerPort(getPort());
+ mRtspServer.setCallback(mCallback);
+ try {
+ mRtspServer.start();
+ } catch (Exception e) {
+ callback.onFail();
+ return;
+ }
+ }
+ callback.onStart(getUri());
+ }
+
+ @Override
+ public void stopWebServer() {
+ if (mRtspServer != null) {
+ mRtspServer.stop();
+ mRtspServer = null;
+ }
+ }
+
+ @Override
+ public boolean requestSyncFrame() {
+ RtspServer server = mRtspServer;
+ if (server != null) {
+ RtspSession session = server.getRtspSession();
+ if (session != null) {
+ VideoStream videoStream = session.getVideoStream();
+ if (videoStream != null) {
+ videoStream.getVideoEncoder().requestSyncKeyFrame();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public long getBPS() {
+ return mRtspServer != null ? mRtspServer.getBPS() : 0;
+ }
+
+ @Override
+ public void onConfigChange() {
+ super.onConfigChange();
+ restartVideoStream();
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ super.setMute(mute);
+
+ if (mRtspServer != null) {
+ RtspSession session = mRtspServer.getRtspSession();
+ if (session != null) {
+ AudioStream stream = session.getAudioStream();
+ if (stream != null) {
+ stream.setMute(mute);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected VideoQuality getVideoQuality() {
+ if (mRtspServer != null) {
+ RtspSession session = mRtspServer.getRtspSession();
+ if (session != null) {
+ VideoStream videoStream = session.getVideoStream();
+ if (videoStream != null) {
+ return videoStream.getVideoEncoder().getVideoQuality();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected AudioQuality getAudioQuality() {
+ if (mRtspServer != null) {
+ RtspSession session = mRtspServer.getRtspSession();
+ if (session != null) {
+ AudioStream audioStream = session.getAudioStream();
+ if (audioStream != null) {
+ return audioStream.getAudioEncoder().getAudioQuality();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * エンコーダに設定を反映し、再スタートします.
+ */
+ private void restartVideoStream() {
+ if (mRtspServer != null) {
+ RtspSession session = mRtspServer.getRtspSession();
+ if (session != null) {
+ session.restartVideoStream();
+ }
+ }
+ }
+
+ /**
+ * 映像用の VideoStream を作成します.
+ *
+ * @return VideoStream のインスタンス
+ */
+ protected VideoStream createVideoStream() {
+ return null;
+ }
+
+ /**
+ * 音声用の AudioStream を作成します.
+ *
+ * @return AudioStream のインスタンス
+ */
+ protected AudioStream createAudioStream() {
+ MediaRecorder recorder = getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+
+ if (settings.isAudioEnabled()) {
+ return new MicAACLATMStream(5004);
+ }
+ return null;
+ }
+
+ /**
+ * RtspServer からのイベントを受け取るためのコールバック.
+ *
+ *
+ * RTSP 配信の開始時と停止時に呼び出されます。
+ *
+ */
+ private final RtspServer.Callback mCallback = new RtspServer.Callback() {
+ @Override
+ public void createSession(RtspSession session) {
+ if (DEBUG) {
+ Log.d(TAG, "RtspServer.Callback#createSession()");
+ }
+
+ VideoStream videoStream = createVideoStream();
+ if (videoStream != null) {
+ setVideoQuality(videoStream.getVideoEncoder().getVideoQuality());
+ session.setVideoMediaStream(videoStream);
+ }
+
+ AudioStream audioStream = createAudioStream();
+ if (audioStream != null) {
+ AudioEncoder audioEncoder = audioStream.getAudioEncoder();
+ audioEncoder.setMute(isMuted());
+ setAudioQuality(audioEncoder.getAudioQuality());
+ session.setAudioMediaStream(audioStream);
+ }
+ }
+
+ @Override
+ public void releaseSession(RtspSession session) {
+ if (DEBUG) {
+ Log.d(TAG, "RtspServer.Callback#releaseSession()");
+ }
+ }
+ };
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractSRTBroadcaster.java
new file mode 100644
index 0000000000..b5bf3c7385
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractSRTBroadcaster.java
@@ -0,0 +1,174 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import org.deviceconnect.android.libmedia.streaming.MediaEncoderException;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality;
+import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
+import org.deviceconnect.android.libsrt.broadcast.SRTClient;
+
+public abstract class AbstractSRTBroadcaster extends AbstractBroadcaster {
+
+ /**
+ * RTMP 配信クライアント.
+ */
+ private SRTClient mSrtClient;
+
+ /**
+ * イベントを通知するためのリスナー.
+ */
+ private OnEventListener mOnBroadcasterEventListener;
+
+ public AbstractSRTBroadcaster(MediaRecorder recorder, String broadcastURI) {
+ super(recorder, broadcastURI);
+ }
+
+ /**
+ * SRT 配信に使用する VideoEncoder のインスタンスを作成します.
+ *
+ * @return SRT 配信に使用する VideoEncoder
+ */
+ protected VideoEncoder createVideoEncoder() {
+ return null;
+ }
+
+ /**
+ * SRT 配信に使用する AudioEncoder のインスタンスを作成します.
+ *
+ * @return SRT 配信に使用する AudioEncoder
+ */
+ protected AudioEncoder createAudioEncoder() {
+ MediaRecorder.Settings settings = getRecorder().getSettings();
+ if (settings.isAudioEnabled()) {
+ return new MicAACLATMEncoder();
+ }
+ return null;
+ }
+
+ @Override
+ public void setOnEventListener(OnEventListener listener) {
+ mOnBroadcasterEventListener = listener;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mSrtClient != null && mSrtClient.isRunning();
+ }
+
+ @Override
+ public void start(OnStartCallback callback) {
+ VideoEncoder videoEncoder = createVideoEncoder();
+ if (videoEncoder != null) {
+ setVideoQuality(videoEncoder.getVideoQuality());
+ }
+
+ AudioEncoder audioEncoder = createAudioEncoder();
+ if (audioEncoder != null) {
+ setAudioQuality(audioEncoder.getAudioQuality());
+ }
+
+ MediaRecorder.Settings settings = getRecorder().getSettings();
+
+ mSrtClient = new SRTClient(getBroadcastURI());
+ mSrtClient.setMaxRetryCount(settings.getRetryCount());
+ mSrtClient.setRetryInterval(settings.getRetryInterval());
+ mSrtClient.setVideoEncoder(videoEncoder);
+ mSrtClient.setAudioEncoder(audioEncoder);
+ mSrtClient.setOnEventListener(new SRTClient.OnEventListener() {
+ @Override
+ public void onStarted() {
+ if (callback != null) {
+ callback.onSuccess();
+ }
+
+ if (mOnBroadcasterEventListener != null) {
+ mOnBroadcasterEventListener.onStarted();
+ }
+ }
+
+ @Override
+ public void onStopped() {
+ if (mOnBroadcasterEventListener != null) {
+ mOnBroadcasterEventListener.onStopped();
+ }
+ }
+
+ @Override
+ public void onError(MediaEncoderException e) {
+ if (callback != null) {
+ callback.onFailed(e);
+ }
+
+ if (mOnBroadcasterEventListener != null) {
+ mOnBroadcasterEventListener.onError(e);
+ }
+ AbstractSRTBroadcaster.this.stop();
+ }
+
+ @Override
+ public void onConnected() {
+ }
+
+ @Override
+ public void onDisconnected() {
+ }
+
+ @Override
+ public void onNewBitrate(long bitrate) {
+ }
+ });
+ mSrtClient.start();
+ }
+
+ @Override
+ public void stop() {
+ if (mSrtClient != null) {
+ mSrtClient.stop();
+ mSrtClient = null;
+ }
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ if (mSrtClient != null) {
+ mSrtClient.setMute(mute);
+ }
+ }
+
+ @Override
+ public boolean isMute() {
+ return mSrtClient != null && mSrtClient.isMute();
+ }
+
+ @Override
+ public void onConfigChange() {
+ super.onConfigChange();
+
+ if (mSrtClient != null) {
+ mSrtClient.restartVideoEncoder();
+ }
+ }
+
+ @Override
+ protected VideoQuality getVideoQuality() {
+ if (mSrtClient != null) {
+ VideoEncoder videoEncoder = mSrtClient.getVideoEncoder();
+ if (videoEncoder != null) {
+ return videoEncoder.getVideoQuality();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected AudioQuality getAudioQuality() {
+ if (mSrtClient != null) {
+ AudioEncoder audioEncoder = mSrtClient.getAudioEncoder();
+ if (audioEncoder != null) {
+ return audioEncoder.getAudioQuality();
+ }
+ }
+ return null;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractSRTPreviewServer.java
new file mode 100644
index 0000000000..9c254a5a9e
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/AbstractSRTPreviewServer.java
@@ -0,0 +1,215 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.content.Context;
+import android.util.Log;
+
+import org.deviceconnect.android.deviceplugin.uvc.BuildConfig;
+import org.deviceconnect.android.deviceplugin.uvc.util.SRTSettings;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioEncoder;
+import org.deviceconnect.android.libmedia.streaming.audio.AudioQuality;
+import org.deviceconnect.android.libmedia.streaming.audio.MicAACLATMEncoder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
+import org.deviceconnect.android.libsrt.server.SRTServer;
+import org.deviceconnect.android.libsrt.server.SRTSession;
+
+import java.io.IOException;
+
+public abstract class AbstractSRTPreviewServer extends AbstractPreviewServer {
+ private static final boolean DEBUG = BuildConfig.DEBUG;
+ private static final String TAG = "CameraSRT";
+
+ /**
+ * プレビュー配信サーバのマイムタイプを定義.
+ */
+ private static final String MIME_TYPE = "video/MP2T";
+
+ /**
+ * SRTの設定.
+ */
+ private final SRTSettings mSettings;
+
+ /**
+ * SRT サーバ.
+ */
+ private SRTServer mSRTServer;
+
+ public AbstractSRTPreviewServer(Context context, MediaRecorder recorder) {
+ this(context, recorder, false);
+ }
+
+ public AbstractSRTPreviewServer(Context context, MediaRecorder recorder, boolean useSSL) {
+ super(context, recorder, useSSL);
+ mSettings = new SRTSettings(context);
+ }
+
+
+ @Override
+ public String getUri() {
+ return "srt://localhost:" + getPort();
+ }
+
+ @Override
+ public String getMimeType() {
+ return MIME_TYPE;
+ }
+
+ @Override
+ public void startWebServer(final OnWebServerStartCallback callback) {
+ if (mSRTServer == null) {
+ try {
+ mSRTServer = new SRTServer(getPort());
+// mSRTServer.setStatsInterval(BuildConfig.STATS_INTERVAL);
+ mSRTServer.setShowStats(DEBUG);
+ mSRTServer.setCallback(mCallback);
+ mSRTServer.setSocketOptions(mSettings.loadSRTSocketOptions());
+ mSRTServer.start();
+ } catch (Exception e) {
+ callback.onFail();
+ return;
+ }
+ }
+ callback.onStart(getUri());
+ }
+
+ @Override
+ public void stopWebServer() {
+ if (mSRTServer != null) {
+ mSRTServer.stop();
+ mSRTServer = null;
+ }
+ }
+
+ @Override
+ public boolean requestSyncFrame() {
+ SRTServer server = mSRTServer;
+ if (server != null) {
+ SRTSession session = server.getSRTSession();
+ if (session != null) {
+ VideoEncoder videoEncoder = session.getVideoEncoder();
+ if (videoEncoder != null) {
+ videoEncoder.requestSyncKeyFrame();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public long getBPS() {
+ // TODO
+ return 0;
+ }
+
+ @Override
+ public void onConfigChange() {
+ super.onConfigChange();
+ restartVideoEncoder();
+ }
+
+ @Override
+ public void setMute(boolean mute) {
+ super.setMute(mute);
+
+ if (mSRTServer != null) {
+ SRTSession session = mSRTServer.getSRTSession();
+ if (session != null) {
+ AudioEncoder audioEncoder = session.getAudioEncoder();
+ if (audioEncoder != null) {
+ audioEncoder.setMute(mute);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected VideoQuality getVideoQuality() {
+ if (mSRTServer != null) {
+ SRTSession session = mSRTServer.getSRTSession();
+ if (session != null) {
+ VideoEncoder videoEncoder = session.getVideoEncoder();
+ if (videoEncoder != null) {
+ return videoEncoder.getVideoQuality();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected AudioQuality getAudioQuality() {
+ if (mSRTServer != null) {
+ SRTSession session = mSRTServer.getSRTSession();
+ if (session != null) {
+ AudioEncoder audioEncoder = session.getAudioEncoder();
+ if (audioEncoder != null) {
+ return audioEncoder.getAudioQuality();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * エンコーダの設定を反映して、再スタートします.
+ */
+ private void restartVideoEncoder() {
+ if (mSRTServer != null) {
+ SRTSession session = mSRTServer.getSRTSession();
+ if (session != null) {
+ session.restartVideoEncoder();
+ }
+ }
+ }
+
+ /**
+ * SRT 用の映像エンコーダを作成します.
+ *
+ * @return SRT 用の映像エンコーダ
+ */
+ protected VideoEncoder createVideoEncoder() {
+ return null;
+ }
+
+ protected AudioEncoder createAudioEncoder() {
+ MediaRecorder recorder = getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+ if (settings.isAudioEnabled()) {
+ return new MicAACLATMEncoder();
+ }
+ return null;
+ }
+
+ /**
+ * SRTServer からのイベントを受け取るためのコールバック.
+ */
+ private final SRTServer.Callback mCallback = new SRTServer.Callback() {
+ @Override
+ public void createSession(final SRTSession session) {
+ if (DEBUG) {
+ Log.d(TAG, "SRTServer.Callback#createSession()");
+ }
+
+ VideoEncoder videoEncoder = createVideoEncoder();
+ if (videoEncoder != null) {
+ setVideoQuality(videoEncoder.getVideoQuality());
+ session.setVideoEncoder(videoEncoder);
+ }
+
+ AudioEncoder audioEncoder = createAudioEncoder();
+ if (audioEncoder != null) {
+ audioEncoder.setMute(isMuted());
+ setAudioQuality(audioEncoder.getAudioQuality());
+ session.setAudioEncoder(audioEncoder);
+ }
+ }
+
+ @Override
+ public void releaseSession(final SRTSession session) {
+ if (DEBUG) {
+ Log.d(TAG, "SRTServer.Callback#releaseSession()");
+ }
+ }
+ };
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/Broadcaster.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/Broadcaster.java
new file mode 100644
index 0000000000..8307f1461f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/Broadcaster.java
@@ -0,0 +1,100 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+public interface Broadcaster {
+ /**
+ * マイムタイプを取得します.
+ *
+ * @return マイムタイプ
+ */
+ String getMimeType();
+
+ /**
+ * ブロードキャスト先の URI を取得します.
+ *
+ * @return ブロードキャスト先の URI
+ */
+ String getBroadcastURI();
+
+ /**
+ * ブロードキャストのイベントを通知するリスナーを設定します.
+ *
+ * @param listener リスナー
+ */
+ void setOnEventListener(OnEventListener listener);
+
+ /**
+ * ブロードキャスト中か確認します.
+ *
+ * @return ブロードキャスト中の場合は true、それ以外は false
+ */
+ boolean isRunning();
+
+ /**
+ * ブロードキャストを開始します.
+ */
+ void start(OnStartCallback callback);
+
+ /**
+ * ブロードキャストを停止します.
+ */
+ void stop();
+
+ /**
+ * ミュート設定を行います.
+ *
+ * @param mute ミュートにする場合にはtrue、それ以外はfalse
+ */
+ void setMute(boolean mute);
+
+ /**
+ * ミュート設定を取得します.
+ *
+ * @return ミュートの場合はtrue、それ以外はfalse
+ */
+ boolean isMute();
+
+ /**
+ * 設定が変更されたことを通知します.
+ */
+ void onConfigChange();
+
+ /**
+ * ブロードキャストの開始結果を通知するコールバック.
+ */
+ interface OnStartCallback {
+ /**
+ * ブロードキャストに成功したことを通知します.
+ */
+ void onSuccess();
+
+ /**
+ * ブロードキャストに失敗したことを通知します.
+ *
+ * @param e 失敗原因の例外
+ */
+ void onFailed(Exception e);
+ }
+
+ /**
+ * ブロードキャストのイベントを通知するリスナー.
+ */
+ interface OnEventListener {
+
+ /**
+ * ブロードキャストが開始されたことを通知します.
+ */
+ void onStarted();
+
+ /**
+ * ブロードキャストが停止されたことを通知します.
+ */
+ void onStopped();
+
+ /**
+ * ブロードキャストでエラーが発生したことを通知します.
+ *
+ * @param e エラー原因の例外
+ */
+ void onError(Exception e);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/BroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/BroadcasterProvider.java
new file mode 100644
index 0000000000..e97d67de68
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/BroadcasterProvider.java
@@ -0,0 +1,73 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+public interface BroadcasterProvider {
+ /**
+ * ブロードキャスターのリストを取得します.
+ *
+ * @return ブロードキャスターのリスト
+ */
+ Broadcaster getBroadcaster();
+
+ /**
+ * ブロードキャスト中か確認します.
+ *
+ * @return ブロードキャスト中は true、それ以外は false
+ */
+ boolean isRunning();
+
+ /**
+ * ブロードキャスターを開始します.
+ *
+ * @param broadcastURI 配信先の URI
+ */
+ Broadcaster startBroadcaster(String broadcastURI);
+
+ /**
+ * ブロードキャスターを停止します.
+ */
+ void stopBroadcaster();
+
+ /**
+ * 設定が変更されたことを通知します.
+ */
+ void onConfigChange();
+
+ /**
+ * Recorder をミュート状態にする.
+ */
+ void setMute(boolean mute);
+
+ /**
+ * BroadcasterProvider で発生したイベントを通知するリスナーを設定します.
+ *
+ * @param listener リスナー
+ */
+ void setOnEventListener(OnEventListener listener);
+
+ /**
+ * イベントを通知するリスナー.
+ */
+ interface OnEventListener {
+ /**
+ * Broadcaster が開始されたことを通知します.
+ *
+ * @param broadcaster 開始した Broadcaster
+ */
+ void onStarted(Broadcaster broadcaster);
+
+ /**
+ * Broadcaster が停止されたことを通知します.
+ *
+ * @param broadcaster 停止した Broadcaster
+ */
+ void onStopped(Broadcaster broadcaster);
+
+ /**
+ * Broadcaster でエラーが発生したことを通知します.
+ *
+ * @param broadcaster エラーが発生した Broadcaster
+ * @param e エラー原因の例外
+ */
+ void onError(Broadcaster broadcaster, Exception e);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/MediaRecorder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/MediaRecorder.java
index bbaea1518c..9104126835 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/MediaRecorder.java
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/MediaRecorder.java
@@ -6,311 +6,1145 @@
*/
package org.deviceconnect.android.deviceplugin.uvc.recorder;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.Size;
-import org.deviceconnect.android.deviceplugin.uvc.recorder.preview.PreviewServer;
+import org.deviceconnect.android.deviceplugin.uvc.util.CapabilityUtil;
+import org.deviceconnect.android.deviceplugin.uvc.util.PropertyUtil;
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+import java.util.ArrayList;
import java.util.List;
+import javax.net.ssl.SSLContext;
+
/**
* メディアレコーダのインターフェース.
*
* @author NTT DOCOMO, INC.
*/
public interface MediaRecorder {
+ /**
+ * MediaRecorder で使用するデフォルトのマイムタイプ.
+ */
+ String MIME_TYPE_JPEG = "image/jpeg";
/**
- * レコーダの初期化処理を行います.
+ * MediaRecorder の初期化処理を行います.
*/
void initialize();
/**
- * レコーダの後始末処理を行います.
+ * プロセス起動時の状態に戻す.
+ *
+ * プラグイン再起動時に呼び出すこと.
*/
void clean();
/**
- * レコーダのIDを取得します.
+ * オブジェクトを破棄する.
+ *
+ * プロセス終了時に呼び出すこと.
+ */
+ void destroy();
+
+ /**
+ * MediaRecorder を識別する ID を取得します.
*
- * @return レコーダID
+ * @return MediaRecorder を識別する ID
*/
String getId();
/**
- * レコーダ名を取得します.
+ * MediaRecorder の名前を取得します.
*
- * @return レコーダ名
+ * @return MediaRecorder の名前
*/
String getName();
/**
- * レコーダに設定されているマイムタイプを取得します.
+ * MediaRecorder のマイムタイプを取得します.
*
* @return マイムタイプ
*/
String getMimeType();
/**
- * レコーダにマイムタイプを設定します.
+ * MediaRecorder の状態を取得します.
*
- * @param mimeType マイムタイプ
+ * @return MediaRecorder の状態
*/
- void setMimeType(final String mimeType);
+ State getState();
/**
- * レコーダの状態を取得します.
+ * HostMediaRecorder の設定を取得します.
*
- * @return レコーダの状態
+ * @return HostMediaRecorder の設定
*/
- State getState();
+ Settings getSettings();
/**
- * レコーダに設定されている写真サイズを取得します.
+ * プレビュー配信サーバの管理クラスを取得します.
*
- * @return 写真サイズ
+ * @return プレビュー配信サーバ
*/
- Size getPictureSize();
+ PreviewServerProvider getServerProvider();
/**
- * レコーダに写真サイズを設定します.
+ * プレビュー配信管理クラスを取得します.
*
- * @param size 写真サイズ
+ * @return BroadcasterProvider の実装クラス
*/
- void setPictureSize(Size size);
+ BroadcasterProvider getBroadcasterProvider();
/**
- * レコーダに設定されているプレビューサイズを取得します.
+ * プレビュー配信サーバの動作状況を確認します.
*
- * @return プレビューサイズ
+ * @return プレビュー配信サーバが動作している場合はtrue、それ以外はfalse
*/
- Size getPreviewSize();
+ boolean isPreviewRunning();
/**
- * レコーダにプレビューサイズを設定します.
+ * プレビュー配信サーバを開始します.
+ *
+ * 開始できなかった場合には、空のリストを返します。
*
- * @param size プレビューサイズ
+ * @return 開始したプレビュー配信サーバのリスト
*/
- void setPreviewSize(Size size);
+ List startPreview();
/**
- * レコーダに設定されているフレームレートを取得します.
- *
- * @return フレームレート
+ * プレビュー配信サーバを停止します.
*/
- double getMaxFrameRate();
+ void stopPreview();
/**
- * レコーダにフレームレートを設定します.
+ * ブロードキャストの動作状況を確認します.
*
- * @param frameRate フレームレート
+ * @return ブロードキャストされている場合はtrue、それ以外はfalse
*/
- void setMaxFrameRate(double frameRate);
+ boolean isBroadcasterRunning();
/**
- * レコーダに設定されているビットレートを取得します.
+ * ブロードキャストを開始します.
*
- * @return ビットレート
+ * @param broadcastURI ブロードキャスト先のURI
+ * @return ブロードキャストしているクラス
*/
- int getPreviewBitRate();
+ Broadcaster startBroadcaster(String broadcastURI);
/**
- * レコーダにビットレートを設定します.
- *
- * @param bitRate ビットレート
+ * ブロードキャストを停止します.
*/
- void setPreviewBitRate(int bitRate);
+ void stopBroadcaster();
/**
- * サポートしている写真サイズを取得します.
+ * 描画用オブジェクトを取得します.
*
- * @return サポートしている写真サイズ
+ * @return EGLSurfaceDrawingThread のインスタンス
*/
- List getSupportedPictureSizes();
+ EGLSurfaceDrawingThread getSurfaceDrawingThread();
/**
- * サポートしているプレビューサイズを取得します.
+ * ミュート設定を行います.
*
- * @return サポートしているプレビューサイズ
+ * @param mute ミュート設定
*/
- List getSupportedPreviewSizes();
+ void setMute(boolean mute);
/**
- * サポートしているマイムタイプを取得します.
+ * ミュート設定を取得します.
*
- * @return サポートしているマイムタイプ
+ * @return ミュートの場合はtrue、それ以外はfalse
*/
- List getSupportedMimeTypes();
+ boolean isMute();
/**
- * 写真撮影を行います.
- *
- * @param listener 写真撮影のイベントを通知するリスナー
+ * 設定が変更されたことを通知します.
*/
- void takePhoto(OnPhotoEventListener listener);
+ void onConfigChange();
/**
- * プレビュー用のサーバを取得します.
+ * SSL コンテキストの設定を行います.
*
- * @return プレビュー用サーバ
+ * @param sslContext SSL コンテキスト
*/
- List getServers();
+ void setSSLContext(SSLContext sslContext);
/**
- * プレビューを開始します.
- * サーバが起動できなかった場合には、空のリストを返却する。
- * @return 起動したプレビュー配信サーバのリスト
+ * パーミッションの要求結果を通知するコールバックを設定します.
+ *
+ * @param callback コールバック
*/
- List startPreview();
+ void requestPermission(PermissionCallback callback);
/**
- * プレビューを停止します.
+ * イベントを通知するためのリスナーを設定します.
+ *
+ * @param listener リスナー
*/
- void stopPreview();
+ void setOnEventListener(OnEventListener listener);
/**
- * プレビューが廃止されているか.
- * @return
+ * レコーダで映像を配信したデータの BPS を取得します.
+ *
+ * @return レコーダで映像を配信したデータの BPS
*/
- boolean isStartedPreview();
+ long getBPS();
/**
- * {@link #takePhoto(OnPhotoEventListener)} のイベントを受け取るリスナー.
+ * パーミッション結果通知用コールバック.
*/
- interface OnPhotoEventListener {
+ interface PermissionCallback {
/**
- * 写真が取られた時のイベントを通知します.
- *
- * @param uri 写真へのURI
- * @param filePath 写真へのパス
+ * 許可された場合に呼び出されます.
*/
- void onTakePhoto(String uri, String filePath);
+ void onAllowed();
/**
- * 写真撮影に失敗した時のイベントを通知します.
- *
- * @param errorMessage エラーメッセージ
+ * 拒否された場合に呼び出されます.
*/
- void onFailedTakePhoto(String errorMessage);
+ void onDisallowed();
}
/**
- * レコーダの状態.
+ * MediaRecorder の状態.
*/
enum State {
/**
- * アイドル中.
+ * 動作していない.
*/
- INACTTIVE,
+ INACTIVE,
/**
- * 一時停止中.
+ * 録画が一時停止中の状態.
*/
PAUSED,
/**
- * レコーディング中.
+ * 録画・静止画撮影中の状態.
*/
RECORDING,
/**
- * エラー中.
+ * プレビューが表示されている状態.
+ */
+ PREVIEW,
+
+ /**
+ * エラーで停止している状態.
*/
ERROR
}
/**
- * レコーダで使用するサイズ.
+ * HostMediaRecorder のイベントを通知するリスナー.
+ */
+ interface OnEventListener {
+ /**
+ * レコーダの設定が変更されたことを通知します.
+ */
+ void onConfigChanged();
+
+ /**
+ * プレビュー配信を開始した時に呼び出されます.
+ *
+ * @param servers 開始したプレビュー配信サーバ
+ */
+ void onPreviewStarted(List servers);
+
+ /**
+ * プレビュー配信を停止した時に呼び出されます.
+ */
+ void onPreviewStopped();
+
+ /**
+ * プレビュー配信でエラーが発生したときに呼び出されます.
+ *
+ * @param e エラー原因の例外
+ */
+ void onPreviewError(Exception e);
+
+ /**
+ * ブロードキャストを開始した時に呼び出されます.
+ *
+ * @param broadcaster 開始したブロードキャスト
+ */
+ void onBroadcasterStarted(Broadcaster broadcaster);
+
+ /**
+ * ブロードキャストを停止した時に呼び出されます.
+ *
+ * @param broadcaster 停止したブロードキャスト
+ */
+ void onBroadcasterStopped(Broadcaster broadcaster);
+
+ /**
+ * ブロードキャストでエラーが発生したときに呼び出されます.
+ *
+ * @param broadcaster エラーが発生した Broadcaster
+ * @param e エラー原因の例外
+ */
+ void onBroadcasterError(Broadcaster broadcaster, Exception e);
+
+ /**
+ * レコーダで発生したエラーを通知します.
+ *
+ * @param e エラー原因の例外
+ */
+ void onError(Exception e);
+ }
+
+ enum AudioSource {
+ DEFAULT("default"),
+ MIC("mic"),
+ APP("app");
+
+ private final String mSource;
+
+ AudioSource(String source) {
+ mSource = source;
+ }
+
+ public String getValue() {
+ return mSource;
+ }
+
+ public static AudioSource typeOf(String source) {
+ for (AudioSource audioSource : values()) {
+ if (audioSource.mSource.equalsIgnoreCase(source)) {
+ return audioSource;
+ }
+ }
+ return null;
+ }
+ }
+
+ enum VideoEncoderName {
+ H264("h264", "video/avc"),
+ H265("h265", "video/hevc");
+
+ private final String mName;
+ private final String mMimeType;
+
+ VideoEncoderName(String name, String mimeType) {
+ mName = name;
+ mMimeType = mimeType;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ public static VideoEncoderName nameOf(String name) {
+ for (VideoEncoderName encoder : values()) {
+ if (encoder.getName().equalsIgnoreCase(name)) {
+ return encoder;
+ }
+ }
+ return H264;
+ }
+ }
+
+ class ProfileLevel {
+ private final int mProfile;
+ private final int mLevel;
+
+ public ProfileLevel(int profile, int level) {
+ mProfile = profile;
+ mLevel = level;
+ }
+
+ public int getProfile() {
+ return mProfile;
+ }
+
+ public int getLevel() {
+ return mLevel;
+ }
+ }
+
+ /**
+ * HostMediaRecorder の設定を保持するクラス.
*/
- class Size implements Parcelable {
+ abstract class Settings {
+ private final PropertyUtil mPref;
+
+ public Settings(Context context, String name) {
+ mPref = new PropertyUtil(context, name);
+ }
+
/**
- * 横幅.
+ * 初期化されているか確認します.
+ *
+ * @return 初期化されている場合はtrue、それ以外はfalse
*/
- private final int mWidth;
+ public boolean isInitialized() {
+ return mPref.getString("test", null) != null;
+ }
/**
- * 縦幅.
+ * 初期化完了を書き込みます.
*/
- private final int mHeight;
+ public void finishInitialization() {
+ mPref.put("test", "test");
+ }
/**
- * コンストラクタ.
- * @param w 横幅
- * @param h 縦幅
+ * 保存データを初期化します.
*/
- public Size(final int w, final int h) {
- mWidth = w;
- mHeight = h;
+ public void clear() {
+ mPref.clear();
}
/**
- * コンストラクタ.
- * @param in Parcelのストリーム
+ * 写真サイズを取得します.
+ *
+ * @return 写真サイズ
*/
- private Size(final Parcel in) {
- this(in.readInt(), in.readInt());
+ public Size getPictureSize() {
+ return mPref.getSize("picture_size_width", "picture_size_height");
}
/**
- * 横幅を取得します.
+ * 写真サイズを設定します.
+ *
+ * サポートされていない写真サイズの場合は IllegalArgumentException を発生させます。
*
- * @return 横幅
+ * @param pictureSize 写真サイズ
*/
- public int getWidth() {
- return mWidth;
+ public void setPictureSize(Size pictureSize) {
+ if (!isSupportedPictureSize(pictureSize)) {
+ throw new IllegalArgumentException("pictureSize is not supported.");
+ }
+ mPref.put(
+ "picture_size_width",
+ "picture_size_height",
+ pictureSize);
}
/**
- * 縦幅を取得します.
+ * プレビューサイズを取得します.
*
- * @return 縦幅
+ * @return プレビューサイズ
*/
- public int getHeight() {
- return mHeight;
+ public Size getPreviewSize() {
+ return mPref.getSize("preview_size_width", "preview_size_height");
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * プレビューサイズを設定します.
+ *
+ * サポートされていないプレビューサイズの場合は IllegalArgumentException を発生させます。
+ *
+ * @param previewSize プレビューサイズ
+ */
+ public void setPreviewSize(Size previewSize) {
+ if (!isSupportedPreviewSize(previewSize)) {
+ throw new IllegalArgumentException("previewSize is not supported.");
+ }
+ mPref.put("preview_size_width", "preview_size_height", previewSize);
}
- @Override
- public void writeToParcel(final Parcel out, final int flags) {
- out.writeInt(mWidth);
- out.writeInt(mHeight);
+ /**
+ * フレームレートを取得します.
+ *
+ * @return フレームレート
+ */
+ public int getPreviewMaxFrameRate() {
+ return mPref.getInteger("preview_framerate", 30);
}
- public static final Creator CREATOR = new Creator() {
- @Override
- public Size createFromParcel(Parcel in) {
- return new Size(in);
+ /**
+ * フレームレートを設定します.
+ *
+ * @param previewMaxFrameRate フレームレート
+ */
+ public void setPreviewMaxFrameRate(Integer previewMaxFrameRate) {
+ if (previewMaxFrameRate <= 0) {
+ throw new IllegalArgumentException("previewMaxFrameRate is zero or negative.");
}
- @Override
- public Size[] newArray(int size) {
- return new Size[size];
+ mPref.put("preview_framerate", previewMaxFrameRate);
+ }
+
+ /**
+ * ビットレートを取得します.
+ *
+ * @return ビットレート(byte)
+ */
+ public int getPreviewBitRate() {
+ return mPref.getInteger("preview_bitrate", 2 * 1024 * 1024);
+ }
+
+ /**
+ * ビットレートを設定します.
+ *
+ * @param previewBitRate ビットレート(byte)
+ */
+ public void setPreviewBitRate(int previewBitRate) {
+ if (previewBitRate <= 0) {
+ throw new IllegalArgumentException("previewBitRate is zero or negative.");
}
- };
+ mPref.put("preview_bitrate", String.valueOf(previewBitRate));
+ }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ /**
+ * キーフレームインターバルを取得します.
+ *
+ * @return キーフレームを発行する間隔(ミリ秒)
+ */
+ public int getPreviewKeyFrameInterval() {
+ return mPref.getInteger("preview_i_frame_interval", 1);
+ }
- Size size = (Size) o;
+ /**
+ * キーフレームインターバルを設定します.
+ *
+ * @param previewKeyFrameInterval キーフレームを発行する間隔(ミリ秒)
+ */
+ public void setPreviewKeyFrameInterval(int previewKeyFrameInterval) {
+ if (previewKeyFrameInterval <= 0) {
+ throw new IllegalArgumentException("previewKeyFrameInterval is zero or negative.");
+ }
+ mPref.put("preview_i_frame_interval", previewKeyFrameInterval);
+ }
- return mWidth == size.mWidth && mHeight == size.mHeight;
+ /**
+ * プレビュー配信エンコード名を取得します.
+ *
+ * @return エンコード名
+ */
+ public VideoEncoderName getPreviewEncoderName() {
+ return VideoEncoderName.nameOf(getPreviewEncoder());
}
- @Override
- public int hashCode() {
- int result = mWidth;
- result = 31 * result + mHeight;
- return result;
+ /**
+ * プレビューの配信エンコードの名前を取得します.
+ *
+ * 未設定の場合は h264 を返却します。
+ *
+ * @return プレビューの配信エンコードの名前
+ */
+ public String getPreviewEncoder() {
+ return mPref.getString("preview_encoder", "h264");
+ }
+
+ /**
+ * プレビューの配信エンコードの名前を設定します.
+ *
+ * @param encoder プレビューの配信エンコードの名前
+ */
+ public void setPreviewEncoder(String encoder) {
+ if (encoder == null) {
+ mPref.remove("preview_encoder");
+ } else {
+ if (!isSupportedVideoEncoder(encoder)) {
+ throw new IllegalArgumentException("encoder is not supported.");
+ }
+ mPref.put("preview_encoder", encoder);
+ }
+ }
+
+ /**
+ * ソフトウェアエンコーダを優先的に使用するフラグを確認します.
+ *
+ * @return ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false
+ */
+ public boolean isUseSoftwareEncoder() {
+ return mPref.getBoolean("preview_use_software_encoder", false);
}
- @Override
- public String toString() {
- return "(width = " + getWidth() + ", height = " + getHeight() + ")";
+ /**
+ * ソフトウェアエンコーダを優先的に使用するフラグを設定します.
+ *
+ * @param used ソフトウェアエンコーダを優先的に使用する場合は true、それ以外は false
+ */
+ public void setUseSoftwareEncoder(boolean used) {
+ mPref.put("preview_use_software_encoder", used);
+ }
+
+ /**
+ * イントラリフレッシュのフレーム数を取得します.
+ *
+ * @return イントラリフレッシュのフレーム数
+ */
+ public Integer getIntraRefresh() {
+ return mPref.getInteger("preview_intra_refresh", 0);
+ }
+
+ /**
+ * イントラリフレッシュのフレーム数を設定します.
+ *
+ * @param refresh イントラリフレッシュのフレーム数
+ */
+ public void setIntraRefresh(Integer refresh) {
+ if (refresh == null) {
+ mPref.remove("preview_intra_refresh");
+ } else {
+ mPref.put("preview_intra_refresh", refresh);
+ }
+ }
+
+ /**
+ * プロファイルとレベルを取得します.
+ *
+ * 未設定の場合には、null を返却します。
+ *
+ * @return プロファイルとレベル
+ */
+ public ProfileLevel getProfileLevel() {
+ Integer profile = mPref.getInteger("preview_profile", null);
+ Integer level = mPref.getInteger("preview_level", null);
+ if (profile != null && level != null) {
+ return new ProfileLevel(profile, level);
+ }
+ return null;
+ }
+
+ /**
+ * プロファイルとレベルを設定します.
+ *
+ * null が設定された場合には、未設定にします。
+ *
+ * サポートされていないプロファイルとレベルが設定された場合には例外を発生します。
+ *
+ * @param pl プロファイルとレベル
+ */
+ public void setProfileLevel(ProfileLevel pl) {
+ if (pl == null) {
+ mPref.remove("preview_profile");
+ mPref.remove("preview_level");
+ } else {
+ if (!isSupportedProfileLevel(pl.getProfile(), pl.getLevel())) {
+ throw new IllegalArgumentException("profile and level are not supported.");
+ }
+ mPref.put("preview_profile", pl.getProfile());
+ mPref.put("preview_level", pl.getLevel());
+ }
+ }
+
+ /**
+ * 設定されているプロファイルを取得します.
+ *
+ * @return プロファイル
+ */
+ public Integer getProfile() {
+ return mPref.getInteger("preview_profile", 0);
+ }
+
+ /**
+ * 設定されているレベルを取得します.
+ *
+ * @return レベル
+ */
+ public Integer getLevel() {
+ return mPref.getInteger("preview_level", 0);
+ }
+
+ /**
+ * プレビューの品質を取得します.
+ *
+ * @return プレビューの品質
+ */
+ public int getPreviewQuality() {
+ return mPref.getInteger("preview_jpeg_quality", 80);
+ }
+
+ /**
+ * プレビューの品質を設定します.
+ *
+ * 0 から 100 の間で設定することができます。
+ * それ以外は例外が発生します。
+ *
+ * @param quality プレビューの品質
+ */
+ public void setPreviewQuality(int quality) {
+ if (quality < 0) {
+ throw new IllegalArgumentException("quality is negative value.");
+ }
+ if (quality > 100) {
+ throw new IllegalArgumentException("quality is over 100.");
+ }
+ mPref.put("preview_jpeg_quality", quality);
+ }
+
+ /**
+ * 切り抜き範囲を取得します.
+ *
+ * 範囲ば設定されていない場合には、null を返却します.
+ *
+ * @return 切り抜き範囲
+ */
+ public Rect getDrawingRange() {
+ return mPref.getRect("preview_clip_left",
+ "preview_clip_top",
+ "preview_clip_right",
+ "preview_clip_bottom");
+ }
+
+ /**
+ * 切り抜き範囲を設定します.
+ *
+ * 引数に null が指定された場合には、切り抜き範囲を削除します。
+ *
+ * @param rect 切り抜き範囲
+ */
+ public void setDrawingRange(Rect rect) {
+ if (rect == null) {
+ mPref.remove("preview_clip_left");
+ mPref.remove("preview_clip_top");
+ mPref.remove("preview_clip_right");
+ mPref.remove("preview_clip_bottom");
+ } else {
+ mPref.put(
+ "preview_clip_left",
+ "preview_clip_top",
+ "preview_clip_right",
+ "preview_clip_bottom",
+ rect);
+ }
+ }
+
+ /**
+ * サポートしている写真サイズを取得します.
+ *
+ * サポートしていない場合には空のリストを返却します。
+ *
+ * @return サポートしている写真サイズ
+ */
+ public List getSupportedPictureSizes() {
+ return new ArrayList<>();
+ }
+
+ /**
+ * サポートしているプレビューサイズを取得します.
+ *
+ * サポートしていない場合には空のリストを返却します。
+ *
+ * @return サポートしているプレビューサイズ
+ */
+ public List getSupportedPreviewSizes() {
+ return new ArrayList<>();
+ }
+
+ /**
+ * サポートしているエンコーダのリストを取得します.
+ *
+ * @return サポートしているエンコーダのリスト
+ */
+ public List getSupportedVideoEncoders() {
+ List list = new ArrayList<>();
+ List supported = CapabilityUtil.getSupportedVideoEncoders();
+ for (VideoEncoderName encoderName : VideoEncoderName.values()) {
+ if (supported.contains(encoderName.getMimeType())) {
+ list.add(encoderName.getName());
+ }
+ }
+ return list;
+ }
+
+ /**
+ * サポートしているプロファイル・レベルのリストを取得します.
+ *
+ * @return サポートしているプロファイル・レベルのリスト
+ */
+ public List getSupportedProfileLevel() {
+ VideoEncoderName encoderName = getPreviewEncoderName();
+ return CapabilityUtil.getSupportedProfileLevel(encoderName.getMimeType());
+ }
+
+ /**
+ * 指定されたサイズがサポートされているか確認します.
+ *
+ * @param size 確認するサイズ
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedPictureSize(final Size size) {
+ for (Size s : getSupportedPictureSizes()) {
+ if (s.getWidth() == size.getWidth()
+ && s.getHeight() == size.getHeight()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 指定されたサイズが静止画でサポートされているか確認します.
+ *
+ * @param width 横幅
+ * @param height 縦幅
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedPictureSize(int width, int height) {
+ return isSupportedPictureSize(new Size(width, height));
+ }
+
+ /**
+ * 指定されたサイズがプレビューでサポートされているか確認します.
+ *
+ * @param size 確認するサイズ
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedPreviewSize(Size size) {
+ for (Size s : getSupportedPreviewSizes()) {
+ if (s.getWidth() == size.getWidth()
+ && s.getHeight() == size.getHeight()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 指定されたサイズがプレビューでサポートされているか確認します.
+ *
+ * @param width 横幅
+ * @param height 縦幅
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedPreviewSize(int width, int height) {
+ return isSupportedPreviewSize(new Size(width, height));
+ }
+
+ /**
+ * 指定されたエンコーダがサポートされているか確認します.
+ *
+ * @param encoder エンコーダ名
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedVideoEncoder(String encoder) {
+ List encoderList = getSupportedVideoEncoders();
+ if (encoderList != null) {
+ for (String e : encoderList) {
+ if (e.equalsIgnoreCase(encoder)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 指定されたプロファイルとレベルがサポートされているか確認します.
+ *
+ * @param profile プロファイル
+ * @param level レベル
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedProfileLevel(int profile, int level) {
+ List list = getSupportedProfileLevel();
+ if (list != null) {
+ for (ProfileLevel pl : list) {
+ if (profile == pl.getProfile() && level == pl.getLevel()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // 音声
+
+ /**
+ * プレビュー音声が有効化確認します.
+ *
+ * @return プレビュー音声が有効の場合はtrue、それ以外はfalse
+ */
+ public boolean isAudioEnabled() {
+ return getPreviewAudioSource() != null;
+ }
+
+ /**
+ * プレビューの音声タイプを取得します.
+ *
+ * @return 音声タイプ
+ */
+ public AudioSource getPreviewAudioSource() {
+ return AudioSource.typeOf(mPref.getString("preview_audio_source", "none"));
+ }
+
+ /**
+ * プレビューの音声タイプを設定します.
+ *
+ * @param audioSource 音声タイプ
+ */
+ public void setPreviewAudioSource(AudioSource audioSource) {
+ if (audioSource == null) {
+ mPref.put("preview_audio_source", "none");
+ } else {
+ mPref.put("preview_audio_source", audioSource.mSource);
+ }
+ }
+
+ /**
+ * プレビュー音声のビットレートを取得します.
+ *
+ * @return プレビュー音声のビットレート
+ */
+ public int getPreviewAudioBitRate() {
+ return mPref.getInteger("preview_audio_bitrate", 64 * 1024);
+ }
+
+ /**
+ * プレビュー音声のビットレートを設定します.
+ *
+ * @param bitRate プレビュー音声のビットレート
+ */
+ public void setPreviewAudioBitRate(int bitRate) {
+ if (bitRate <= 0) {
+ throw new IllegalArgumentException("previewAudioBitRate is zero or negative value.");
+ }
+ mPref.put("preview_audio_bitrate", bitRate);
+ }
+
+ /**
+ * プレビュー音声のサンプルレートを取得します.
+ *
+ * @return プレビュー音声のサンプルレート
+ */
+ public int getPreviewSampleRate() {
+ return mPref.getInteger("preview_audio_sample_rate", 8000);
+ }
+
+ /**
+ * プレビュー音声のサンプルレートを設定します.
+ *
+ * @param sampleRate プレビュー音声のサンプルレート
+ */
+ public void setPreviewSampleRate(int sampleRate) {
+ mPref.put("preview_audio_sample_rate", sampleRate);
+ }
+
+ /**
+ * プレビュー音声のチャンネル数を取得します.
+ *
+ * @return プレビュー音声のチャンネル数
+ */
+ public int getPreviewChannel() {
+ return mPref.getInteger("preview_audio_channel", 1);
+ }
+
+ /**
+ * プレビュー音声のチャンネル数を設定します.
+ *
+ * @param channel プレビュー音声のチャンネル数
+ */
+ public void setPreviewChannel(int channel) {
+ mPref.put("preview_audio_channel", channel);
+ }
+
+ /**
+ * プレビュー配信のエコーキャンセラーを取得します.
+ *
+ * @return プレビュー配信のエコーキャンセラー
+ */
+ public boolean isUseAEC() {
+ return mPref.getBoolean("preview_audio_aec", true);
+ }
+
+ /**
+ * プレビュー配信のエコーキャンセラーを設定します.
+ *
+ * @param used プレビュー配信のエコーキャンセラー
+ */
+ public void setUseAEC(boolean used) {
+ mPref.put("preview_audio_aec", used);
+ }
+
+ /**
+ * プレビューの音声ミュート設定を確認します.
+ *
+ * @return ミュートの場合はtrue、それ以外の場合はfalse
+ */
+ public boolean isMute() {
+ return mPref.getBoolean("preview_audio_mute", false);
+ }
+
+ /**
+ * プレビューの音声ミュート設定を行います.
+ *
+ * @param mute ミュートにする場合はtrue、それ以外はfalse
+ */
+ public void setMute(boolean mute) {
+ mPref.put("preview_audio_mute", mute);
+ }
+
+ public boolean isSupportedAudioSource(AudioSource source) {
+ List sourceList = getSupportedAudioSource();
+ if (sourceList != null) {
+ for (AudioSource s : sourceList) {
+ if (s == source) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * サポートしている音声入力のリストを取得します.
+ *
+ * @return サポートしている音声入力のリスト
+ */
+ public List getSupportedAudioSource() {
+ List list = new ArrayList<>();
+ for (AudioSource audioSource : AudioSource.values()) {
+ if (audioSource == AudioSource.APP) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ list.add(audioSource);
+ }
+ } else {
+ list.add(audioSource);
+ }
+ }
+ return list;
+ }
+
+ // 配信
+
+ /**
+ * 配信先の URI を取得します.
+ *
+ * 設定されていない場合は null を返却します.
+ *
+ * @return 配信先の URI
+ */
+ public String getBroadcastURI() {
+ return mPref.getString("broadcast_uri", null);
+ }
+
+ /**
+ * 配信先の URI を設定します.
+ *
+ * @param broadcastURI 配信先の URI
+ */
+ public void setBroadcastURI(String broadcastURI) {
+ mPref.put("broadcast_uri", broadcastURI);
+ }
+
+ /**
+ * リトライ回数を取得します.
+ *
+ * @return リトライ回数
+ */
+ public int getRetryCount() {
+ return mPref.getInteger("broadcast_retry_count", 0);
+ }
+
+ /**
+ * リトライ回数を設定します.
+ *
+ * @param count リトライ回数
+ */
+ public void setRetryCount(int count) {
+ if (count < 0) {
+ mPref.remove("broadcast_retry_count");
+ } else {
+ mPref.put("broadcast_retry_count", count);
+ }
+ }
+
+ /**
+ * リトライのインターバルを取得します.
+ *
+ * @return リトライのインターバル
+ */
+ public int getRetryInterval() {
+ return mPref.getInteger("broadcast_retry_interval", 3000);
+ }
+
+ /**
+ * リトライのインターバルを設定します.
+ *
+ * @param interval リトライのインターバル
+ */
+ public void setRetryInterval(int interval) {
+ if (interval < 0) {
+ mPref.remove("broadcast_retry_interval");
+ } else {
+ mPref.put("broadcast_retry_interval", interval);
+ }
+ }
+
+ // ポート番号
+
+ /**
+ * Motion JPEG サーバ用のポート番号を取得します.
+ *
+ * @return Motion JPEG サーバ用のポート番号
+ */
+ public Integer getMjpegPort() {
+ return mPref.getInteger("mjpeg_port", 0);
+ }
+
+ /**
+ * Motion JPEG サーバ用のポート番号を設定します.
+ *
+ * @param port Motion JPEG サーバ用のポート番号
+ */
+ public void setMjpegPort(int port) {
+ mPref.put("mjpeg_port", port);
+ }
+
+ /**
+ * SSL で暗号化された Motion JPEG サーバ用のポート番号を取得します.
+ *
+ * @return Motion JPEG サーバ用のポート番号
+ */
+ public Integer getMjpegSSLPort() {
+ return mPref.getInteger("mjpeg_ssl_port", 0);
+ }
+
+ /**
+ * SSL で暗号化された Motion JPEG サーバ用のポート番号を取得します.
+ *
+ * @param port Motion JPEG サーバ用のポート番号
+ */
+ public void setMjpegSSLPort(int port) {
+ mPref.put("mjpeg_ssl_port", port);
+ }
+
+ /**
+ * RTSP サーバ用のポート番号を取得します.
+ *
+ * @return RTSP サーバ用のポート番号
+ */
+ public Integer getRtspPort() {
+ return mPref.getInteger("rtsp_port", 0);
+ }
+
+ /**
+ * RTSP サーバ用のポート番号を設定します.
+ *
+ * @param port RTSP サーバ用のポート番号
+ */
+ public void setRtspPort(int port) {
+ mPref.put("rtsp_port", port);
+ }
+
+ /**
+ * SRT サーバ用のポート番号を取得します.
+ *
+ * @return SRT サーバ用のポート番号
+ */
+ public Integer getSrtPort() {
+ return mPref.getInteger("srt_port", 0);
+ }
+
+ /**
+ * SRT サーバ用のポート番号を設定します.
+ *
+ * @param port SRT サーバ用のポート番号
+ */
+ public void setSrtPort(int port) {
+ mPref.put("srt_port", port);
}
}
}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/MediaRecorderManager.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/MediaRecorderManager.java
new file mode 100644
index 0000000000..290bfa00b8
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/MediaRecorderManager.java
@@ -0,0 +1,453 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.h264.UvcH264Recorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.mjpeg.UvcMjpgRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uncompressed.UvcUncompressedRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+import org.deviceconnect.android.libmedia.streaming.util.WeakReferenceList;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaRecorderManager {
+ /**
+ * レコーディング停止アクションを定義.
+ */
+ public static final String ACTION_STOP_RECORDING = "org.deviceconnect.android.deviceplugin.uvc.STOP_RECORDING";
+
+ /**
+ * プレビュー停止用アクションを定義.
+ */
+ public static final String ACTION_STOP_PREVIEW = "org.deviceconnect.android.deviceplugin.uvc.STOP_PREVIEW";
+
+ /**
+ * ブロードキャスト停止用アクションを定義.
+ */
+ public static final String ACTION_STOP_BROADCAST = "org.deviceconnect.android.deviceplugin.uvc.STOP_BROADCAST";
+
+ /**
+ * レコーダのIDを格納するためのキーを定義.
+ */
+ public static final String KEY_RECORDER_ID = "recorder_id";
+
+ /**
+ * プレビュー停止・レコーディング停止・ブロードキャスト停止・画面の回転イベントなどを受け取るための BroadcastReceiver.
+ */
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_STOP_PREVIEW.equals(action)) {
+ stopPreviewServer(intent.getStringExtra(KEY_RECORDER_ID));
+ } else if (ACTION_STOP_RECORDING.equals(action)) {
+ stopRecording(intent.getStringExtra(KEY_RECORDER_ID));
+ } else if (ACTION_STOP_BROADCAST.equals(action)) {
+ stopBroadcast(intent.getStringExtra(KEY_RECORDER_ID));
+ }
+ }
+ };
+
+ /**
+ * レコーダのリスト.
+ */
+ private final List mUvcRecorderList = new ArrayList<>();
+ private final Context mContext;
+
+ /**
+ * 各レコーダのイベントを通知するためのリスナー.
+ */
+ private final WeakReferenceList mOnEventListeners = new WeakReferenceList<>();
+
+ /**
+ * コンストラクタ.
+ *
+ * @param context コンテキスト
+ */
+ public MediaRecorderManager(Context context, UVCCamera uvcCamera) {
+ mContext = context;
+ initRecorders(context, uvcCamera);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_STOP_PREVIEW);
+ filter.addAction(ACTION_STOP_RECORDING);
+ filter.addAction(ACTION_STOP_BROADCAST);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ /**
+ * 後始末を行います.
+ */
+ public void destroy() {
+ try {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ } catch (Exception e) {
+ // ignore.
+ }
+
+ mOnEventListeners.clear();
+
+ for (MediaRecorder recorder : mUvcRecorderList) {
+ try {
+ recorder.destroy();
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ mUvcRecorderList.clear();
+ }
+
+ /**
+ * レコーダが使用できるか確認します.
+ *
+ * @param recorder レコーダ
+ * @return 使用できる場合はtrue、それ以外はfalse
+ */
+ public boolean canUseRecorder(UvcRecorder recorder) {
+ for (UvcRecorder uvcRecorder : getUvcRecorderList()) {
+ if (uvcRecorder == recorder) {
+ continue;
+ }
+ if (uvcRecorder.isPreviewRunning() || uvcRecorder.isBroadcasterRunning() ||
+ uvcRecorder.getState() == MediaRecorder.State.RECORDING) {
+ // カメラが使用中
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 使用するレコーダ以外のレコーダを停止します。
+ *
+ * プレビュー配信中などの停止できない場合には、停止しません。
+ * その場合には、カメラを使用できないので注意が必要になります。
+ *
+ * @param useRecorder 使用するレコーダ
+ */
+ public void stopCameraRecorder(MediaRecorder useRecorder) {
+ for (MediaRecorder recorder : getUvcRecorderList()) {
+ if (recorder == useRecorder) {
+ continue;
+ }
+
+ if (recorder instanceof UvcRecorder) {
+ if (!recorder.isPreviewRunning() && !recorder.isBroadcasterRunning() &&
+ recorder.getState() != MediaRecorder.State.RECORDING) {
+ // 強制的にカメラを停止
+ EGLSurfaceDrawingThread drawingThread = recorder.getSurfaceDrawingThread();
+ drawingThread.stop(true);
+
+ // カメラの処理は別スレッドで行われているので、ここで少し待機します
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * レコーダのリストを取得します.
+ *
+ * @return レコーダ
+ */
+ public List getUvcRecorderList() {
+ return mUvcRecorderList;
+ }
+
+ /**
+ * レコーダ ID を指定してレコーダを取得します.
+ *
+ * レコーダが見つからない場合は null を返却します。
+ *
+ * @param id レコーダID
+ * @return レコーダ
+ */
+ public UvcRecorder findUvcRecorderById(String id) {
+ if (id != null) {
+ for (UvcRecorder recorder : getUvcRecorderList()) {
+ if (id.equalsIgnoreCase(recorder.getId())) {
+ return recorder;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * デフォルトのレコーダを取得します.
+ *
+ * レコーダが存在しない場合には null を返却します。
+ *
+ * @return デフォルトのレコーダ
+ */
+ public UvcRecorder getDefaultRecorder() {
+ if (mUvcRecorderList.isEmpty()) {
+ return null;
+ }
+ return mUvcRecorderList.get(0);
+ }
+
+ /**
+ * レコーダの初期化処理を行います.
+ *
+ * @param context コンテキスト
+ * @param camera UVC デバイス
+ */
+ private void initRecorders(Context context, UVCCamera camera) {
+ boolean hasMJPEG = false;
+ boolean hasH264 = false;
+ boolean hasUncompressed = false;
+ try {
+ List parameters = camera.getParameter();
+ for (Parameter p : parameters) {
+ switch (p.getFrameType()) {
+ case UNCOMPRESSED:
+ hasUncompressed = true;
+ break;
+ case MJPEG:
+ hasMJPEG = true;
+ if (p.hasExtH264()) {
+ // Extension Unit を持っている場合に H264 として使用できる。
+ hasH264 = true;
+ }
+ break;
+ case H264:
+ hasH264 = true;
+ break;
+ }
+ }
+ } catch (IOException e) {
+ // ignore.
+ }
+
+ if (hasMJPEG) {
+ addUvcRecorder(new UvcMjpgRecorder(context, camera));
+ }
+
+ if (hasH264) {
+ addUvcRecorder(new UvcH264Recorder(context, camera));
+ }
+
+ if (hasUncompressed) {
+ addUvcRecorder(new UvcUncompressedRecorder(context, camera));
+ }
+
+ for (MediaRecorder recorder : mUvcRecorderList) {
+ recorder.initialize();
+ }
+ }
+
+ /**
+ * UvcRecorder をリストに追加します.
+ *
+ * @param mediaRecorder 追加するレコーダ
+ */
+ private void addUvcRecorder(UvcRecorder mediaRecorder) {
+ mediaRecorder.setOnEventListener(new MediaRecorder.OnEventListener() {
+ @Override
+ public void onConfigChanged() {
+ postOnConfigChanged(mediaRecorder);
+ }
+
+ @Override
+ public void onPreviewStarted(List servers) {
+ postOnPreviewStarted(mediaRecorder, servers);
+ }
+
+ @Override
+ public void onPreviewStopped() {
+ postOnPreviewStopped(mediaRecorder);
+ }
+
+ @Override
+ public void onPreviewError(Exception e) {
+ postOnPreviewError(mediaRecorder, e);
+ }
+
+ @Override
+ public void onBroadcasterStarted(Broadcaster broadcaster) {
+ postOnBroadcasterStarted(mediaRecorder, broadcaster);
+ }
+
+ @Override
+ public void onBroadcasterStopped(Broadcaster broadcaster) {
+ postOnBroadcasterStopped(mediaRecorder, broadcaster);
+ }
+
+ @Override
+ public void onBroadcasterError(Broadcaster broadcaster, Exception e) {
+ postOnBroadcasterError(mediaRecorder, broadcaster, e);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ postOnError(mediaRecorder, e);
+ }
+ });
+ mUvcRecorderList.add(mediaRecorder);
+ }
+
+ /**
+ * 指定された ID のレコーダのプレビューを停止します.
+ *
+ * @param id レコーダの ID
+ */
+ private void stopPreviewServer(final String id) {
+ MediaRecorder recorder = findUvcRecorderById(id);
+ if (recorder != null) {
+ recorder.stopPreview();
+ }
+ }
+
+ /**
+ * 指定された ID のレコードのブロードキャストを停止します.
+ *
+ * @param id レコードの ID
+ */
+ private void stopBroadcast(final String id) {
+ MediaRecorder recorder = findUvcRecorderById(id);
+ if (recorder != null) {
+ recorder.stopBroadcaster();
+ }
+ }
+
+ /**
+ * 指定された ID のレコードの録音・録画を停止します.
+ *
+ * @param id レコードの ID
+ */
+ private void stopRecording(final String id) {
+ }
+
+ /**
+ * イベント通知用のリスナーを追加します.
+ *
+ * @param listener 追加するリスナー
+ */
+ public void addOnEventListener(OnEventListener listener) {
+ mOnEventListeners.add(listener);
+ }
+
+ /**
+ * イベント通知用のリスナーを削除します.
+ *
+ * @param listener 削除するリスナー
+ */
+ public void removeOnEventListener(OnEventListener listener) {
+ mOnEventListeners.remove(listener);
+ }
+
+ private void postOnConfigChanged(MediaRecorder recorder) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onConfigChanged(recorder);
+ }
+ }
+
+ private void postOnPreviewStarted(MediaRecorder recorder, List servers) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onPreviewStarted(recorder, servers);
+ }
+ }
+
+ private void postOnPreviewStopped(MediaRecorder recorder) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onPreviewStopped(recorder);
+ }
+ }
+
+ private void postOnPreviewError(MediaRecorder recorder, Exception e) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onPreviewError(recorder, e);
+ }
+ }
+
+ private void postOnBroadcasterStarted(MediaRecorder recorder, Broadcaster broadcaster) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onBroadcasterStarted(recorder, broadcaster);
+ }
+ }
+
+ private void postOnBroadcasterStopped(MediaRecorder recorder, Broadcaster broadcaster) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onBroadcasterStopped(recorder, broadcaster);
+ }
+ }
+
+ private void postOnBroadcasterError(MediaRecorder recorder, Broadcaster broadcaster, Exception e) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onBroadcasterError(recorder, broadcaster, e);
+ }
+ }
+
+// private void postOnTakePhoto(MediaRecorder recorder, String uri, String filePath, String mimeType) {
+// for (OnEventListener l : mOnEventListeners) {
+// l.onTakePhoto(recorder, uri, filePath, mimeType);
+// }
+// }
+//
+// private void postOnRecordingStarted(MediaRecorder recorder, String fileName) {
+// for (OnEventListener l : mOnEventListeners) {
+// l.onRecordingStarted(recorder, fileName);
+// }
+// }
+//
+// private void postOnRecordingPause(MediaRecorder recorder) {
+// for (OnEventListener l : mOnEventListeners) {
+// l.onRecordingPause(recorder);
+// }
+// }
+//
+// private void postOnRecordingResume(MediaRecorder recorder) {
+// for (OnEventListener l : mOnEventListeners) {
+// l.onRecordingResume(recorder);
+// }
+// }
+//
+// private void postOnRecordingStopped(MediaRecorder recorder, String fileName) {
+// for (OnEventListener l : mOnEventListeners) {
+// l.onRecordingStopped(recorder, fileName);
+// }
+// }
+
+ private void postOnError(MediaRecorder recorder, Exception e) {
+ for (OnEventListener l : mOnEventListeners) {
+ l.onError(recorder, e);
+ }
+ }
+
+ public interface OnEventListener {
+ void onConfigChanged(MediaRecorder recorder);
+
+ void onPreviewStarted(MediaRecorder recorder, List servers);
+ void onPreviewStopped(MediaRecorder recorder);
+ void onPreviewError(MediaRecorder recorder, Exception e);
+
+ void onBroadcasterStarted(MediaRecorder recorder, Broadcaster broadcaster);
+ void onBroadcasterStopped(MediaRecorder recorder, Broadcaster broadcaster);
+ void onBroadcasterError(MediaRecorder recorder, Broadcaster broadcaster, Exception e);
+
+// void onTakePhoto(MediaRecorder recorder, String uri, String filePath, String mimeType);
+//
+// void onRecordingStarted(MediaRecorder recorder, String fileName);
+// void onRecordingPause(MediaRecorder recorder);
+// void onRecordingResume(MediaRecorder recorder);
+// void onRecordingStopped(MediaRecorder recorder, String fileName);
+
+ void onError(MediaRecorder recorder, Exception e);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/PreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/PreviewServer.java
new file mode 100644
index 0000000000..aba34d3d39
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/PreviewServer.java
@@ -0,0 +1,122 @@
+/*
+ PreviewServer.java
+ Copyright (c) 2017 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import javax.net.ssl.SSLContext;
+
+/**
+ * プレビュー配信用サーバを定義するインターフェース.
+ */
+public interface PreviewServer {
+ /**
+ * サーバが配信するプレビューのマイムタイプを取得します.
+ *
+ * @return マイムタイプ
+ */
+ String getMimeType();
+
+ /**
+ * サーバへの URL を取得します.
+ *
+ * @return サーバへの URL
+ */
+ String getUri();
+
+ /**
+ * プレビュー配信サーバのポート番号を取得します.
+ *
+ * @return ポート番号
+ */
+ int getPort();
+
+ /**
+ * プレビュー配信サーバのポート番号を設定します.
+ *
+ * @param port ポート番号
+ */
+ void setPort(int port);
+
+ /**
+ * サーバを開始します.
+ *
+ * @param callback 開始結果を通知するコールバック
+ */
+ void startWebServer(OnWebServerStartCallback callback);
+
+ /**
+ * サーバを停止します.
+ */
+ void stopWebServer();
+
+ /**
+ * 設定が変更されたことを通知します.
+ */
+ void onConfigChange();
+
+ /**
+ * Recorder をミュート状態にする.
+ */
+ void setMute(boolean mute);
+
+ /**
+ * Recorder のミュート状態を返す.
+ * @return mute状態
+ */
+ boolean isMuted();
+
+ /**
+ * 映像のエンコーダーに対して sync frame の即時生成を要求する.
+ *
+ * @return 即時生成を受け付けた場合はtrue, そうでない場合はfalse
+ */
+ boolean requestSyncFrame();
+
+ /**
+ * SSLContext を使用するかどうかのフラグを返す.
+ *
+ * @return SSLContext を使用する場合はtrue, そうでない場合はfalse
+ */
+ boolean useSSLContext();
+
+ /**
+ * SSL コンテキストの設定を行います.
+ *
+ * @param sslContext SSL コンテキスト
+ */
+ void setSSLContext(SSLContext sslContext);
+
+ /**
+ * SSL コンテキストを取得します.
+ *
+ * @return SSL コンテキスト
+ */
+ SSLContext getSSLContext();
+
+ /**
+ * プレビューサーバから配信したデータの BPS を取得します.
+ *
+ * @return プレビューサーバから配信したデータの BPS
+ */
+ long getBPS();
+
+ /**
+ * Callback interface used to receive the result of starting a web server.
+ */
+ interface OnWebServerStartCallback {
+ /**
+ * Called when a web server successfully started.
+ *
+ * @param uri An ever-updating, static image URI.
+ */
+ void onStart(String uri);
+
+ /**
+ * Called when a web server failed to start.
+ */
+ void onFail();
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/PreviewServerProvider.java
new file mode 100644
index 0000000000..8cb5e0751f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/PreviewServerProvider.java
@@ -0,0 +1,113 @@
+/*
+ PreviewServerProvider.java
+ Copyright (c) 2017 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.deviceplugin.uvc.recorder;
+
+import java.util.List;
+
+public interface PreviewServerProvider {
+ /**
+ * プレビューで配信するマイムタイプを取得します.
+ *
+ * @return プレビューで配信するマイムタイプ
+ */
+ List getSupportedMimeType();
+
+ /**
+ * サポートしているプレビュー配信サーバを追加します.
+ *
+ * @param server 追加するプレビュー配信サーバ
+ */
+ void addServer(PreviewServer server);
+
+ /**
+ * サポートしているプレビュー配信用サーバのリストを取得します.
+ * @return プレビュー配信用サーバのリスト
+ */
+ List getServers();
+
+ /**
+ * 指定されたマイムタイプに対応するプレビュー配信サーバを取得します.
+ *
+ *
+ * マイムタイプに対応したプレビュー配信サーバが存在しない場合は null を返却します。
+ *
+ *
+ * @param mimeType マイムタイプ
+ * @return プレビュー配信サーバ
+ */
+ PreviewServer getServerByMimeType(String mimeType);
+
+ /**
+ * プレビューサーバが動作している確認します.
+ *
+ * @return 動作中の場合は true、それ以外は false
+ */
+ boolean isRunning();
+
+ /**
+ * 全てのプレビュー配信サーバを開始します.
+ *
+ * レスポンスのリストが空の場合には、全てのプレビュー配信サーバの起動に失敗しています。
+ *
+ * @return 起動に成功したプレビュー配信サーバのリスト
+ */
+ List startServers();
+
+ /**
+ * 全てのプレビュー配信サーバを停止します.
+ */
+ void stopServers();
+
+ /**
+ * 全てのサーバの映像のエンコーダーに対して sync frame の即時生成を要求する.
+ *
+ * @return 実際に即時生成を受け付けたサーバのリスト
+ */
+ List requestSyncFrame();
+
+ /**
+ * 設定が変更されたことを通知します.
+ */
+ void onConfigChange();
+
+ /**
+ * Recorder をミュート状態にする.
+ */
+ void setMute(boolean mute);
+
+ /**
+ * イベントを通知するリスナーを設定します.
+ *
+ * @param listener リスナー
+ */
+ void setOnEventListener(OnEventListener listener);
+
+ /**
+ * プレビュー配信サーバのイベントを通知するリスナー.
+ */
+ interface OnEventListener {
+ /**
+ * プレビュー配信サーバを開始したことを通知します.
+ *
+ * @param servers 開始したサーバのリスト
+ */
+ void onStarted(List servers);
+
+ /**
+ * プレビュー配信サーバを停止したことを通知します.
+ */
+ void onStopped();
+
+ /**
+ * プレビュー配信サーバでエラーが発生したことを通知します.
+ *
+ * @param server エラーが発生したサーバ
+ * @param e エラー原因の例外
+ */
+ void onError(PreviewServer server, Exception e);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/UVCRecorder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/UVCRecorder.java
deleted file mode 100644
index a21eb06114..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/UVCRecorder.java
+++ /dev/null
@@ -1,195 +0,0 @@
-package org.deviceconnect.android.deviceplugin.uvc.recorder;
-
-import android.util.Log;
-
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDevice;
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDeviceManager;
-import org.deviceconnect.android.deviceplugin.uvc.recorder.preview.MJPEGPreviewServer;
-import org.deviceconnect.android.deviceplugin.uvc.recorder.preview.PreviewServer;
-import org.deviceconnect.android.deviceplugin.uvc.recorder.preview.RTSPPreviewServer;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import androidx.annotation.NonNull;
-
-public class UVCRecorder implements MediaRecorder {
-
- private static final String RECORDER_ID = "0";
- private static final String RECORDER_MIME_TYPE_MJPEG = "video/x-mjpeg";
-
- private final List mPreviewServers = new ArrayList<>();
- private final UVCDeviceManager mDeviceMgr;
- private UVCDevice mDevice;
- /**
- * コンストラクタ.
- * @param manager ファイル管理クラス
- * @param device UVCカメラ
- */
- public UVCRecorder(final UVCDeviceManager manager, final UVCDevice device) {
- mDeviceMgr = manager;
- mDevice = device;
- }
-
- public void setDevice(UVCDevice device) {
- mDevice = device;
- }
-
- @Override
- public void initialize() {
- mPreviewServers.clear();
- mPreviewServers.add(new MJPEGPreviewServer(false, mDeviceMgr, mDevice, 40000));
- mPreviewServers.add(new MJPEGPreviewServer(true, mDeviceMgr, mDevice, 41000));
- mPreviewServers.add(new RTSPPreviewServer(mDeviceMgr, mDevice, 40001));
- }
-
- @Override
- public void clean() {
- stopPreview();
- }
-
- @Override
- public String getId() {
- return RECORDER_ID;
- }
-
- @Override
- public String getName() {
- return mDevice.getName();
- }
-
- @Override
- public String getMimeType() {
- return RECORDER_MIME_TYPE_MJPEG;
- }
-
- @Override
- public void setMimeType(String mimeType) {
- }
-
- @Override
- public State getState() {
- return null;
- }
-
- @Override
- public Size getPictureSize() {
- return null;
- }
-
- @Override
- public void setPictureSize(Size size) {
- }
-
- @Override
- public Size getPreviewSize() {
- return new Size(mDevice.getPreviewWidth(), mDevice.getPreviewHeight());
- }
-
- @Override
- public void setPreviewSize(Size size) {
- mDevice.setPreviewSize(size.getWidth(), size.getHeight());
- }
-
- @Override
- public double getMaxFrameRate() {
- return mDevice.getFrameRate();
- }
-
- @Override
- public void setMaxFrameRate(double frameRate) {
- mDevice.setPreviewFrameRate(frameRate);
- }
-
- @Override
- public int getPreviewBitRate() {
- return 0;
- }
-
- @Override
- public void setPreviewBitRate(int bitRate) {
- }
-
- @Override
- public List getSupportedPictureSizes() {
- List result = new ArrayList<>();
- for (UVCDevice.PreviewOption option : mDevice.getPreviewOptions()) {
- result.add(new Size(option.getWidth(), option.getHeight()));
- }
- return result;
- }
-
- @Override
- public List getSupportedPreviewSizes() {
- List result = new ArrayList<>();
- for (UVCDevice.PreviewOption option : mDevice.getPreviewOptions()) {
- result.add(new Size(option.getWidth(), option.getHeight()));
- }
- return result;
- }
-
- @Override
- public List getSupportedMimeTypes() {
- List result = new ArrayList<>();
- for (PreviewServer server : mPreviewServers) {
- result.add(server.getMimeType());
- }
- return result;
- }
-
- @Override
- public void takePhoto(OnPhotoEventListener listener) {
- }
-
- @Override
- public List getServers() {
- return mPreviewServers;
- }
-
- @Override
- public List startPreview() {
- List results = new ArrayList<>();
-
- CountDownLatch lock = new CountDownLatch(mPreviewServers.size());
- for (PreviewServer server : mPreviewServers) {
- server.start(new PreviewServer.OnWebServerStartCallback() {
- @Override
- public void onStart(@NonNull String uri) {
- results.add(server);
- lock.countDown();
- }
-
- @Override
- public void onFail() {
- lock.countDown();
- }
- });
- }
- try {
- lock.await(5, TimeUnit.SECONDS);
- } catch (Exception e) {
- // ignore.
- }
-
- return results;
- }
-
- @Override
- public void stopPreview() {
- for (PreviewServer server : mPreviewServers) {
- server.stop();
- }
- }
-
- @Override
- public boolean isStartedPreview() {
- for (PreviewServer server : mPreviewServers) {
- if (server.isStarted()) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/h264/UvcH264Recorder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/h264/UvcH264Recorder.java
new file mode 100644
index 0000000000..c4284f89f1
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/h264/UvcH264Recorder.java
@@ -0,0 +1,148 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.h264;
+
+import android.content.Context;
+import android.util.Size;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
+import org.deviceconnect.android.libuvc.FrameType;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UvcH264Recorder extends UvcRecorder {
+ private static final String RECORDER_ID = "1";
+ private static final String RECORDER_NAME = "h264";
+ private static final String RECORDER_MIME_TYPE_MJPEG = "video/x-mjpeg";
+
+ public UvcH264Recorder(Context context, UVCCamera camera) {
+ super(context, camera);
+ }
+
+ @Override
+ protected UvcSettings createSettings() {
+ H264Settings settings = new H264Settings(getContext());
+ if (!settings.isInitialized()) {
+ List supportPictureSizes = settings.getSupportedPictureSizes();
+ List supportPreviewSizes = settings.getSupportedPreviewSizes();
+
+ settings.setPictureSize(supportPictureSizes.get(0));
+ settings.setPreviewSize(supportPreviewSizes.get(0));
+ settings.setPreviewBitRate(2 * 1024 * 1024);
+ settings.setPreviewMaxFrameRate(30);
+ settings.setPreviewKeyFrameInterval(1);
+ settings.setPreviewQuality(80);
+
+ settings.setPreviewAudioSource(null);
+ settings.setPreviewAudioBitRate(64 * 1024);
+ settings.setPreviewSampleRate(16000);
+ settings.setPreviewChannel(1);
+ settings.setUseAEC(true);
+
+ settings.setMjpegPort(11001);
+ settings.setMjpegSSLPort(11101);
+ settings.setRtspPort(12001);
+ settings.setSrtPort(13001);
+
+ settings.finishInitialization();
+ }
+ return settings;
+ }
+
+ @Override
+ public String getId() {
+ return RECORDER_ID;
+ }
+
+ @Override
+ public String getName() {
+ return RECORDER_NAME;
+ }
+
+ @Override
+ public String getMimeType() {
+ return RECORDER_MIME_TYPE_MJPEG;
+ }
+
+ public class H264Settings extends UvcSettings {
+ H264Settings(Context context) {
+ super(context);
+ }
+
+ @Override
+ public List getSupportedPictureSizes() {
+ List sizes = new ArrayList<>();
+ try {
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.H264) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ } else if (p.getFrameType() == FrameType.MJPEG) {
+ if (p.hasExtH264()) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ }
+ }
+ }
+ } catch (IOException e) {
+ // ignore.
+ }
+ return sizes;
+ }
+
+ @Override
+ public List getSupportedPreviewSizes() {
+ List sizes = new ArrayList<>();
+ try {
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.H264) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ } else if (p.getFrameType() == FrameType.MJPEG) {
+ if (p.hasExtH264()) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ }
+ }
+ }
+ } catch (IOException e) {
+ // ignore.
+ }
+ return sizes;
+ }
+
+ /**
+ * 設定されているパラメータに一致するパラメータを取得します.
+ *
+ * @return パラメータ
+ * @throws IOException カメラ情報の取得に失敗した場合に例外が発生
+ */
+ public Parameter getParameter() throws IOException {
+ Parameter parameter = null;
+ Size previewSize = getPreviewSize();
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.H264) {
+ if (p.getWidth() == previewSize.getWidth() && p.getHeight() == previewSize.getHeight()) {
+ parameter = p;
+ break;
+ }
+ }
+ }
+
+ if (parameter == null) {
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.MJPEG && p.hasExtH264()) {
+ if (p.getWidth() == previewSize.getWidth() && p.getHeight() == previewSize.getHeight()) {
+ parameter = p;
+ parameter.setUseH264(true);
+ break;
+ }
+ }
+ }
+ }
+
+ return parameter;
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/mjpeg/UvcMjpgRecorder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/mjpeg/UvcMjpgRecorder.java
new file mode 100644
index 0000000000..1ef28ddd9f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/mjpeg/UvcMjpgRecorder.java
@@ -0,0 +1,128 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.mjpeg;
+
+import android.content.Context;
+import android.util.Size;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
+import org.deviceconnect.android.libuvc.FrameType;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UvcMjpgRecorder extends UvcRecorder {
+ private static final String RECORDER_ID = "0";
+ private static final String RECORDER_NAME = "mjpeg";
+ private static final String RECORDER_MIME_TYPE_MJPEG = "video/x-mjpeg";
+
+ public UvcMjpgRecorder(Context context, UVCCamera camera) {
+ super(context, camera);
+ }
+
+ @Override
+ protected UvcSettings createSettings() {
+ MjpegSettings settings = new MjpegSettings(getContext());
+ if (!settings.isInitialized()) {
+ List supportPictureSizes = settings.getSupportedPictureSizes();
+ List supportPreviewSizes = settings.getSupportedPreviewSizes();
+
+ settings.setPictureSize(supportPictureSizes.get(0));
+ settings.setPreviewSize(supportPreviewSizes.get(0));
+
+ settings.setPreviewBitRate(2 * 1024 * 1024);
+ settings.setPreviewMaxFrameRate(30);
+ settings.setPreviewKeyFrameInterval(1);
+ settings.setPreviewQuality(80);
+
+ settings.setPreviewAudioSource(null);
+ settings.setPreviewAudioBitRate(64 * 1024);
+ settings.setPreviewSampleRate(16000);
+ settings.setPreviewChannel(1);
+ settings.setUseAEC(true);
+
+ settings.setMjpegPort(11000);
+ settings.setMjpegSSLPort(11100);
+ settings.setRtspPort(12000);
+ settings.setSrtPort(13000);
+
+ settings.finishInitialization();
+ }
+ return settings;
+ }
+
+ @Override
+ public String getId() {
+ return RECORDER_ID;
+ }
+
+ @Override
+ public String getName() {
+ return RECORDER_NAME;
+ }
+
+ @Override
+ public String getMimeType() {
+ return RECORDER_MIME_TYPE_MJPEG;
+ }
+
+ public class MjpegSettings extends UvcSettings {
+ MjpegSettings(Context context) {
+ super(context);
+ }
+
+ @Override
+ public List getSupportedPictureSizes() {
+ List sizes = new ArrayList<>();
+ try {
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.MJPEG) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ }
+ }
+ } catch (IOException e) {
+ // ignore.
+ }
+ return sizes;
+ }
+
+ @Override
+ public List getSupportedPreviewSizes() {
+ List sizes = new ArrayList<>();
+ try {
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.MJPEG) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ }
+ }
+ } catch (IOException e) {
+ // ignore.
+ }
+ return sizes;
+ }
+
+ /**
+ * 設定されているパラメータに一致するパラメータを取得します.
+ *
+ * @return パラメータ
+ * @throws IOException カメラ情報の取得に失敗した場合に例外が発生
+ */
+ public Parameter getParameter() throws IOException {
+ Parameter parameter = null;
+ Size previewSize = getPreviewSize();
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.MJPEG) {
+ if (p.getWidth() == previewSize.getWidth() && p.getHeight() == previewSize.getHeight()) {
+ parameter = p;
+ parameter.setUseH264(false);
+ }
+ }
+ }
+ return parameter;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/MJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/MJPEGPreviewServer.java
deleted file mode 100644
index 11ced68a0c..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/MJPEGPreviewServer.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package org.deviceconnect.android.deviceplugin.uvc.recorder.preview;
-
-import com.serenegiant.usb.UVCCamera;
-
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDevice;
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDeviceManager;
-import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGEncoder;
-import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGServer;
-
-import java.io.IOException;
-import java.net.Socket;
-
-import javax.net.ssl.SSLContext;
-
-public class MJPEGPreviewServer implements PreviewServer {
-
- private static final String SERVER_NAME = "UVC Plugin MotionJPEG Server";
-
- private final UVCDeviceManager mDeviceMgr;
- private final UVCDevice mDevice;
- private MJPEGServer mServer;
- protected int mPort;
- /**
- * SSLContext を使用するかどうかのフラグ.
- */
- private boolean mUsesSSLContext;
- /**
- * SSLContext のインスタンス.
- */
- private SSLContext mSSLContext;
- public MJPEGPreviewServer(final boolean isSSL, final UVCDeviceManager mgr, final UVCDevice device, final int port) {
- mDeviceMgr = mgr;
- mDevice = device;
- mPort = port;
- mUsesSSLContext = isSSL;
- }
-
- @Override
- public String getUrl() {
- return mServer == null ? null : mServer.getUri();
- }
-
- @Override
- public String getMimeType() {
- return "video/x-mjpeg";
- }
-
- @Override
- public boolean isStarted() {
- return mServer != null;
- }
-
- @Override
- public boolean usesSSLContext() {
- return mUsesSSLContext;
- }
-
- @Override
- public void setSSLContext(SSLContext sslContext) {
- mSSLContext = sslContext;
- }
-
- @Override
- public SSLContext getSSLContext() {
- return mSSLContext;
- }
-
- @Override
- public void start(final OnWebServerStartCallback callback) {
- if (mServer == null) {
- SSLContext sslContext = getSSLContext();
- if (usesSSLContext() && sslContext == null) {
- callback.onFail();
- return;
- }
- mServer = new MJPEGServer();
- if (sslContext != null) {
- mServer.setSSLContext(sslContext);
- }
- mServer.setServerName(SERVER_NAME);
- mServer.setServerPort(mPort);
- mServer.setCallback(mCallback);
- try {
- mServer.start();
- } catch (IOException e) {
- callback.onFail();
- return;
- }
- }
- callback.onStart(mServer.getUri());
- }
-
- @Override
- public void stop() {
- if (mServer != null) {
- mServer.stop();
- mServer = null;
- }
- }
-
- protected MJPEGServer.Callback mCallback = new MJPEGServer.Callback() {
- @Override
- public boolean onAccept(Socket socket) {
- return true;
- }
-
- @Override
- public void onClosed(Socket socket) {
- }
-
- @Override
- public MJPEGEncoder createMJPEGEncoder() {
- return new UVCEncoder();
- }
-
- @Override
- public void releaseMJPEGEncoder(MJPEGEncoder encoder) {
- }
- };
-
- private class UVCEncoder extends MJPEGEncoder implements UVCDeviceManager.PreviewListener {
- @Override
- public void start() {
- boolean isStarted = mDevice.startPreview();
- if (isStarted) {
- mDeviceMgr.addPreviewListener(this);
- }
- }
-
- @Override
- public void stop() {
- mDeviceMgr.removePreviewListener(this);
- }
-
- // UVCDeviceManager.PreviewListener
-
- @Override
- public void onFrame(UVCDevice device, byte[] frame, int frameFormat, int width, int height) {
- if (frameFormat != UVCCamera.FRAME_FORMAT_MJPEG) {
- return;
- }
- postJPEG(frame);
- }
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/PreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/PreviewServer.java
deleted file mode 100644
index 1119cc7a6b..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/PreviewServer.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- PreviewServer.java
- Copyright (c) 2017 NTT DOCOMO,INC.
- Released under the MIT license
- http://opensource.org/licenses/mit-license.php
- */
-package org.deviceconnect.android.deviceplugin.uvc.recorder.preview;
-
-
-import javax.net.ssl.SSLContext;
-
-public interface PreviewServer {
-
- String getUrl();
-
- String getMimeType();
-
- /**
- * サーバを開始します.
- * @param callback 開始結果を通知するコールバック
- */
- void start(OnWebServerStartCallback callback);
-
- /**
- * サーバを停止します.
- */
- void stop();
-
- /**
- * サーバが開始されているか確認します.
- *
- * @return サーバが開始されている場合はtrue、それ以外はfalse
- */
- boolean isStarted();
- /**
- * SSLContext を使用するかどうかのフラグを返す.
- *
- * @return SSLContext を使用する場合はtrue, そうでない場合はfalse
- */
- boolean usesSSLContext();
-
- void setSSLContext(SSLContext sslContext);
-
- SSLContext getSSLContext();
- /**
- * Callback interface used to receive the result of starting a web server.
- */
- interface OnWebServerStartCallback {
- /**
- * Called when a web server successfully started.
- *
- * @param uri An ever-updating, static image URI.
- */
- void onStart(String uri);
-
- /**
- * Called when a web server failed to start.
- */
- void onFail();
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/PreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/PreviewServerProvider.java
deleted file mode 100644
index 31c082a936..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/PreviewServerProvider.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.deviceconnect.android.deviceplugin.uvc.recorder.preview;
-
-
-import java.util.List;
-
-public interface PreviewServerProvider {
-
- List getServers();
-
- void stopAll();
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/RTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/RTSPPreviewServer.java
deleted file mode 100644
index 709fcf662e..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/preview/RTSPPreviewServer.java
+++ /dev/null
@@ -1,177 +0,0 @@
-package org.deviceconnect.android.deviceplugin.uvc.recorder.preview;
-
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDevice;
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDeviceManager;
-import org.deviceconnect.android.libmedia.streaming.rtsp.RtspServer;
-import org.deviceconnect.android.libmedia.streaming.rtsp.session.RtspSession;
-import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.H264VideoStream;
-import org.deviceconnect.android.libmedia.streaming.video.CanvasVideoEncoder;
-import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
-import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
-
-import java.io.IOException;
-
-import javax.net.ssl.SSLContext;
-
-public class RTSPPreviewServer implements PreviewServer {
-
- private static final String SERVER_NAME = "UVC Plugin RTSP Server";
-
- private RtspServer mServer;
- private UVCDeviceManager mDeviceMgr;
- private UVCDevice mDevice;
- private int mPort;
-
- public RTSPPreviewServer(final UVCDeviceManager mgr, final UVCDevice device, final int port) {
- mDeviceMgr = mgr;
- mDevice = device;
- mPort = port;
- }
-
- @Override
- public String getUrl() {
- return "rtsp://localhost:" + mPort;
- }
-
- @Override
- public String getMimeType() {
- return "video/x-rtp";
- }
-
- @Override
- public boolean isStarted() {
- return mServer != null;
- }
-
- @Override
- public boolean usesSSLContext() {
- return false;
- }
-
- @Override
- public void setSSLContext(SSLContext sslContext) {
-
- }
-
- @Override
- public SSLContext getSSLContext() {
- return null;
- }
-
- @Override
- public void start(final OnWebServerStartCallback callback) {
- if (mServer == null) {
- mServer = new RtspServer();
- mServer.setServerName(SERVER_NAME);
- mServer.setServerPort(mPort);
- mServer.setCallback(mCallback);
-
- try {
- mServer.start();
- } catch (IOException e) {
- callback.onFail();
- return;
- }
- }
-
- callback.onStart(getUrl());
- }
-
- @Override
- public void stop() {
- if (mServer != null) {
- mServer.stop();
- mServer = null;
- }
- }
-
- private final RtspServer.Callback mCallback = new RtspServer.Callback() {
- @Override
- public void createSession(RtspSession session) {
- UVCStream uvcStream = new UVCStream();
-
- VideoQuality videoQuality = uvcStream.getVideoEncoder().getVideoQuality();
- videoQuality.setVideoWidth(mDevice.getPreviewWidth());
- videoQuality.setVideoHeight(mDevice.getPreviewHeight());
- videoQuality.setBitRate(1024 * 1024);
- videoQuality.setFrameRate((int) mDevice.getFrameRate());
- videoQuality.setIFrameInterval(2);
-
- session.setVideoMediaStream(uvcStream);
- }
-
- @Override
- public void releaseSession(RtspSession session) {
-
- }
- };
-
- private class UVCStream extends H264VideoStream {
- private UVCEncoder mUVCEncoder;
-
- UVCStream() {
- mUVCEncoder = new UVCEncoder();
- }
-
- @Override
- public VideoEncoder getVideoEncoder() {
- return mUVCEncoder;
- }
- }
-
- private class UVCEncoder extends CanvasVideoEncoder implements UVCDeviceManager.PreviewListener {
- private Bitmap mBitmap;
- private Paint mPaint = new Paint();
-
- void setBitmap(Bitmap bitmap) {
- synchronized (this) {
- if (mBitmap != null) {
- mBitmap.recycle();
- mBitmap = null;
- }
- mBitmap = bitmap;
- }
- }
-
- @Override
- protected void onStartSurfaceDrawing() {
- super.onStartSurfaceDrawing();
-
- if (!mDevice.startPreview()) {
- // TODO
- }
- mDeviceMgr.addPreviewListener(this);
- }
-
- @Override
- protected void onStopSurfaceDrawing() {
- mDevice.stopPreview();
- mDeviceMgr.removePreviewListener(this);
-
- super.onStopSurfaceDrawing();
- }
-
- @Override
- public void draw(Canvas canvas, int width, int height) {
- synchronized (this) {
- if (mBitmap != null && !mBitmap.isRecycled()) {
- canvas.drawBitmap(mBitmap, 0, 0, mPaint);
- }
- }
- }
-
- @Override
- public void onFrame(UVCDevice device, byte[] frame, int frameFormat, int width, int height) {
- Bitmap bitmap = BitmapFactory.decodeByteArray(frame, 0, frame.length);
- if (bitmap != null) {
- setBitmap(bitmap);
- }
- }
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uncompressed/UvcUncompressedRecorder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uncompressed/UvcUncompressedRecorder.java
new file mode 100644
index 0000000000..e4cb12a9f7
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uncompressed/UvcUncompressedRecorder.java
@@ -0,0 +1,128 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uncompressed;
+
+import android.content.Context;
+import android.util.Size;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
+import org.deviceconnect.android.libuvc.FrameType;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UvcUncompressedRecorder extends UvcRecorder {
+ private static final String RECORDER_ID = "2";
+ private static final String RECORDER_NAME = "uncompressed";
+ private static final String RECORDER_MIME_TYPE_MJPEG = "video/x-mjpeg";
+
+ public UvcUncompressedRecorder(Context context, UVCCamera camera) {
+ super(context, camera);
+ }
+
+ @Override
+ protected UvcSettings createSettings() {
+ UncompressedSettings settings = new UncompressedSettings(getContext());
+ if (!settings.isInitialized()) {
+ List supportPictureSizes = settings.getSupportedPictureSizes();
+ List supportPreviewSizes = settings.getSupportedPreviewSizes();
+
+ settings.setPictureSize(supportPictureSizes.get(0));
+ settings.setPreviewSize(supportPreviewSizes.get(0));
+
+ settings.setPreviewBitRate(2 * 1024 * 1024);
+ settings.setPreviewMaxFrameRate(30);
+ settings.setPreviewKeyFrameInterval(1);
+ settings.setPreviewQuality(80);
+
+ settings.setPreviewAudioSource(null);
+ settings.setPreviewAudioBitRate(64 * 1024);
+ settings.setPreviewSampleRate(16000);
+ settings.setPreviewChannel(1);
+ settings.setUseAEC(true);
+
+ settings.setMjpegPort(11002);
+ settings.setMjpegSSLPort(11102);
+ settings.setRtspPort(12002);
+ settings.setSrtPort(13002);
+
+ settings.finishInitialization();
+ }
+ return settings;
+ }
+
+ @Override
+ public String getId() {
+ return RECORDER_ID;
+ }
+
+ @Override
+ public String getName() {
+ return RECORDER_NAME;
+ }
+
+ @Override
+ public String getMimeType() {
+ return RECORDER_MIME_TYPE_MJPEG;
+ }
+
+ public class UncompressedSettings extends UvcSettings {
+ UncompressedSettings(Context context) {
+ super(context);
+ }
+
+ @Override
+ public List getSupportedPictureSizes() {
+ List sizes = new ArrayList<>();
+ try {
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.UNCOMPRESSED) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ }
+ }
+ } catch (IOException e) {
+ // ignore.
+ }
+ return sizes;
+ }
+
+ @Override
+ public List getSupportedPreviewSizes() {
+ List sizes = new ArrayList<>();
+ try {
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.UNCOMPRESSED) {
+ sizes.add(new Size(p.getWidth(), p.getHeight()));
+ }
+ }
+ } catch (IOException e) {
+ // ignore.
+ }
+ return sizes;
+ }
+
+ /**
+ * 設定されているパラメータに一致するパラメータを取得します.
+ *
+ * @return パラメータ
+ * @throws IOException カメラ情報の取得に失敗した場合に例外が発生
+ */
+ public Parameter getParameter() throws IOException {
+ Parameter parameter = null;
+ Size previewSize = getPreviewSize();
+ List parameters = getUVCCamera().getParameter();
+ for (Parameter p : parameters) {
+ if (p.getFrameType() == FrameType.UNCOMPRESSED) {
+ if (p.getWidth() == previewSize.getWidth() && p.getHeight() == previewSize.getHeight()) {
+ parameter = p;
+ parameter.setUseH264(false);
+ }
+ }
+ }
+ return parameter;
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcBroadcasterProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcBroadcasterProvider.java
new file mode 100644
index 0000000000..54574a0ee8
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcBroadcasterProvider.java
@@ -0,0 +1,29 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import android.content.Context;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractBroadcastProvider;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.Broadcaster;
+
+public class UvcBroadcasterProvider extends AbstractBroadcastProvider {
+ /**
+ * カメラを操作するレコーダ.
+ */
+ private final UvcRecorder mRecorder;
+
+ public UvcBroadcasterProvider(Context context, UvcRecorder recorder) {
+ super(context, recorder);
+ mRecorder = recorder;
+ }
+
+ @Override
+ public Broadcaster createBroadcaster(String broadcastURI) {
+ if (broadcastURI.startsWith("srt://")) {
+ return new UvcSRTBroadcaster(mRecorder, broadcastURI);
+ } else if (broadcastURI.startsWith("rtmp://") || broadcastURI.startsWith("rtmps://")) {
+ return new UvcRTMPBroadcaster(mRecorder, broadcastURI);
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH264VideoStream.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH264VideoStream.java
new file mode 100644
index 0000000000..5892c1ae46
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH264VideoStream.java
@@ -0,0 +1,28 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.H264VideoStream;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+
+public class UvcH264VideoStream extends H264VideoStream {
+ /**
+ * 映像用エンコーダ.
+ */
+ private final VideoEncoder mVideoEncoder;
+
+ /**
+ * コンストラクタ.
+ * コンストラクタ.
+ *
+ * @param recorder 操作するカメラのレコーダ.
+ * @param port 送信先のポート番号
+ */
+ UvcH264VideoStream(UvcRecorder recorder, int port) {
+ mVideoEncoder = new UvcVideoEncoder(recorder);
+ setDestinationPort(port);
+ }
+
+ @Override
+ public VideoEncoder getVideoEncoder() {
+ return mVideoEncoder;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH264toMJPEGEncoder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH264toMJPEGEncoder.java
new file mode 100644
index 0000000000..5adbe2b26c
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH264toMJPEGEncoder.java
@@ -0,0 +1,28 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import org.deviceconnect.android.libmedia.streaming.mjpeg.SurfaceMJPEGEncoder;
+
+public class UvcH264toMJPEGEncoder extends SurfaceMJPEGEncoder {
+
+ public UvcH264toMJPEGEncoder(UvcRecorder recorder) {
+ super(recorder.getSurfaceDrawingThread());
+ }
+
+ // SurfaceMJPEGEncoder
+
+ @Override
+ protected void prepare() {
+ }
+
+ @Override
+ protected void startRecording() {
+ }
+
+ @Override
+ protected void stopRecording() {
+ }
+
+ @Override
+ protected void release() {
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH265VideoStream.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH265VideoStream.java
new file mode 100644
index 0000000000..a61bf84f2a
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcH265VideoStream.java
@@ -0,0 +1,27 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.H265VideoStream;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+
+public class UvcH265VideoStream extends H265VideoStream {
+ /**
+ * 映像用エンコーダ.
+ */
+ private final VideoEncoder mVideoEncoder;
+
+ /**
+ * コンストラクタ.
+ *
+ * @param recorder 操作するカメラのレコーダ.
+ * @param port 送信先のポート番号
+ */
+ UvcH265VideoStream(UvcRecorder recorder, int port) {
+ mVideoEncoder = new UvcVideoEncoder(recorder, "video/hevc");
+ setDestinationPort(port);
+ }
+
+ @Override
+ public VideoEncoder getVideoEncoder() {
+ return mVideoEncoder;
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcMJPEGEncoder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcMJPEGEncoder.java
new file mode 100644
index 0000000000..505914858d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcMJPEGEncoder.java
@@ -0,0 +1,46 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGEncoder;
+import org.deviceconnect.android.libuvc.Parameter;
+
+import java.io.IOException;
+
+public class UvcMJPEGEncoder extends MJPEGEncoder {
+
+ private final UvcRecorder mRecorder;
+
+ public UvcMJPEGEncoder(UvcRecorder recorder) {
+ mRecorder = recorder;
+ }
+
+ @Override
+ public void start() {
+ mRecorder.getUVCCamera().setPreviewCallback((frame) -> {
+ try {
+ postJPEG(frame.getBuffer());
+ } finally {
+ frame.release();
+ }
+ });
+
+ try {
+ UvcRecorder.UvcSettings settings = (UvcRecorder.UvcSettings) mRecorder.getSettings();
+ Parameter p = settings.getParameter();
+ if (p == null) {
+ throw new RuntimeException("UVC parameter not found.");
+ }
+ mRecorder.getUVCCamera().startVideo(p);
+ } catch (Exception e) {
+ throw new RuntimeException();
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ mRecorder.getUVCCamera().stopVideo();
+ } catch (IOException e) {
+ // ignore.
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcMJPEGPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcMJPEGPreviewServer.java
new file mode 100644
index 0000000000..aaf8ba5d5d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcMJPEGPreviewServer.java
@@ -0,0 +1,34 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import android.content.Context;
+import android.util.Log;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractMJPEGPreviewServer;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.h264.UvcH264Recorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.mjpeg.UvcMjpgRecorder;
+import org.deviceconnect.android.libmedia.streaming.mjpeg.MJPEGEncoder;
+
+public class UvcMJPEGPreviewServer extends AbstractMJPEGPreviewServer {
+ UvcMJPEGPreviewServer(Context context, UvcRecorder recorder, int port, boolean useSSL) {
+ super(context, recorder, useSSL);
+ setPort(port);
+ }
+
+ @Override
+ protected MJPEGEncoder createSurfaceMJPEGEncoder() {
+ // UvcMJPEGEncoder を使用すると UVC から送られてくる JPEG を
+ // そのまま MJPEG サーバから配信しますが、それだと端末の画面に
+ // 表示する処理が行えないので、MJPEG を一旦 Surface に描画してからエンコードするようにします。
+ return new UvcH264toMJPEGEncoder((UvcRecorder) getRecorder());
+ // 以下の条件分を使用することで、MJPEG をそのまま配信するようになります。
+// MediaRecorder recorder = getRecorder();
+// if (recorder instanceof UvcH264Recorder) {
+// return new UvcH264toMJPEGEncoder((UvcRecorder) getRecorder());
+// } else if (recorder instanceof UvcMjpgRecorder) {
+// return new UvcMJPEGEncoder((UvcRecorder) getRecorder());
+// } else {
+// return null;
+// }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcPreviewServerProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcPreviewServerProvider.java
new file mode 100644
index 0000000000..3b390f2915
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcPreviewServerProvider.java
@@ -0,0 +1,25 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import android.content.Context;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractPreviewServerProvider;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+
+public class UvcPreviewServerProvider extends AbstractPreviewServerProvider {
+ /**
+ * コンストラクタ.
+ *
+ * @param context コンテキスト
+ * @param recorder レコーダ
+ */
+ public UvcPreviewServerProvider(final Context context, final UvcRecorder recorder) {
+ super(context, recorder);
+
+ MediaRecorder.Settings settings = recorder.getSettings();
+
+ addServer(new UvcMJPEGPreviewServer(context, recorder, settings.getMjpegPort(), false));
+ addServer(new UvcMJPEGPreviewServer(context, recorder, settings.getMjpegSSLPort(), true));
+ addServer(new UvcRTSPPreviewServer(context, recorder, settings.getRtspPort()));
+ addServer(new UvcSRTPreviewServer(context, recorder, settings.getSrtPort()));
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRTMPBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRTMPBroadcaster.java
new file mode 100644
index 0000000000..fbb3a0eb5d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRTMPBroadcaster.java
@@ -0,0 +1,17 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractRTMPBroadcaster;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.h264.UvcH264Recorder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+
+public class UvcRTMPBroadcaster extends AbstractRTMPBroadcaster {
+
+ public UvcRTMPBroadcaster(UvcRecorder recorder, String broadcastURI) {
+ super(recorder, broadcastURI);
+ }
+
+ @Override
+ protected VideoEncoder createVideoEncoder() {
+ return new UvcVideoEncoder((UvcRecorder) getRecorder());
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRTSPPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRTSPPreviewServer.java
new file mode 100644
index 0000000000..37311b75ff
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRTSPPreviewServer.java
@@ -0,0 +1,29 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import android.content.Context;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractRTSPPreviewServer;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.h264.UvcH264Recorder;
+import org.deviceconnect.android.libmedia.streaming.rtsp.session.video.VideoStream;
+
+public class UvcRTSPPreviewServer extends AbstractRTSPPreviewServer {
+
+ UvcRTSPPreviewServer(Context context, UvcRecorder recorder, int port) {
+ super(context, recorder);
+ setPort(port);
+ }
+
+ @Override
+ protected VideoStream createVideoStream() {
+ UvcRecorder recorder = (UvcRecorder) getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+ switch (settings.getPreviewEncoderName()) {
+ case H264:
+ default:
+ return new UvcH264VideoStream(recorder, 5006);
+ case H265:
+ return new UvcH265VideoStream(recorder, 5006);
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRecorder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRecorder.java
new file mode 100644
index 0000000000..6937d714dd
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcRecorder.java
@@ -0,0 +1,88 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import android.Manifest;
+import android.content.Context;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractMediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.BroadcasterProvider;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.PreviewServerProvider;
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+import java.io.IOException;
+
+public abstract class UvcRecorder extends AbstractMediaRecorder {
+ private final UVCCamera mUVCCamera;
+ private final UvcSettings mSettings;
+ private final UvcSurfaceDrawingThread mUvcSurfaceDrawingThread;
+ private final UvcBroadcasterProvider mUvcBroadcasterProvider;
+ private final UvcPreviewServerProvider mUvcPreviewServerProvider;
+
+ public UvcRecorder(Context context, UVCCamera camera) {
+ super(context);
+
+ if (camera == null) {
+ throw new IllegalArgumentException("UVCCamera is null.");
+ }
+
+ mUVCCamera = camera;
+ mSettings = createSettings();
+ mUvcSurfaceDrawingThread = new UvcSurfaceDrawingThread(this);
+ mUvcBroadcasterProvider = new UvcBroadcasterProvider(context, this);
+ mUvcPreviewServerProvider = new UvcPreviewServerProvider(context, this);
+ }
+
+ public UVCCamera getUVCCamera() {
+ return mUVCCamera;
+ }
+
+ protected abstract UvcSettings createSettings();
+
+ @Override
+ public Settings getSettings() {
+ return mSettings;
+ }
+
+ @Override
+ public BroadcasterProvider getBroadcasterProvider() {
+ return mUvcBroadcasterProvider;
+ }
+
+ @Override
+ public PreviewServerProvider getServerProvider() {
+ return mUvcPreviewServerProvider;
+ }
+
+ @Override
+ public EGLSurfaceDrawingThread getSurfaceDrawingThread() {
+ return mUvcSurfaceDrawingThread;
+ }
+
+ @Override
+ public void requestPermission(MediaRecorder.PermissionCallback callback) {
+ requestPermission(new String[] {Manifest.permission.CAMERA}, callback);
+ }
+
+ public String getSettingsName() {
+ return mUVCCamera.getDeviceId() + "-" + getId();
+ }
+
+ public class UvcSettings extends MediaRecorder.Settings {
+
+ public UvcSettings(Context context) {
+ super(context, getSettingsName());
+ }
+
+ /**
+ * 設定されているパラメータに一致するパラメータを取得します.
+ *
+ * @return パラメータ
+ * @throws IOException カメラ情報の取得に失敗した場合に例外が発生
+ */
+ public Parameter getParameter() throws IOException {
+ return null;
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSRTBroadcaster.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSRTBroadcaster.java
new file mode 100644
index 0000000000..f39145e753
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSRTBroadcaster.java
@@ -0,0 +1,25 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractSRTBroadcaster;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+
+public class UvcSRTBroadcaster extends AbstractSRTBroadcaster {
+
+ public UvcSRTBroadcaster(UvcRecorder recorder, String broadcastURI) {
+ super(recorder, broadcastURI);
+ }
+
+ @Override
+ protected VideoEncoder createVideoEncoder() {
+ UvcRecorder recorder = (UvcRecorder) getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+ switch (settings.getPreviewEncoderName()) {
+ case H264:
+ default:
+ return new UvcVideoEncoder(recorder);
+ case H265:
+ return new UvcVideoEncoder(recorder, "video/hevc");
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSRTPreviewServer.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSRTPreviewServer.java
new file mode 100644
index 0000000000..a50aaa7656
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSRTPreviewServer.java
@@ -0,0 +1,28 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import android.content.Context;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.AbstractSRTPreviewServer;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.h264.UvcH264Recorder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoEncoder;
+
+public class UvcSRTPreviewServer extends AbstractSRTPreviewServer {
+ UvcSRTPreviewServer(final Context context, final UvcRecorder recorder, final int port) {
+ super(context, recorder);
+ setPort(port);
+ }
+
+ @Override
+ protected VideoEncoder createVideoEncoder() {
+ UvcRecorder recorder = (UvcRecorder) getRecorder();
+ MediaRecorder.Settings settings = recorder.getSettings();
+ switch (settings.getPreviewEncoderName()) {
+ case H264:
+ default:
+ return new UvcVideoEncoder(recorder);
+ case H265:
+ return new UvcVideoEncoder(recorder, "video/hevc");
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSurfaceDrawingThread.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSurfaceDrawingThread.java
new file mode 100644
index 0000000000..2505fc6cad
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcSurfaceDrawingThread.java
@@ -0,0 +1,113 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import android.graphics.SurfaceTexture;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.libmedia.streaming.gles.EGLSurfaceDrawingThread;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+import org.deviceconnect.android.libuvc.player.UVCPlayer;
+import org.deviceconnect.android.libuvc.player.UVCPlayerException;
+
+public class UvcSurfaceDrawingThread extends EGLSurfaceDrawingThread {
+ /**
+ * レコーダ.
+ */
+ private final UvcRecorder mRecorder;
+
+ /**
+ * UVC からの映像をデコードするためのプレイヤー.
+ */
+ private UVCPlayer mPlayer;
+
+ public UvcSurfaceDrawingThread(UvcRecorder recorder) {
+ if (recorder == null) {
+ throw new IllegalArgumentException("recorder is null.");
+ }
+ mRecorder = recorder;
+ }
+
+ public UvcRecorder getRecorder() {
+ return mRecorder;
+ }
+
+ // EGLSurfaceDrawingThread
+
+ @Override
+ public void start() {
+ MediaRecorder.Settings settings = mRecorder.getSettings();
+ Size previewSize = settings.getPreviewSize();
+ setSize(previewSize.getWidth(), previewSize.getHeight());
+ setDrawingRange(settings.getDrawingRange());
+ super.start();
+ }
+
+ @Override
+ public int getDisplayRotation() {
+ return Surface.ROTATION_0;
+ }
+
+ @Override
+ public boolean isSwappedDimensions() {
+ return false;
+ }
+
+ @Override
+ protected void onStarted() {
+ startCamera(getSurfaceTexture());
+ }
+
+ @Override
+ protected void onStopped() {
+ stopCamera();
+ }
+
+ private synchronized void startCamera(SurfaceTexture surfaceTexture) {
+ try {
+ UvcRecorder.UvcSettings settings = (UvcRecorder.UvcSettings) mRecorder.getSettings();
+
+ UVCCamera camera = mRecorder.getUVCCamera();
+ if (camera.isRunning()) {
+ throw new RuntimeException("UVC camera is already running.");
+ }
+
+ Parameter parameter = settings.getParameter();
+ if (parameter == null) {
+ throw new RuntimeException("UVC parameter not found.");
+ }
+
+ mPlayer = new UVCPlayer();
+ mPlayer.setSurface(new Surface(surfaceTexture));
+ mPlayer.setOnEventListener(new UVCPlayer.OnEventListener() {
+ @Override
+ public void onStarted() {
+ }
+
+ @Override
+ public void onStopped() {
+ }
+
+ @Override
+ public void onError(UVCPlayerException e) {
+ }
+ });
+ mPlayer.start(camera, parameter);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private synchronized void stopCamera() {
+ if (mPlayer != null) {
+ try {
+ mPlayer.stop();
+ } catch (Exception e) {
+ // ignore.
+ }
+ mPlayer = null;
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcVideoEncoder.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcVideoEncoder.java
new file mode 100644
index 0000000000..2994c5b3a2
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/recorder/uvc/UvcVideoEncoder.java
@@ -0,0 +1,38 @@
+package org.deviceconnect.android.deviceplugin.uvc.recorder.uvc;
+
+import org.deviceconnect.android.libmedia.streaming.video.CameraVideoQuality;
+import org.deviceconnect.android.libmedia.streaming.video.SurfaceVideoEncoder;
+import org.deviceconnect.android.libmedia.streaming.video.VideoQuality;
+
+public class UvcVideoEncoder extends SurfaceVideoEncoder {
+ /**
+ * 映像のエンコード設定.
+ */
+ private final CameraVideoQuality mVideoQuality;
+
+ public UvcVideoEncoder(UvcRecorder recorder) {
+ this(recorder, "video/avc");
+ }
+
+ public UvcVideoEncoder(UvcRecorder recorder, String mimeType) {
+ super(recorder.getSurfaceDrawingThread());
+ mVideoQuality = new CameraVideoQuality(mimeType);
+ }
+
+ // VideoEncoder
+
+ @Override
+ public VideoQuality getVideoQuality() {
+ return mVideoQuality;
+ }
+
+ // SurfaceVideoEncoder
+
+ @Override
+ protected void onStartSurfaceDrawing() {
+ }
+
+ @Override
+ protected void onStopSurfaceDrawing() {
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/service/UVCService.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/service/UVCService.java
index f29589483d..0a87459562 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/service/UVCService.java
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/service/UVCService.java
@@ -1,43 +1,89 @@
package org.deviceconnect.android.deviceplugin.uvc.service;
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDevice;
-import org.deviceconnect.android.deviceplugin.uvc.core.UVCDeviceManager;
+import android.content.Context;
+import android.util.Log;
+
import org.deviceconnect.android.deviceplugin.uvc.profile.UVCMediaStreamRecordingProfile;
-import org.deviceconnect.android.deviceplugin.uvc.recorder.UVCRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorderManager;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.h264.UvcH264Recorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.mjpeg.UvcMjpgRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uncompressed.UvcUncompressedRecorder;
+import org.deviceconnect.android.deviceplugin.uvc.recorder.uvc.UvcRecorder;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
import org.deviceconnect.android.service.DConnectService;
-import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
public class UVCService extends DConnectService {
- private UVCDeviceManager mDeviceManager;
- private UVCRecorder mUVCRecorder;
- public UVCService(UVCDeviceManager deviceMgr, UVCDevice device) {
- super(device.getId());
+ private final Context mContext;
+ private MediaRecorderManager mMediaRecorderManager;
+
+ public UVCService(Context context, String serviceId) {
+ super(serviceId);
+ mContext = context;
- mDeviceManager = deviceMgr;
- setName("UVC: " + device.getName());
setOnline(false);
setNetworkType(NetworkType.UNKNOWN);
+
addProfile(new UVCMediaStreamRecordingProfile());
}
- public UVCRecorder getUVCRecorder() {
- return mUVCRecorder;
+ /**
+ * UVC に接続した時の処理を行います.
+ *
+ * @param camera UVC デバイス
+ */
+ public synchronized void connect(UVCCamera camera) {
+ setName(camera.getDeviceName());
+ setOnline(true);
+
+ if (mMediaRecorderManager != null) {
+ mMediaRecorderManager.destroy();
+ }
+ mMediaRecorderManager = new MediaRecorderManager(mContext, camera);
}
- public void openUVCDevice(UVCDevice device) {
+ /**
+ * UVC が切断された時の処理を行います.
+ */
+ public synchronized void disconnect() {
+ setOnline(false);
+ if (mMediaRecorderManager != null) {
+ mMediaRecorderManager.destroy();
+ mMediaRecorderManager = null;
+ }
+ }
- mUVCRecorder = new UVCRecorder(mDeviceManager, device);
- mUVCRecorder.initialize();
+ public synchronized MediaRecorderManager getMediaRecorderManager() {
+ return mMediaRecorderManager;
}
- public void closeUVCDevice() {
- mUVCRecorder.clean();
+ /**
+ * レコーダのリストを取得します.
+ *
+ * @return レコーダ
+ */
+ public List getUvcRecorderList() {
+ return mMediaRecorderManager != null ? mMediaRecorderManager.getUvcRecorderList() : new ArrayList<>();
}
- public void reset() {
- if (mUVCRecorder != null) {
- mUVCRecorder.stopPreview();
- }
+ /**
+ * レコーダ ID を指定してレコーダを取得します.
+ *
+ * レコーダが見つからない場合は null を返却します。
+ *
+ * @param id レコーダID
+ * @return レコーダ
+ */
+ public UvcRecorder findUvcRecorderById(String id) {
+ return mMediaRecorderManager != null ? mMediaRecorderManager.findUvcRecorderById(id) : null;
+ }
+
+ public UvcRecorder getDefaultRecorder() {
+ return mMediaRecorderManager != null ? mMediaRecorderManager.getDefaultRecorder() : null;
}
}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/CapabilityUtil.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/CapabilityUtil.java
new file mode 100644
index 0000000000..4cfc46072b
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/CapabilityUtil.java
@@ -0,0 +1,201 @@
+/*
+ CapabilityUtil.java
+ Copyright (c) 2016 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.deviceplugin.uvc.util;
+
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.os.Build;
+import android.util.Size;
+
+import org.deviceconnect.android.deviceplugin.uvc.recorder.MediaRecorder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public final class CapabilityUtil {
+ private CapabilityUtil() {
+ }
+
+ private static List getMediaCodecInfoList() {
+ List infoList = new ArrayList<>();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ infoList.addAll(Arrays.asList(list.getCodecInfos()));
+ } else {
+ for (int j = MediaCodecList.getCodecCount() - 1; j >= 0; j--) {
+ infoList.add(MediaCodecList.getCodecInfoAt(j));
+ }
+ }
+ return infoList;
+ }
+
+ public static List getSupportedProfileLevel(String mimeType) {
+ List list = new ArrayList<>();
+
+ for (MediaCodecInfo codecInfo : getMediaCodecInfoList()) {
+ if (codecInfo.isEncoder()) {
+ String[] types = codecInfo.getSupportedTypes();
+ if (Arrays.asList(types).contains(mimeType)) {
+ MediaCodecInfo.CodecCapabilities codecCapabilities = codecInfo.getCapabilitiesForType(mimeType);
+ if (codecCapabilities.profileLevels != null) {
+ for (MediaCodecInfo.CodecProfileLevel c : codecCapabilities.profileLevels) {
+ list.add(new MediaRecorder.ProfileLevel(c.profile, c.level));
+ }
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ private static List getSupportedEncoders(String mimeType) {
+ List encoderList = new ArrayList<>();
+
+ for (MediaCodecInfo codecInfo : getMediaCodecInfoList()) {
+ if (codecInfo.isEncoder()) {
+ String[] types = codecInfo.getSupportedTypes();
+ for (String type : types) {
+ if (!type.startsWith(mimeType)) {
+ continue;
+ }
+ encoderList.add(type);
+ }
+ }
+ }
+
+ return encoderList;
+ }
+
+ /**
+ * サポートされている音声コーデックのリストを取得します.
+ *
+ * @return サポートされている音声コーデックのリスト
+ */
+ public static List getSupportedAudioEncoders() {
+ return getSupportedEncoders("audio/");
+ }
+
+ /**
+ * サポートされている映像コーデックのリストを取得します.
+ *
+ * @return サポートされている映像コーデックのリスト
+ */
+ public static List getSupportedVideoEncoders() {
+ return getSupportedEncoders("video/");
+ }
+
+ /**
+ * MediaCodec でサポートされている解像度の最大値を取得します.
+ *
+ * @param mimeType マイムタイプ
+ * @return サポートされている解像度の最大値
+ */
+ public static Size getSupportedMaxSize(String mimeType) {
+ List sizeList = new ArrayList<>();
+
+ for (MediaCodecInfo codecInfo : getMediaCodecInfoList()) {
+ if (codecInfo.isEncoder() &&
+ isHardware(codecInfo) &&
+ isMediaCodecInfo(codecInfo, mimeType, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)) {
+ Size size = getSizeFromCodecInfo(codecInfo, mimeType);
+ if (size != null) {
+ sizeList.add(size);
+ }
+ }
+ }
+
+ Size size = null;
+ int max = 0;
+
+ for (Size s : sizeList) {
+ int d = s.getWidth() * s.getHeight();
+ if (max < d) {
+ max = d;
+ size = s;
+ }
+ }
+
+ return size;
+ }
+
+ /**
+ * MediaCodecInfo から解像度を取得します.
+ *
+ * @param codecInfo コーデック情報
+ * @param mimeType マイムタイプ
+ * @return 解像度
+ */
+ private static Size getSizeFromCodecInfo(MediaCodecInfo codecInfo, String mimeType) {
+ String[] types = codecInfo.getSupportedTypes();
+ for (String type : types) {
+ if (!type.startsWith(mimeType)) {
+ continue;
+ }
+
+ try {
+ MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(type);
+ MediaCodecInfo.VideoCapabilities videoCapabilities = capabilities.getVideoCapabilities();
+ if (videoCapabilities != null) {
+ int w = videoCapabilities.getSupportedWidths().getUpper();
+ int h = videoCapabilities.getSupportedHeights().getUpper();
+ return new Size(w, h);
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 指定された MediaCodecInfo のマイムタイプとカラーフォーマットが一致するか確認します.
+ *
+ * @param codecInfo 確認する MediaCodecInfo
+ * @param mimeType マイムタイプ
+ * @param colorFormat カラーフォーマット
+ * @return 一致する場合はtrue、それ以外はfalse
+ */
+ private static boolean isMediaCodecInfo(MediaCodecInfo codecInfo, String mimeType, int colorFormat) {
+ if (!codecInfo.isEncoder()) {
+ return false;
+ }
+
+ String[] types = codecInfo.getSupportedTypes();
+ for (String type : types) {
+ if (!type.equalsIgnoreCase(mimeType)) {
+ continue;
+ }
+
+ try {
+ MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(type);
+ for (int i = 0; i < capabilities.colorFormats.length; i++) {
+ int format = capabilities.colorFormats[i];
+ if (colorFormat == format) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isHardware(MediaCodecInfo info) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ return info.isHardwareAccelerated();
+ } else {
+ // エンコーダ名が OMX.qcom. または OMX.Exynos. から始まる場合はハードウェアエンコーダ
+ // エンコーダ名が OMX.google. から始まる場合はソフトウェアエンコーダ
+ String name = info.getName();
+ return name.startsWith("OMX.qcom.") || name.startsWith("OMX.Exynos.");
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/PropertyUtil.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/PropertyUtil.java
new file mode 100644
index 0000000000..fc5073b9bc
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/PropertyUtil.java
@@ -0,0 +1,144 @@
+package org.deviceconnect.android.deviceplugin.uvc.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Rect;
+import android.util.Log;
+import android.util.Size;
+
+import java.util.Set;
+
+/**
+ * Properties を使用して、データを保存するためのユーテリティクラス.
+ */
+public final class PropertyUtil {
+ private final SharedPreferences mPref;
+
+ public PropertyUtil(Context context, String name) {
+ mPref = context.getSharedPreferences(name, Context.MODE_PRIVATE);
+ }
+
+ public void clear() {
+ mPref.edit().clear().apply();
+ }
+
+ public Set getKeys() {
+ return mPref.getAll().keySet();
+ }
+
+ public void put(String key, int value) {
+ mPref.edit().putString(key, String.valueOf(value)).apply();
+ }
+
+ public void put(String key, long value) {
+ mPref.edit().putString(key, String.valueOf(value)).apply();
+ }
+
+ public void put(String key, float value) {
+ mPref.edit().putString(key, String.valueOf(value)).apply();
+ }
+
+ public void put(String key, boolean value) {
+ mPref.edit().putBoolean(key, value).apply();
+ }
+
+ public void put(String key, String value) {
+ mPref.edit().putString(key, value).apply();
+ }
+
+ public void put(String widthKey, String heightKey, Size size) {
+ mPref.edit().putString(widthKey, String.valueOf(size.getWidth()))
+ .putString(heightKey, String.valueOf(size.getHeight()))
+ .apply();
+ }
+
+ public void put(String leftKey, String topKey, String rightKey, String bottomKey, Rect rect) {
+ mPref.edit().putString(leftKey, String.valueOf(rect.left))
+ .putString(topKey, String.valueOf(rect.top))
+ .putString(rightKey, String.valueOf(rect.right))
+ .putString(bottomKey, String.valueOf(rect.bottom))
+ .apply();
+ }
+
+ public Integer getInteger(String key, Integer defaultValue) {
+ String value = mPref.getString(key, String.valueOf(defaultValue));
+ try {
+ if (value != null) {
+ return Integer.parseInt(value);
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ return defaultValue;
+ }
+
+ public Long getLong(String key, Long defaultValue) {
+ String value = mPref.getString(key, String.valueOf(defaultValue));
+ try {
+ if (value != null) {
+ return Long.parseLong(value);
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ return defaultValue;
+ }
+
+ public String getString(String key, String defaultValue) {
+ return mPref.getString(key, defaultValue);
+ }
+
+ public Float getFloat(String key, Float defaultValue) {
+ String value = mPref.getString(key, String.valueOf(defaultValue));
+ try {
+ if (value != null) {
+ return Float.parseFloat(value);
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ return defaultValue;
+ }
+
+ public boolean getBoolean(String key, boolean defaultValue) {
+ return mPref.getBoolean(key, defaultValue);
+ }
+
+ public Size getSize(String widthKey, String heightKey) {
+ String w = mPref.getString(widthKey, null);
+ String h = mPref.getString(heightKey, null);
+ if (w != null && h != null) {
+ try {
+ return new Size(Integer.parseInt(w), Integer.parseInt(h));
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ return null;
+ }
+
+ public Rect getRect(String leftKey, String topKey, String rightKey, String bottomKey) {
+ String l = mPref.getString(leftKey, null);
+ String t = mPref.getString(topKey, null);
+ String r = mPref.getString(rightKey, null);
+ String b = mPref.getString(bottomKey, null);
+ if (l != null && t != null && r != null && b != null) {
+ try {
+ int left = Integer.parseInt(l);
+ int top = Integer.parseInt(t);
+ int right = Integer.parseInt(r);
+ int bottom = Integer.parseInt(b);
+ if (left >= 0 && top >= 0 && right >= 0 && bottom >= 0) {
+ return new Rect(left, top, right, bottom);
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ return null;
+ }
+
+ public void remove(String key) {
+ mPref.edit().remove(key).apply();
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/SRTSettings.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/SRTSettings.java
new file mode 100644
index 0000000000..177e0968e4
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/SRTSettings.java
@@ -0,0 +1,101 @@
+package org.deviceconnect.android.deviceplugin.uvc.util;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class SRTSettings {
+
+ public static final String FILE_NAME = "srt_properties";
+
+ /**
+ * 設定画面でサポートする SRT オプションの定義.
+ */
+ private final List mSRTOptionItems = new ArrayList<>();
+ {
+// mSRTOptionItems.add(new SRTOptionItem(SRT.SRTO_PEERLATENCY, Integer.class, R.string.pref_key_settings_srt_peerlatency));
+// mSRTOptionItems.add(new SRTOptionItem(SRT.SRTO_LOSSMAXTTL, Integer.class, R.string.pref_key_settings_srt_lossmaxttl));
+// mSRTOptionItems.add(new SRTOptionItem(SRT.SRTO_INPUTBW, Long.class, R.string.pref_key_settings_srt_inputbw));
+// mSRTOptionItems.add(new SRTOptionItem(SRT.SRTO_OHEADBW, Integer.class, R.string.pref_key_settings_srt_oheadbw));
+// mSRTOptionItems.add(new SRTOptionItem(SRT.SRTO_CONNTIMEO, Integer.class, R.string.pref_key_settings_srt_conntimeo));
+// mSRTOptionItems.add(new SRTOptionItem(SRT.SRTO_PEERIDLETIMEO, Integer.class, R.string.pref_key_settings_srt_peeridletimeo));
+// mSRTOptionItems.add(new SRTOptionItem(SRT.SRTO_PACKETFILTER, String.class, R.string.pref_key_settings_srt_packetfilter));
+ }
+
+ private final PropertyUtil mPref;
+ private final Context mContext;
+
+ public SRTSettings(Context context) {
+ mContext = context;
+ mPref = new PropertyUtil(context, FILE_NAME);
+ }
+
+ /**
+ * 指定されたキーの値を整数にして取得します.
+ *
+ * @param key 格納されているキー
+ * @param defaultValue 値が格納されていない場合に返却する値
+ * @return 整数値
+ */
+ private int getInt(String key, int defaultValue) {
+ String value = mPref.getString(key, String.valueOf(defaultValue));
+ try {
+ return Integer.parseInt(value);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * SRT サーバに対して設定するオプションの一覧を作成します.
+ *
+ * @return オプションの一覧
+ */
+ public Map loadSRTSocketOptions() {
+ Map options = new HashMap<>();
+ for (SRTOptionItem item : mSRTOptionItems) {
+ options.put(item.getOptionEnum(), item.getValue());
+ }
+ return options;
+ }
+
+ /**
+ * SRT オプション設定項目の定義.
+ *
+ * SRT オプションの列挙子 ({@link SRT} で定義されているもの) に対して、値の型とプリファレンスキーを対応づける.
+ */
+ private class SRTOptionItem {
+ final int mOptionEnum;
+ final Class> mValueClass;
+ final int mPrefKey;
+
+ SRTOptionItem(int optionEnum, Class> valueClass, int prefKey) {
+ mOptionEnum = optionEnum;
+ mValueClass = valueClass;
+ mPrefKey = prefKey;
+ }
+
+ int getOptionEnum() {
+ return mOptionEnum;
+ }
+
+ Object getValue() {
+ String key = mContext.getString(mPrefKey);
+ String value = mPref.getString(key, null);
+ if (value == null || "".equals(value)) {
+ return null;
+ }
+ try {
+ if (mValueClass == Long.class) {
+ return Long.parseLong(value);
+ } else if (mValueClass == Integer.class) {
+ return Integer.parseInt(value);
+ }
+ } catch (Exception ignored) {}
+ return value;
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/UVCRegistry.java b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/UVCRegistry.java
new file mode 100644
index 0000000000..65d2100f78
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/java/org/deviceconnect/android/deviceplugin/uvc/util/UVCRegistry.java
@@ -0,0 +1,142 @@
+package org.deviceconnect.android.deviceplugin.uvc.util;
+
+import android.content.Context;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class UVCRegistry {
+ private static final String FILE_NAME = "uvc-save.dat";
+
+ private final List mUVCList = new ArrayList<>();
+ private final Context mContext;
+
+ public UVCRegistry(Context context) {
+ mContext = context;
+ load();
+ }
+
+ public List getUVCList() {
+ return mUVCList;
+ }
+
+ public void addUVC(String deviceId, String name) {
+ UVC uvc = new UVC(deviceId, name);
+ if (!mUVCList.contains(uvc)) {
+ mUVCList.add(uvc);
+ }
+ save();
+ }
+
+ public void removeUVC(String deviceId) {
+ UVC removeUVC = null;
+ for (UVC uvc : mUVCList) {
+ if (uvc.getDeviceId().equalsIgnoreCase(deviceId)) {
+ removeUVC = uvc;
+ break;
+ }
+ }
+ if (removeUVC != null) {
+ mUVCList.remove(removeUVC);
+ }
+ save();
+ }
+
+ private void removeUVC(UVC uvc) {
+ mUVCList.remove(uvc);
+ }
+
+ private void save() {
+ JSONArray array = new JSONArray();
+ for (UVC uvc : mUVCList) {
+ try {
+ array.put(createObject(uvc));
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ String text = array.toString();
+ try (FileOutputStream fos = mContext.openFileOutput(FILE_NAME, Context.MODE_PRIVATE)) {
+ fos.write(text.getBytes());
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ private void load() {
+ int len;
+ byte[] buf = new byte[4092];
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (FileInputStream ios = mContext.openFileInput(FILE_NAME)) {
+ while ((len = ios.read(buf)) > 0) {
+ baos.write(buf, 0, len);
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+
+ mUVCList.clear();
+ try {
+ JSONArray array = new JSONArray(new String(baos.toByteArray()));
+ for (int i = 0; i < array.length(); i++) {
+ mUVCList.add(createUVC(array.getJSONObject(i)));
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ private JSONObject createObject(UVC uvc) throws JSONException {
+ JSONObject object = new JSONObject();
+ object.put("deviceId", uvc.getDeviceId());
+ object.put("name", uvc.getName());
+ return object;
+ }
+
+ private UVC createUVC(JSONObject object) throws JSONException {
+ String deviceId = object.getString("deviceId");
+ String name = object.getString("name");
+ return new UVC(deviceId, name);
+ }
+
+ public static class UVC {
+ private final String mDeviceId;
+ private final String mName;
+
+ public UVC(String deviceId, String name) {
+ mDeviceId = deviceId;
+ mName = name;
+ }
+
+ public String getDeviceId() {
+ return mDeviceId;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ UVC uvc = (UVC) o;
+ return Objects.equals(mDeviceId, uvc.mDeviceId) &&
+ Objects.equals(mName, uvc.mName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDeviceId, mName);
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/drawable-hdpi/dconnect_icon_lollipop.png b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/drawable-hdpi/dconnect_icon_lollipop.png
new file mode 100644
index 0000000000..c20d88d29c
Binary files /dev/null and b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/drawable-hdpi/dconnect_icon_lollipop.png differ
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/drawable-xhdpi/button_red.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/drawable/button_red.xml
similarity index 100%
rename from dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/drawable-xhdpi/button_red.xml
rename to dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/drawable/button_red.xml
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/activity_uvc_settings.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/activity_uvc_settings.xml
new file mode 100644
index 0000000000..75c02043e3
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/activity_uvc_settings.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/dialog_preference_seek_bar.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/dialog_preference_seek_bar.xml
new file mode 100644
index 0000000000..eb4be8c649
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/dialog_preference_seek_bar.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/dialog_progress.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/dialog_progress.xml
deleted file mode 100644
index f9217db3d6..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/dialog_progress.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_instruction.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_instruction.xml
index 934a972e97..8654d01a7c 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_instruction.xml
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_instruction.xml
@@ -1,33 +1,14 @@
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
-
-
-
-
-
-
-
-
+ android:paddingTop="10dp"
+ android:text="@string/uvc_settings_instruction_uvc_device_connection" />
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_list.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_list.xml
index bd4353b404..8a53e95770 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_list.xml
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_device_list.xml
@@ -1,30 +1,27 @@
-
+
+
-
+
-
+
+
-
+
-
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:onItemClickListener="@{presenter.onItemClick}"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_permission_confirmation.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_permission_confirmation.xml
new file mode 100644
index 0000000000..68726662a7
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_permission_confirmation.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_recorder_list.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_recorder_list.xml
new file mode 100644
index 0000000000..088bdeddf3
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/fragment_uvc_recorder_list.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_device.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_device.xml
index 03f872b89a..1ac1200db6 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_device.xml
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_device.xml
@@ -1,36 +1,44 @@
-
+
+
-
+
+
+
+
+
-
+ android:textSize="@dimen/text_size_setting_device_name"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
-
+
-
\ No newline at end of file
+
+
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_error.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_error.xml
deleted file mode 100644
index 7caf4d3076..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_error.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_recorder.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_recorder.xml
new file mode 100644
index 0000000000..9f9d98cf62
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_recorder.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_searching.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_searching.xml
deleted file mode 100644
index abd6edf26c..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/layout/item_uvc_searching.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/navigation/navigation_uvc_settings.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/navigation/navigation_uvc_settings.xml
new file mode 100644
index 0000000000..6f9c66cf35
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/navigation/navigation_uvc_settings.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/dimens.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/dimens.xml
index ac1fc8de71..dc7044fff7 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/dimens.xml
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/dimens.xml
@@ -6,6 +6,7 @@
16sp
24sp
+ 16sp
16sp
16sp
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/strings.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/strings.xml
index fc9dd4500e..73c2d45a5b 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/strings.xml
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/strings.xml
@@ -1,21 +1,110 @@
- UVC (Device Connect Device Plug-in)
- エラー
- USBカメラ {NAME} は下記のフォーマットをサポートしないため利用できません。\n\n - MotionJPEG
+ UVC (Device Connect Device Plug-in)
+ UVC 設定
+
+ UVC プラグイン設定
+ ユーザ認可
+ プラグインにアクセスがあった時に認可用のダイアログを表示します。
+ USBカメラとの接続方法
+ USBカメラ一覧
- [1/2] USBカメラとの接続方法
+
+ USBカメラとの接続方法
端末のUSB端子にUSBカメラを接続してください。\n\n端末からUSBカメラへ電力を充分に供給できない場合には、セルフパワー付USBハブ経由で接続する必要があります。
- [2/2] USBカメラ一覧
- デバイス検索中
- 接続
- 解除
- Unknown
- USB接続
- %1$sと接続中
- OK
- USB接続エラー
- %1$sとの接続に失敗しました
- [ONLINE]
- [OFFLINE]
+
+
+ USBカメラ一覧
+ ONLINE
+ OFFLINE
USBカメラが接続されていません。
+
+
+ このアプリで使用する以下のパーミッションを取得する必要があります。\n\n - 写真と動画の撮影\n - デバイス内の写真やメディアへのアクセス
+ パーミッション取得
+ パーミッションエラー
+ アプリを実行するためのパーミッションが許可されなかったので、アプリケーションを終了します。
+ はい
+ いいえ
+ キャンセル
+
+
+ レコーダ設定
+ 映像設定
+ 音声設定
+ 配信設定
+ SRT 設定
+ ポート設定
+
+
+ 静止画解像度
+ プレビュー解像度
+ フレームレート (fps)
+ ビットレート (bps)
+ ビットレートモード
+ キーフレームインターバル(秒)
+ エンコーダ
+ プロファイル - レベル
+ ソフトウェアエンコーダ
+ MediaCodec でソフトウェアエンコーダを優先して使用します。
+ イントラリフレッシュ
+ MotionJPEG 設定
+ クオリティ (%)
+ 切り抜き範囲
+ Left
+ Top
+ Right
+ Bottom
+ リセット
+
+
+ MotionJPEG 設定
+ RTSP 設定
+ SRT 設定
+ ポート番号
+
+
+ URL
+ リトライ回数
+ リトライ間隔(ミリ秒)
+
+
+ PREVIEW
+ カメラを起動します
+ org.deviceconnect.android.intent.action.preview.uvc
+ Device Connect Preview
+ DeviceConnect でのプレビュー
+ プレビュー配信サーバ [ %1$s ]
+ タップして撮影を停止します。
+ org.deviceconnect.android.intent.action.notifiy.uvc
+ Device Connect Notification
+ DeviceConnect の通知
+ プレビュー配信サーバを起動します
+ プレビュー配信サーバ [ %1$s ]
+ タップして配信を停止します。
+ ブロードキャストを起動します
+ ブロードキャスト [ %1$s ]
+ タップして配信を停止します。
+ 録画を開始します
+ 録画 [ %1$s ]
+ タップして録画を停止します。
+
+
+ SRTO_PEERLATENCY
+ SRTO_LOSSMAXTTL
+ SRTO_OHEADBW
+ SRTO_INPUTBW
+ SRTO_CONNTIMEO
+ SRTO_PEERIDLETIMEO
+ SRTO_PACKETFILTER
+ Auto
+
+
+ pref_key_settings_srt_peerlatency
+ pref_key_settings_srt_lossmaxttl
+ pref_key_settings_srt_oheadbw
+ pref_key_settings_srt_inputbw
+ pref_key_settings_srt_conntimeo
+ pref_key_settings_srt_peeridletimeo
+ pref_key_settings_srt_packetfilter
+
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/styles.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/styles.xml
index 2aacd06723..613f1363a7 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/styles.xml
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/values/styles.xml
@@ -3,5 +3,40 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/org_deviceconnect_android_deviceplugin_uvc.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/org_deviceconnect_android_deviceplugin_uvc.xml
index 5e3f21f9af..5d5b964ca8 100755
--- a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/org_deviceconnect_android_deviceplugin_uvc.xml
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/org_deviceconnect_android_deviceplugin_uvc.xml
@@ -1,9 +1,10 @@
-
+
+
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_plugin.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_plugin.xml
new file mode 100644
index 0000000000..07b1002643
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_plugin.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_broadcast.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_broadcast.xml
new file mode 100644
index 0000000000..d269f9839d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_broadcast.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_main.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_main.xml
new file mode 100644
index 0000000000..fcee95005f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_main.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_port.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_port.xml
new file mode 100644
index 0000000000..f3991f26ac
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_port.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_srt.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_srt.xml
new file mode 100644
index 0000000000..9fd72d533d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_srt.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_video.xml b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_video.xml
new file mode 100644
index 0000000000..ad81146e1c
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/app/src/main/res/xml/settings_uvc_recorder_video.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle b/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle
index 40217ff89f..6a0e51ad96 100644
--- a/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/build.gradle
@@ -15,7 +15,6 @@ buildscript {
allprojects {
repositories {
- maven { url 'http://raw.github.com/saki4510t/libcommon/master/repository/' }
maven { url 'https://jitpack.io' }
jcenter()
google()
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/.gitignore b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/.gitignore
new file mode 100644
index 0000000000..1ae99b430b
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/.gitignore
@@ -0,0 +1,3 @@
+/build
+
+.externalNativeBuild
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/CMakeLists.txt b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/CMakeLists.txt
new file mode 100644
index 0000000000..12c1e96625
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/CMakeLists.txt
@@ -0,0 +1,45 @@
+# Sets the minimum version of CMake required to build your native library.
+# This ensures that a certain set of CMake features is available to
+# your build.
+
+cmake_minimum_required(VERSION 3.4.1)
+
+# Specifies a library name, specifies whether the library is STATIC or
+# SHARED, and provides relative paths to the source code. You can
+# define multiple libraries by adding multiple add.library() commands,
+# and CMake builds them for you. When you build your app, Gradle
+# automatically packages shared libraries with your APK.
+
+include_directories(src/main/jni)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall")
+
+add_library( # Specifies the name of the library.
+ native-lib
+
+ # Sets the library as a shared library.
+ SHARED
+
+ # Provides a relative path to your source file(s).
+ src/main/jni/image-utils.cpp
+ src/main/jni/uvc-native-lib.cpp
+ src/main/jni/uvc.c
+ src/main/jni/uvc-descriptor.c
+ src/main/jni/uvc-h264-config.c)
+
+find_library( # Defines the name of the path variable that stores the
+ # location of the NDK library.
+ log-lib
+
+ # Specifies the name of the NDK library that
+ # CMake needs to locate.
+ log)
+
+
+# Links your native library against one or more other native libraries.
+target_link_libraries( # Specifies the target library.
+ native-lib
+
+ # Links the log library to the target library.
+ jnigraphics
+ ${log-lib})
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/build.gradle b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/build.gradle
new file mode 100644
index 0000000000..42f6235dad
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/build.gradle
@@ -0,0 +1,51 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 29
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 29
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags "-std=c++11 -Wno-error -frtti -fexceptions -mfloat-abi=softfp -mfpu=neon -ffast-math"
+ }
+ }
+ ndk {
+ // Specifies the ABI configurations of your native
+ // libraries Gradle should build and package with your APK.
+ abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ buildTypes {
+ debug {
+ externalNativeBuild {
+ cmake {
+ cFlags "-DDEBUG"
+ cppFlags "-DDEBUG"
+ }
+ }
+ }
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path 'CMakeLists.txt'
+ }
+ }
+}
+
+dependencies {
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/proguard-rules.pro b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/proguard-rules.pro
new file mode 100644
index 0000000000..f1b424510d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/androidTest/java/org/deviceconnect/android/libuvc/ExampleInstrumentedTest.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/androidTest/java/org/deviceconnect/android/libuvc/ExampleInstrumentedTest.java
new file mode 100644
index 0000000000..cc3f2465d0
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/androidTest/java/org/deviceconnect/android/libuvc/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package org.deviceconnect.android.libuvc;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("jp.gclue.android.libuvc.test", appContext.getPackageName());
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/AndroidManifest.xml b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..d6484ba4f8
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceClassFilter.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceClassFilter.java
new file mode 100644
index 0000000000..6f2985a3aa
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceClassFilter.java
@@ -0,0 +1,44 @@
+/*
+ UsbDeviceClassFilter.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libusb;
+
+import android.hardware.usb.UsbDevice;
+
+import java.util.Objects;
+
+/**
+ * デバイスクラスでフィルタリングするためのクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+class UsbDeviceClassFilter implements UsbDeviceFilter {
+
+ private final int mClassType;
+
+ UsbDeviceClassFilter(int classType) {
+ mClassType = classType;
+ }
+
+ @Override
+ public boolean checkFilter(UsbDevice device) {
+ int classType = device.getDeviceClass();
+ return mClassType == classType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ UsbDeviceClassFilter that = (UsbDeviceClassFilter) o;
+ return mClassType == that.mClassType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mClassType);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceFilter.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceFilter.java
new file mode 100644
index 0000000000..512c210553
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceFilter.java
@@ -0,0 +1,18 @@
+/*
+ UsbDeviceFilter.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libusb;
+
+import android.hardware.usb.UsbDevice;
+
+/**
+ * USBのフィルタリングするためのインターフェース.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+interface UsbDeviceFilter {
+ boolean checkFilter(UsbDevice device);
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceIdFilter.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceIdFilter.java
new file mode 100644
index 0000000000..765f7e698f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbDeviceIdFilter.java
@@ -0,0 +1,48 @@
+/*
+ UsbDeviceIdFilter.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libusb;
+
+import android.hardware.usb.UsbDevice;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * デバイスIDでフィルタリングするためのクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+class UsbDeviceIdFilter implements UsbDeviceFilter {
+ private final int mVendorId;
+ private final int mProductId;
+
+ UsbDeviceIdFilter(int vendorId, int productId) {
+ mVendorId = vendorId;
+ mProductId = productId;
+ }
+
+ @Override
+ public boolean checkFilter(UsbDevice device) {
+ int vendorId = device.getVendorId();
+ int productId = device.getProductId();
+ return (mVendorId == vendorId && mProductId == productId);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ UsbDeviceIdFilter filter = (UsbDeviceIdFilter) o;
+ return mVendorId == filter.mVendorId &&
+ mProductId == filter.mProductId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mVendorId, mProductId);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbSerialPort.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbSerialPort.java
new file mode 100644
index 0000000000..97af9fe616
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbSerialPort.java
@@ -0,0 +1,515 @@
+/*
+ UsbSerialPort.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libusb;
+
+import android.hardware.usb.UsbConfiguration;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.deviceconnect.android.libuvc.BuildConfig;
+
+import java.io.IOException;
+
+/**
+ * USBデバイスのシリアルポート.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class UsbSerialPort {
+ /**
+ * デバッグ用フラグ.
+ */
+ private static final boolean DEBUG = BuildConfig.DEBUG;
+
+ /**
+ * デバッグ用タグ.
+ */
+ private static final String TAG = "USB";
+
+ /**
+ * USB管理するクラス.
+ */
+ private final UsbManager mUsbManager;
+
+ /**
+ * USBデバイス.
+ */
+ private final UsbDevice mUsbDevice;
+
+ /**
+ * USBデバイスとのコネクション.
+ */
+ private UsbDeviceConnection mConnection;
+
+ /**
+ * タイムアウト.
+ */
+ private int mTimeout = 1000;
+
+ /**
+ * デバイスID.
+ */
+ private final int mDeviceId;
+
+ /**
+ * ベンダーID.
+ */
+ private final int mVendorId;
+
+ /**
+ * プロダクトID.
+ */
+ private final int mProductId;
+
+ /**
+ * デバイス名.
+ */
+ private String mDeviceName;
+
+ /**
+ * シリアルナンバー.
+ */
+ private String mSerialNumber;
+
+ /**
+ * 製造業者名.
+ */
+ private String mManufacturerName;
+
+ /**
+ * バージョン.
+ */
+ private String mVersion;
+
+ /**
+ * コンストラクタ.
+ *
+ * @param manager USB管理クラス
+ * @param device USBデバイス
+ */
+ UsbSerialPort(final UsbManager manager, final UsbDevice device) {
+ if (manager == null || device == null) {
+ throw new IllegalArgumentException("manager or device is null.");
+ }
+ mUsbManager = manager;
+ mUsbDevice = device;
+
+ mDeviceId = mUsbDevice.getDeviceId();
+ mVendorId = mUsbDevice.getVendorId();
+ mProductId = mUsbDevice.getProductId();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mDeviceName = mUsbDevice.getProductName();
+ mSerialNumber = mUsbDevice.getSerialNumber();
+ mManufacturerName = mUsbDevice.getManufacturerName();
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mVersion = mUsbDevice.getVersion();
+ }
+ }
+
+ /**
+ * USBデバイスを取得します.
+ *
+ * @return USBデバイス
+ */
+ public UsbDevice getUsbDevice() {
+ return mUsbDevice;
+ }
+
+ /**
+ * デバイス名を取得します.
+ *
+ * Android OS が LOLLIPOP 以下の場合は null を返却します。
+ * USB からデバイス名が取得できなかった場合は null を返却します。
+ *
+ * @return デバイス名
+ */
+ public String getDeviceName() {
+ return mDeviceName;
+ }
+
+ /**
+ * バージョンを取得します.
+ *
+ * Android OS が M 未満の場合は null を返却します。
+ * USB からバージョンが取得できなかった場合は null を返却します。
+ *
+ * @return バージョン
+ */
+ public String getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * 製造業者名を取得します.
+ *
+ * Android OS が LOLLIPOP 以下の場合は null を返却します。
+ * USB から製造業者名が取得できなかった場合は null を返却します。
+ *
+ * @return 製造業者名
+ */
+ public String getManufacturerName() {
+ return mManufacturerName;
+ }
+
+ /**
+ * シリアルナンバーを取得します.
+ *
+ * Android OS が LOLLIPOP 以下の場合は null を返却します。
+ * USB からシリアルナンバーが取得できなかった場合は null を返却します。
+ *
+ * @return シリアルナンバー
+ */
+ public String getSerialNumber() {
+ return mSerialNumber;
+ }
+
+ /**
+ * ベンダー ID を取得します.
+ *
+ * @return ベンダー ID
+ */
+ public int getVendorId() {
+ return mVendorId;
+ }
+
+ /**
+ * プロダクト ID を取得します.
+ *
+ * @return プロダクト ID
+ */
+ public int getProductId() {
+ return mProductId;
+ }
+
+ /**
+ * デバイス ID を取得します.
+ *
+ * @return デバイス ID
+ */
+ public int getDeviceId() {
+ return mDeviceId;
+ }
+
+ /**
+ * USBデバイスに接続します.
+ *
+ * 既に接続されている場合には、呼び出されても特に処理は行いません。
+ *
+ * @throws IOException 接続に失敗した場合に発生
+ */
+ public synchronized void open() throws IOException {
+ if (mConnection != null) {
+ return;
+ }
+
+ mConnection = mUsbManager.openDevice(getUsbDevice());
+ if (mConnection == null) {
+ throw new IOException("Failed to connect device. device=" + mUsbDevice.getDeviceName());
+ }
+ }
+
+ /**
+ * 接続したUSBデバイスのファイルディスクリプタを取得します.
+ *
+ * @return ファイルディスクリプタ
+ */
+ public synchronized int getFileDescriptor() {
+ checkConnected();
+ return mConnection.getFileDescriptor();
+ }
+
+ /**
+ * 接続したUSBデバイスのディスクリプタを取得します.
+ *
+ * @return ディスクリプタの配列
+ */
+ public byte[] getRawDescriptors() {
+ checkConnected();
+ return mConnection.getRawDescriptors();
+ }
+
+ /**
+ * USBデバイスの接続を切断します.
+ */
+ public synchronized void close() {
+ if (mConnection != null) {
+ try {
+ mConnection.close();
+ mConnection = null;
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ }
+
+ /**
+ * USBデバイスとの接続状態を取得します.
+ *
+ * @return 接続中の場合はtrue、それ以外はfalse
+ */
+ public synchronized boolean isConnected() {
+ return mConnection != null;
+ }
+
+ /**
+ * デバイスディスクリプタを読み込みます.
+ *
+ * @return デバイスディスクリプタ
+ * @throws IOException 読み込みに失敗した場合に発生
+ */
+ public byte[] readDeviceDescriptor() throws IOException {
+ return readDescriptor(UsbConstants.USB_DIR_IN, (1 << 8), 18);
+ }
+
+ /**
+ * 指定されたIDのサブクラスのディスクリプタを取得します.
+ *
+ * @param value ID
+ * @param length サイズ
+ * @return ディスクリプタの配列
+ * @throws IOException 読み込みに失敗した場合
+ */
+ public byte[] readSubClassDescriptor(final int value, final int length) throws IOException {
+ checkConnected();
+
+ int requestType = UsbConstants.USB_DIR_IN | UsbConstants.USB_INTERFACE_SUBCLASS_BOOT;
+ return readDescriptor(requestType, value, length);
+ }
+
+ /**
+ * コンフィグレーションディスクリプタを読み込みます.
+ *
+ * @return コンフィグレーションディスクリプタ
+ * @throws IOException 読み込みに失敗した場合に発生
+ */
+ public byte[] readConfiguration() throws IOException {
+ checkConnected();
+
+ int requestType = UsbConstants.USB_DIR_IN;
+ int request = 8; // GET_CONFIGURATION
+ int value = 0;
+ int index = 0;
+ int length = 1;
+ byte[] buffer = new byte[length];
+
+ int result = mConnection.controlTransfer(requestType, request, value, index, buffer, length, mTimeout);
+ if (result >= 0) {
+ int idx = buffer[0] & 0xff;
+ return getConfigurationDescriptor(idx);
+ } else {
+ throw new IOException("Failed to read a Configuration.");
+ }
+ }
+
+ private byte[] getConfigurationDescriptor(final int index) throws IOException {
+ int requestType = UsbConstants.USB_DIR_IN;
+ byte[] buf = readDescriptor(requestType, (2 << 8) | index, 9);
+ int totalLength = (buf[3] & 0xff) << 8 | (buf[2] & 0xff);
+ buf = readDescriptor(requestType, (2 << 8) | index, totalLength);
+ return buf;
+ }
+
+ private byte[] readDescriptor(final int requestType, final int value, final int length) throws IOException {
+ checkConnected();
+
+ int request = 6; // GET_DESCRIPTOR
+ int index = 0;
+ byte[] buf = new byte[length];
+ int result = mConnection.controlTransfer(requestType, request, value, index, buf, length, mTimeout);
+ if (result < 0) {
+ throw new IOException("Failed to read a descriptor.");
+ }
+ return buf;
+ }
+
+ /**
+ * 指定されたパケットデータをbulk転送で送信します.
+ *
+ * @param endpoint 送信先のエンドポイント
+ * @param packet 送信するパケットデータ
+ * @return 送信できたバイト数、負の値の場合には送信失敗
+ */
+ public int bulkTransfer(final UsbEndpoint endpoint, final byte[] packet) {
+ checkConnected();
+
+ if (endpoint == null) {
+ throw new IllegalArgumentException("endpoint is null.");
+ }
+
+ if (packet == null) {
+ throw new IllegalArgumentException("packet is null.");
+ }
+
+ int offset = 0;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ while (offset < packet.length) {
+ int len = mConnection.bulkTransfer(endpoint, packet, offset, packet.length - offset, mTimeout);
+ if (len < 0) {
+ return -1;
+ }
+ offset += len;
+ }
+ } else {
+ while (offset < packet.length) {
+ // TODO コピーが遅いので最適化すること
+ byte[] data = new byte[packet.length - offset];
+ System.arraycopy(packet, offset, data, 0, packet.length - offset);
+ int len = mConnection.bulkTransfer(endpoint, data, packet.length - offset, mTimeout);
+ if (len < 0) {
+ return -1;
+ }
+ offset += len;
+ }
+ }
+ return offset;
+ }
+
+ /**
+ * 指定されたインターフェースクラスのエンドポイントを取得します.
+ *
+ * エンドポイントが見つからない場合にはnullを返却します。
+ *
+ * @param interfaceClass インターフェースクラス
+ * @return エンドポイント
+ */
+ public UsbEndpoint findEndpoint(final int interfaceClass) {
+ for (int i = 0; i < mUsbDevice.getInterfaceCount(); i++) {
+ UsbInterface usbInterface = mUsbDevice.getInterface(i);
+ int code = usbInterface.getInterfaceClass();
+ if (code == interfaceClass) {
+ if (usbInterface.getEndpointCount() > 0) {
+ return usbInterface.getEndpoint(0);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 全てのインターフェースを登録します.
+ */
+ public void claimAllInterfaces() {
+ checkConnected();
+
+ for (int i = 0; i < mUsbDevice.getInterfaceCount(); i++) {
+ if (!claimInterface(i)) {
+ if (DEBUG) {
+ Log.w(TAG, "Failed to claim interface.");
+ }
+ }
+ }
+ }
+
+ /**
+ * 全てのインターフェースを解除します.
+ */
+ public void releaseAllInterfaces() {
+ checkConnected();
+
+ for (int i = 0; i < mUsbDevice.getInterfaceCount(); i++) {
+ if (!releaseInterface(i)) {
+ if (DEBUG) {
+ Log.w(TAG, "Failed to release interface.");
+ }
+ }
+ }
+ }
+
+ /**
+ * インターフェースの登録を行います.
+ *
+ * @param interfaceIndex 登録するインターフェースのインデックス
+ * @return 登録に成功した場合はtrue、それ以外はfalse
+ */
+ public boolean claimInterface(final int interfaceIndex) {
+ checkConnected();
+
+ UsbInterface usbInterface = mUsbDevice.getInterface(interfaceIndex);
+ return mConnection.claimInterface(usbInterface, true);
+ }
+
+ /**
+ * インターフェースの解除を行います.
+ *
+ * @param interfaceIndex 解除するインターフェースのインデックス
+ * @return 解除に成功した場合はtrue、それ以外はfalse
+ */
+ public boolean releaseInterface(final int interfaceIndex) {
+ checkConnected();
+
+ UsbInterface usbInterface = mUsbDevice.getInterface(interfaceIndex);
+ return mConnection.releaseInterface(usbInterface);
+ }
+
+ /**
+ * コンフィグレーション数を取得します.
+ *
+ * @return コンフィグレーション数
+ */
+ public int getConfigurationCount() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ return mUsbDevice.getConfigurationCount();
+ }
+ return 0;
+ }
+
+ /**
+ * 指定されたconfigIdと同じIDを持つUsbConfigurationを設定します.
+ *
+ * @param configId コンフィグレーションID
+ * @return 設定に成功した場合はtrue、それ以外はfalse
+ */
+ public boolean setConfiguration(final int configId) {
+ checkConnected();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ for (int i = 0; i < mUsbDevice.getConfigurationCount(); i++) {
+ UsbConfiguration config = mUsbDevice.getConfiguration(i);
+ if (config.getId() == configId) {
+ return mConnection.setConfiguration(config);
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 接続状態を確認します.
+ *
+ * @throws IllegalStateException 接続されていない場合に発生
+ */
+ private void checkConnected() {
+ if (mConnection == null) {
+ throw new IllegalStateException("This port is not connected yet.");
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ UsbSerialPort that = (UsbSerialPort) o;
+
+ return mUsbDevice.equals(that.mUsbDevice);
+ }
+
+ @Override
+ public int hashCode() {
+ return mUsbDevice.hashCode();
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbSerialPortManager.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbSerialPortManager.java
new file mode 100644
index 0000000000..c7d42936ce
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/UsbSerialPortManager.java
@@ -0,0 +1,672 @@
+/*
+ UsbSerialPortManager.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libusb;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import org.deviceconnect.android.libuvc.BuildConfig;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * USBデバイスとのアタッチ・デタッチを管理するクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class UsbSerialPortManager {
+ /**
+ * デバッグフラグ.
+ */
+ private static final boolean DEBUG = BuildConfig.DEBUG;
+
+ /**
+ * デバッグタグ.
+ */
+ private static final String TAG = "USB";
+
+ /**
+ * タイムアウトのメッセージタイプを定義します.
+ */
+ private static final int MSG_TYPE_TIMEOUT = 123;
+
+ /**
+ * リクエストコードを定義します.
+ */
+ private static final int REQUEST_CODE = 12345;
+
+ /**
+ * USBパーミッションのアクションを定義します.
+ */
+ private static final String ACTION_USB_PERMISSION = "org.deviceconnect.android.libuvc.USB_PERMISSION";
+
+ /**
+ * USB管理クラス.
+ */
+ private final UsbManager mUsbManager;
+
+ /**
+ * コンテキスト.
+ */
+ private final Context mContext;
+
+ /**
+ * USBのイベントを通知するリスナー.
+ */
+ private final List mOnUsbEventListenerHolders = new ArrayList<>();
+
+ /**
+ * モニタリングフラグ.
+ *
+ * モニタリング中はtrue、それ以外はfalse.
+ *
+ */
+ private boolean mIsMonitoringFlag;
+
+ /**
+ * 通知を行うフィルターのリスト.
+ */
+ private final List mFilters = new ArrayList<>();
+
+ /**
+ * 接続しているUSBデバイスのリスト.
+ */
+ private final List mUsbSerialPorts = new ArrayList<>();
+
+ /**
+ * パーミッションの取得を行うためのキュー.
+ */
+ private final List mRequestPermissionQueue = new ArrayList<>();
+
+ /**
+ * パーミッションのリクエストを処理中フラグ.
+ */
+ private boolean mProcessingRequestPermission;
+
+ /**
+ * パーミッション要求を行うためのスレッド.
+ */
+ private HandlerThread mHandlerThread;
+
+ /**
+ * パーミッション要求を行うためのハンドラ.
+ */
+ private Handler mHandler;
+
+ /**
+ * コンストラクタ.
+ * @param context コンテキスト
+ * @throws UnsupportedOperationException USBデバイスをサポートしていない場合に発生
+ */
+ public UsbSerialPortManager(final Context context) {
+ if (context == null) {
+ throw new IllegalArgumentException("context is null.");
+ }
+ mContext = context;
+ mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+ if (mUsbManager == null) {
+ throw new UnsupportedOperationException("Not support USB.");
+ }
+ }
+
+ /**
+ * コンテキストを取得します.
+ *
+ * @return コンテキスト
+ */
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * フィルターにvendorIdとproductIdを追加します.
+ * @param vendorId ベンダーID
+ * @param productId プロダクトID
+ */
+ public void addFilter(final int vendorId, final int productId) {
+ mFilters.add(new UsbDeviceIdFilter(vendorId, productId));
+ }
+
+ /**
+ * フィルターにclassを追加します.
+ * @param classType USBデバイスのクラス
+ */
+ public void addFilter(final int classType) {
+ mFilters.add(new UsbDeviceClassFilter(classType));
+ }
+
+ /**
+ * フィルターをクリアします.
+ */
+ public void clearFilter() {
+ mFilters.clear();
+ }
+
+ /**
+ * USBデバイスのアタッチ・デタッチのイベントを通知するリスナーを設定します.
+ *
+ * deviceClassが一致するリスナーに対して通知します。
+ * deviceClassが-1の場合には、全てのイベントを通知します。
+ *
+ * @param deviceClass USBのデバイスクラス
+ * @param listener リスナー
+ */
+ public void addOnUsbEventListener(final int deviceClass, final OnUsbEventListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener is null.");
+ }
+
+ Holder holder = new Holder();
+ holder.mDeviceClass = deviceClass;
+ holder.mListener = listener;
+
+ synchronized (mOnUsbEventListenerHolders) {
+ mOnUsbEventListenerHolders.add(holder);
+ }
+ }
+
+ /**
+ * USBデバイスのアタッチ・デタッチのイベントを通知するリスナーを削除します.
+ *
+ * @param deviceClass USBのデバイスクラス
+ * @param listener リスナー
+ */
+ public void removeOnUsbEventListener(final int deviceClass, final OnUsbEventListener listener) {
+ synchronized (mOnUsbEventListenerHolders) {
+ for (Holder holder : mOnUsbEventListenerHolders) {
+ if (holder.mDeviceClass == deviceClass && holder.mListener == listener) {
+ mOnUsbEventListenerHolders.add(holder);
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * USBデバイスの監視を開始します.
+ *
+ * 既に監視が開始されている場合には何も処理を行いません。
+ * 監視を開始するときに、既に接続されているデバイスは即座にイベントを通知します。
+ *
+ */
+ public synchronized void startUsbMonitoring() {
+ if (mIsMonitoringFlag) {
+ if (DEBUG) {
+ Log.d(TAG, "Monitoring is already started.");
+ }
+ return;
+ }
+ mIsMonitoringFlag = true;
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_USB_PERMISSION);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ mContext.registerReceiver(mUsbReceiver, filter);
+
+ // 既にUSBに接続されているデバイスがあれば、通知する
+ List deviceList = getDeviceList();
+ for (UsbDevice device : deviceList) {
+ checkAttach(device);
+ }
+
+ mHandlerThread = new HandlerThread("request-permission");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_TYPE_TIMEOUT) {
+ if (msg.obj instanceof UsbDevice) {
+ checkAttach((UsbDevice) msg.obj);
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * USBデバイスの監視を停止します.
+ */
+ public synchronized void stopUsbMonitoring() {
+ if (!mIsMonitoringFlag) {
+ if (DEBUG) {
+ Log.d(TAG, "Monitoring is already stopped.");
+ }
+ return;
+ }
+ mIsMonitoringFlag = false;
+ mProcessingRequestPermission = false;
+
+ if (mHandlerThread != null) {
+ mHandlerThread.quit();
+ mHandlerThread = null;
+ }
+ mHandler = null;
+
+ synchronized (mRequestPermissionQueue) {
+ mRequestPermissionQueue.clear();
+ }
+
+ try {
+ mContext.unregisterReceiver(mUsbReceiver);
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ /**
+ * 接続されている全てのデバイスをデタッチします.
+ */
+ public void dispose() {
+ synchronized (mUsbSerialPorts) {
+ List serialPorts = new ArrayList<>(mUsbSerialPorts);
+ for (UsbSerialPort serialPort : serialPorts) {
+ detachUsbSerialPort(serialPort.getUsbDevice());
+ }
+ mUsbSerialPorts.clear();
+ }
+ }
+
+ /**
+ * 接続しているUSBデバイスのリストを取得します.
+ * @return USBデバイスのリスト
+ */
+ public List getUsbSerialPorts() {
+ synchronized (mUsbSerialPorts) {
+ return new ArrayList<>(mUsbSerialPorts);
+ }
+ }
+
+ /**
+ * 接続されているUSBデバイスのパーミッションを再度要求します.
+ */
+ public void requestPermissionAgain() {
+ List deviceList = getDeviceList();
+ for (UsbDevice device : deviceList) {
+ if (!isConnected(device)) {
+ requestPermission(device);
+ }
+ }
+ }
+
+ /**
+ * 指定されたUSBデバイスが接続されているか確認します.
+ *
+ * @param device 確認を行うUSBデバイス
+ * @return 接続されている場合はtrue、それ以外はfalse
+ */
+ private boolean isConnected(final UsbDevice device) {
+ synchronized (mUsbSerialPorts) {
+ for (UsbSerialPort port : mUsbSerialPorts) {
+ if (device.equals(port.getUsbDevice())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * UsbDevice に対応する UsbSerialPort を取得します.
+ *
+ * 保持しているリストに存在しない場合は null を返却します。
+ *
+ * @param device UsbDeviceのインスタンス
+ * @return UsbSerialPortのインスタンス
+ */
+ private UsbSerialPort getUsbSerialPort(final UsbDevice device) {
+ synchronized (mUsbSerialPorts) {
+ for (UsbSerialPort port : mUsbSerialPorts) {
+ if (device.equals(port.getUsbDevice())) {
+ return port;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 接続中のUSBデイバスのリストを取得します.
+ * @return USBデバイスのリスト
+ */
+ private List getDeviceList() {
+ HashMap deviceList = mUsbManager.getDeviceList();
+ return new ArrayList<>(deviceList.values());
+ }
+
+ /**
+ * USBのパーミッションを確認して、問題がなければ接続を行います。
+ *
+ * パーミッションがない場合には許可ダイアログを表示します。
+ *
+ * @param device 接続を行うUSBデバイス
+ */
+ private void checkAttach(final UsbDevice device) {
+ if (!checkFilter(device)) {
+ return;
+ }
+
+ if (mUsbManager.hasPermission(device)) {
+ attachUsbSerialPort(device);
+ } else {
+ synchronized (mRequestPermissionQueue) {
+ if (mProcessingRequestPermission) {
+ mRequestPermissionQueue.add(device);
+ } else {
+ requestPermission(device);
+ }
+ }
+ }
+ }
+
+ /**
+ * USBのパーミッションをリクエストします.
+ *
+ * @param device パーミッションをリクエストするUSBデバイス
+ */
+ private void requestPermission(final UsbDevice device) {
+ mProcessingRequestPermission = true;
+
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, REQUEST_CODE, new Intent(ACTION_USB_PERMISSION),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ mUsbManager.requestPermission(device, pendingIntent);
+
+ // パーミッションのレスポンスを待つタイムアウト処理
+ if (mHandler != null) {
+ Message message = Message.obtain();
+ message.what = MSG_TYPE_TIMEOUT;
+ message.obj = device;
+ mHandler.sendMessageDelayed(message, 30000);
+ }
+ }
+
+ /**
+ * USBデバイスとアタッチします.
+ *
+ * @param device アタッチするUSBデバイス
+ */
+ private synchronized void attachUsbSerialPort(final UsbDevice device) {
+ if (!checkFilter(device)) {
+ return;
+ }
+
+ UsbSerialPort serialPort = getUsbSerialPort(device);
+ if (serialPort != null) {
+ return;
+ }
+
+ serialPort = new UsbSerialPort(mUsbManager, device);
+ synchronized (mUsbSerialPorts) {
+ mUsbSerialPorts.add(serialPort);
+ }
+ postAttach(device, serialPort);
+ }
+
+ /**
+ * USBデバイスとデタッチします.
+ *
+ * @param device デタッチするUSBデバイス
+ */
+ private synchronized void detachUsbSerialPort(final UsbDevice device) {
+ UsbSerialPort serialPort = getUsbSerialPort(device);
+ if (serialPort != null) {
+ synchronized (mUsbSerialPorts) {
+ mUsbSerialPorts.remove(serialPort);
+ }
+ postDetach(device, serialPort);
+ }
+ }
+
+ /**
+ * 指定されたUSBデバイスがフィルターで一致するか確認します.
+ *
+ * フィルターが設定されていない場合には、trueを返却します。
+ *
+ * @param device USBデバイス
+ * @return フィルターに合致する場合はtrue、それ以外はfalse
+ */
+ private boolean checkFilter(final UsbDevice device) {
+ if (mFilters.isEmpty()) {
+ return true;
+ }
+
+ for (UsbDeviceFilter filter : mFilters) {
+ if (filter.checkFilter(device)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 次のリクエストパーミッションを処理します.
+ *
+ * リクエストのキューがなくなった場合には、mProcessingRequestPermission を false に設定します。
+ *
+ */
+ private void nextRequestPermission() {
+ if (mHandler != null) {
+ mHandler.removeMessages(MSG_TYPE_TIMEOUT);
+ }
+
+ synchronized (mRequestPermissionQueue) {
+ if (mRequestPermissionQueue.isEmpty()) {
+ mProcessingRequestPermission = false;
+ } else {
+ // パーミッション要求のレスポンスを受け取ってから直ぐにパーミッション要求を行う
+ // と失敗するので、ここでは、1秒ほど停止してから要求を行う。
+ mHandler.postDelayed(() -> {
+ synchronized (mRequestPermissionQueue) {
+ requestPermission(mRequestPermissionQueue.remove(0));
+ }
+ }, 500);
+ }
+ }
+ }
+
+ /**
+ * アタッチのイベントを通知します.
+ *
+ * @param device アタッチされたデバイス
+ * @param serialPort アタッチされたデバイス
+ */
+ private void postAttach(final UsbDevice device, final UsbSerialPort serialPort) {
+ synchronized (mOnUsbEventListenerHolders) {
+ for (Holder holder : mOnUsbEventListenerHolders) {
+ if (holder.mDeviceClass == -1 || holder.mDeviceClass == device.getDeviceClass()) {
+ try {
+ holder.mListener.onAttached(serialPort);
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * デタッチのイベントを通知します.
+ *
+ * @param device デタッチされたデバイス
+ * @param serialPort デタッチされたデバイス
+ */
+ private void postDetach(final UsbDevice device, final UsbSerialPort serialPort) {
+ synchronized (mOnUsbEventListenerHolders) {
+ for (Holder holder : mOnUsbEventListenerHolders) {
+ if (holder.mDeviceClass == -1 || holder.mDeviceClass == device.getDeviceClass()) {
+ try {
+ holder.mListener.onDetached(serialPort);
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 接続にエラーが発生したことを通知します.
+ *
+ * @param device エラーが発生したデバイス
+ */
+ private void postError(final UsbDevice device) {
+ synchronized (mOnUsbEventListenerHolders) {
+ for (Holder holder : mOnUsbEventListenerHolders) {
+ if (holder.check(device)) {
+ try {
+ holder.mListener.onError(new SecurityException("permission denied for device. " + device.getDeviceName()));
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * パーミッションを要求します.
+ *
+ * @param device USB デバイス
+ */
+ private void postOnRequestPermission(final UsbDevice device) {
+ synchronized (mOnUsbEventListenerHolders) {
+ for (Holder holder : mOnUsbEventListenerHolders) {
+ if (holder.check(device)) {
+ try {
+ holder.mListener.onRequestPermission(new PermissionCallback() {
+ @Override
+ public void allow() {
+ checkAttach(device);
+ }
+
+ @Override
+ public void deny() {
+ postError(device);
+ }
+ });
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * OnUsbEventListenerを保持するためのクラス.
+ */
+ private static class Holder {
+ /**
+ * デバイスクラス.
+ */
+ private int mDeviceClass;
+
+ /**
+ * 通知を行うリスナー.
+ */
+ private OnUsbEventListener mListener;
+
+ /**
+ * デバイスクラスを確認します.
+ *
+ * @param device USBデバイス
+ * @return デバイスクラスが一致する場合はtrue、それ以外はfalse
+ */
+ boolean check(UsbDevice device) {
+ return mDeviceClass == -1 || mDeviceClass == device.getDeviceClass();
+ }
+ }
+
+ /**
+ * USBデバイスのアタッチ・デタッチを受け取る BroadcastReceiver.
+ */
+ private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_USB_PERMISSION.equals(action)) {
+ UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+ attachUsbSerialPort(device);
+ } else {
+ postError(device);
+ }
+ }
+ nextRequestPermission();
+ } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+ UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ postOnRequestPermission(device);
+ }
+ } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+ UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ detachUsbSerialPort(device);
+ }
+ }
+ }
+ };
+
+ /**
+ * パーミッション不足を通知して、許可を受け取るためのリスナー.
+ */
+ public interface PermissionCallback {
+ /**
+ * 許可された場合に呼び出します.
+ */
+ void allow();
+
+ /**
+ * 拒否された場合に呼び出します.
+ */
+ void deny();
+ }
+
+ /**
+ * USBデバイスのアタッチ・デタッチのイベントを通知するリスナー.
+ */
+ public interface OnUsbEventListener {
+
+ /**
+ * USBデバイスにアタッチを通知します.
+ * @param device USBデバイス
+ */
+ void onAttached(UsbSerialPort device);
+
+ /**
+ * USBデバイスとのデタッチを通知します.
+ * @param device USBデバイス
+ */
+ void onDetached(UsbSerialPort device);
+
+ /**
+ * エラーを通知します.
+ * @param e 例外
+ */
+ void onError(Exception e);
+
+ /**
+ * パーミッション要求を通知します.
+ *
+ * @param callback パーミッション要求の結果通知するコールバック
+ */
+ void onRequestPermission(PermissionCallback callback);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/descriptor/Descriptor.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/descriptor/Descriptor.java
new file mode 100644
index 0000000000..6f795722a8
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/descriptor/Descriptor.java
@@ -0,0 +1,30 @@
+/*
+ Descriptor.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libusb.descriptor;
+
+/**
+ * ディスクリプタを定義するインターフェース.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public interface Descriptor {
+ int DEVICE = 0x01;
+ int CONFIGURATION = 0x02;
+ int STRING = 0x03;
+ int INTERFACE = 0x04;
+ int ENDPOINT = 0x05;
+ int DEVICE_QUALIFIER = 0x06;
+ int OTHER_SPEED_CONFIGURATION = 0x07;
+ int INTERFACE_POWER = 0x08;
+
+ // USB 2.0
+ int OTG = 0x09;
+ int INTERFACE_ASSOCIATION = 0x0B;
+
+ int CS_INTERFACE = 0x24;
+ int CS_ENDPOINT = 0x25;
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/descriptor/Scanner.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/descriptor/Scanner.java
new file mode 100644
index 0000000000..8096b46999
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libusb/descriptor/Scanner.java
@@ -0,0 +1,170 @@
+/*
+ Scanner.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libusb.descriptor;
+
+import java.io.IOException;
+
+/**
+ * byte 配列をスキャンするためのクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class Scanner {
+ /**
+ * スキャンする byte 配列.
+ */
+ private final byte[] mBuffer;
+
+ /**
+ * スキャンするサイズ.
+ */
+ private final int mLength;
+
+ /**
+ * スキャンしている位置.
+ */
+ private int mOffset;
+
+ /**
+ * コンストラクタ.
+ *
+ * @param buffer スキャンするbyte配列
+ */
+ public Scanner(final byte[] buffer) {
+ this(buffer, buffer.length);
+ }
+
+ /**
+ * コンストラクタ.
+ *
+ * @param buffer スキャンするbyte配列
+ * @param length スキャンサイズ
+ */
+ public Scanner(final byte[] buffer, final int length) {
+ if (buffer == null) {
+ throw new IllegalArgumentException("buffer is null.");
+ }
+
+ if (buffer.length < length) {
+ throw new IllegalArgumentException("buffer size is smaller than length.");
+ }
+
+ mBuffer = buffer;
+ mLength = length;
+ mOffset = 0;
+ }
+
+ /**
+ * 現在のスキャン位置を取得します.
+ *
+ * @return 現在のスキャン位置
+ */
+ public int getOffset() {
+ return mOffset;
+ }
+
+ /**
+ * 1byte取得して、スキャン位置を1進めます.
+ *
+ * @return 1byte
+ * @throws IOException スキャン位置がスキャンサイズを超えた場合に発生
+ */
+ public byte readByte() throws IOException {
+ checkBound(mOffset + 1);
+ try {
+ return (byte) (mBuffer[mOffset] & 0xFF);
+ } finally {
+ mOffset++;
+ }
+ }
+
+ /**
+ * 2byte取得して、スキャン位置を2進めます.
+ *
+ * @return short
+ * @throws IOException スキャン位置がスキャンサイズを超えた場合に発生
+ */
+ public short readShort() throws IOException {
+ checkBound(mOffset + 2);
+ try {
+ return (short) (((mBuffer[mOffset + 1] & 0xFF) << 8) | (mBuffer[mOffset] & 0xFF));
+ } finally {
+ mOffset += 2;
+ }
+ }
+
+ /**
+ * 4byte取得して、スキャン位置を4進めます.
+ *
+ * @return int
+ * @throws IOException スキャン位置がスキャンサイズを超えた場合に発生
+ */
+ public int readInt() throws IOException {
+ checkBound(mOffset + 4);
+ try {
+ return ((mBuffer[mOffset + 3] & 0xFF) << 24) | ((mBuffer[mOffset + 2] & 0xFF) << 16) |
+ ((mBuffer[mOffset + 1] & 0xFF) << 8) | (mBuffer[mOffset] & 0xFF);
+ } finally {
+ mOffset += 4;
+ }
+ }
+
+ /**
+ * 指定された位置にスキャン位置を移動させます
+ *
+ * @param position 移動する位置
+ * @throws IOException スキャン位置がスキャンサイズを超えた場合に発生
+ */
+ public void seek(final int position) throws IOException {
+ if (position < 0 || position > mLength) {
+ throw new IOException("Scanner's buffer is out of bound. offset=" + mOffset);
+ }
+ mOffset = position;
+ }
+
+ /**
+ * 指定されたサイズ分スキャン位置を移動します.
+ *
+ * @param len 移動する位置
+ * @throws IOException スキャン位置がスキャンサイズを超えた場合に発生
+ */
+ public void skip(final int len) throws IOException {
+ if (len < 0) {
+ throw new IOException("len is negative.");
+ }
+ checkBound(mOffset + len);
+ mOffset += len;
+ }
+
+ /**
+ * スキャン位置が最後まで到達しているか確認します.
+ *
+ * @return 最後まで到達している場合はtrue、それ以外はfalse
+ */
+ public boolean isFinish() {
+ return mOffset >= mLength;
+ }
+
+ /**
+ * スキャン位置をリセットします.
+ */
+ public void reset() {
+ mOffset = 0;
+ }
+
+ /**
+ * 指定された位置がスキャンサイズを超えていないか確認します.
+ *
+ * @param offset スキャン位置
+ * @throws IOException スキャンサイズを超えている場合に発生
+ */
+ private void checkBound(int offset) throws IOException {
+ if (offset > mLength) {
+ throw new IOException("Scanner's buffer is out of bound. offset=" + offset);
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Frame.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Frame.java
new file mode 100644
index 0000000000..f587a65f48
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Frame.java
@@ -0,0 +1,194 @@
+/*
+ Frame.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+/**
+ * UVCカメラのフレームバッファを格納するクラス.
+ *
+ * JNI 側で、このクラスは呼び出すので、修正する場合は注意すること。
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class Frame {
+ /**
+ * パラメータ.
+ */
+ private Parameter mParameter;
+
+ /**
+ * フレームバッファのID.
+ */
+ private int mId;
+
+ /**
+ * フレームバッファ.
+ */
+ private byte[] mBuffer;
+
+ /**
+ * フレームバッファの使用フラグ.
+ *
+ * trueの場合は使用中、false の場合には未使用。
+ *
+ */
+ private boolean mConsumable;
+
+ /**
+ * フレームバッファサイズ.
+ */
+ private int mLength;
+
+ /**
+ * Presentation Time Stamp.
+ */
+ private long mPresentationTimeStamp;
+
+ /**
+ * コンストラクタ.
+ * @param param パラメータ
+ * @param id フレームバッファのID
+ */
+ Frame(Parameter param, int id) {
+ this(param, id, new byte[1024]);
+ }
+
+ /**
+ * コンストラクタ.
+ * @param param パラメータ
+ * @param id フレームバッファのID
+ * @param buffer フレームバッファ
+ */
+ Frame(Parameter param, int id, byte[] buffer) {
+ if (param == null) {
+ throw new IllegalArgumentException("param is null.");
+ }
+ mParameter = param;
+ mId = id;
+ mBuffer = buffer;
+ }
+
+ /**
+ * フレームタイプを取得します.
+ *
+ * @return フレームタイプ
+ */
+ public FrameType getFrameType() {
+ return mParameter.getFrameType();
+ }
+
+ /**
+ * フレームの横幅を取得します.
+ *
+ * @return フレームの横幅
+ */
+ public int getWidth() {
+ return mParameter.getWidth();
+ }
+
+ /**
+ * フレームの縦幅を取得します.
+ *
+ * @return フレームの縦幅
+ */
+ public int getHeight() {
+ return mParameter.getHeight();
+ }
+
+ /**
+ * Presentation Time Stampを取得します.
+ *
+ * @return Presentation Time Stamp
+ */
+ public long getPTS() {
+ return mPresentationTimeStamp;
+ }
+
+ /**
+ * Presentation Time Stampを設定します.
+ *
+ * @param pts Presentation Time Stamp
+ */
+ void setPTS(long pts) {
+ mPresentationTimeStamp = pts;
+ }
+
+ /**
+ * フレームバッファのIDを取得します.
+ *
+ * MEMO: JNI から呼び出されるの変更する場合には注意すること。
+ *
+ * @return フレームバッファのID
+ */
+ int getId() {
+ return mId;
+ }
+
+ /**
+ * フレームバッファを取得します.
+ *
+ * MEMO: JNI から呼び出されるの変更する場合には注意すること。
+ *
+ * @return フレームバッファ
+ */
+ public byte[] getBuffer() {
+ return mBuffer;
+ }
+
+ /**
+ * フレームバッファの使用中フラグを取得します.
+ *
+ * @return フレームバッファの使用中フラグ
+ */
+ boolean isConsumable() {
+ return mConsumable;
+ }
+
+ /**
+ * フレームバッファを使用します.
+ */
+ void consume() {
+ mConsumable = true;
+ }
+
+ /**
+ * フレームバッファを解放します.
+ */
+ public void release() {
+ mConsumable = false;
+ }
+
+ /**
+ * フレームバッファのサイズを取得します.
+ *
+ * @return フレームバッファのサイズ
+ */
+ public int getLength() {
+ return mLength;
+ }
+
+ /**
+ * フレームバッファのサイズを設定します.
+ *
+ * @param length フレームバッファのサイズ
+ */
+ void setLength(int length) {
+ mLength = length;
+ }
+
+ /**
+ * 指定されたサイズがバッファよりも大きい場合にはリサイズを行います.
+ *
+ * 現在のバッファの方が大きい場合には何も行いません。
+ *
+ * @param length バッファサイズ
+ */
+ void resizeBuffer(int length) {
+ if (mBuffer.length < length) {
+ mBuffer = new byte[length];
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/FrameProvider.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/FrameProvider.java
new file mode 100644
index 0000000000..34e4a34113
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/FrameProvider.java
@@ -0,0 +1,89 @@
+package org.deviceconnect.android.libuvc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * フレームバッファを提供するクラス.
+ */
+class FrameProvider {
+ /**
+ * プレビューのfレームバッファを格納するバッファ.
+ */
+ private final List mFrames = new ArrayList<>();
+
+ /**
+ * フレームバッファを初期化します.
+ *
+ * @param parameter フレームバッファに渡すパラメータ
+ */
+ void init(Parameter parameter) {
+ init(parameter, 10);
+ }
+
+ /**
+ * フレームバッファを初期化します.
+ *
+ * @param parameter フレームバッファに渡すパラメータ
+ * @param size フレームバッファの個数
+ */
+ void init(Parameter parameter, int size) {
+ synchronized (mFrames) {
+ mFrames.clear();
+ for (int i = 0; i < size; i++) {
+ mFrames.add(new Frame(parameter, i));
+ }
+ }
+ }
+
+ /**
+ * 使用されていないフレームバッファを取得します.
+ *
+ * 全てのフレームバッファが使用されている場合は null を返却します。
+ *
+ * @param length フレームバッファのサイズ
+ * @return フレームバッファ
+ */
+ Frame getFreeFrame(int length) {
+ synchronized (mFrames) {
+ for (Frame frame : mFrames) {
+ if (!frame.isConsumable()) {
+ // フレームバッファのサイズが足りない場合はリサイズ
+ frame.resizeBuffer(length);
+ frame.consume();
+ return frame;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 指定された ID のフレームバッファを取得します.
+ *
+ * @param id フレームバッファのID
+ * @return フレームバッファ
+ */
+ Frame getFrameById(int id) {
+ synchronized (mFrames) {
+ return mFrames.get(id);
+ }
+ }
+
+ /**
+ * フレームバッファを解放します.
+ */
+ void clear() {
+ mFrames.clear();
+ System.gc();
+ }
+
+ /**
+ * フレームバッファが空か確認します.
+ *
+ * @return 空の場合は true、それ以外はfalse
+ */
+ boolean isEmpty() {
+ return mFrames.isEmpty();
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/FrameType.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/FrameType.java
new file mode 100644
index 0000000000..c17d4dfc6f
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/FrameType.java
@@ -0,0 +1,49 @@
+/*
+ FrameType.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+/**
+ * フレームのタイプを定義します.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public enum FrameType {
+ /**
+ * Motion JPEG.
+ */
+ MJPEG(0x07),
+
+ /**
+ * 非圧縮(YUV).
+ */
+ UNCOMPRESSED(0x05),
+
+ /**
+ * H264.
+ */
+ H264(0x14),
+
+ /**
+ * 不明なフレーム.
+ */
+ UNKNOWN(-1);
+
+ int mValue;
+
+ FrameType(final int value) {
+ mValue = value;
+ }
+
+ static FrameType valueOf(final int value) {
+ for (FrameType type : values()) {
+ if (type.mValue == value) {
+ return type;
+ }
+ }
+ return UNKNOWN;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/ImageUtils.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/ImageUtils.java
new file mode 100644
index 0000000000..e3207005cb
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/ImageUtils.java
@@ -0,0 +1,193 @@
+/*
+ ImageUtils.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+import android.graphics.Bitmap;
+
+/**
+ * NDKとのインターフェース.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public final class ImageUtils {
+
+ /**
+ * YUY2 のフォーマットを定義します.
+ *
+ * MEMO: NDK 側でも同じ定義をしているので注意すること。
+ *
+ */
+ private static final int FMT_YUY2 = 0;
+
+ /**
+ * NV12 のフォーマットを定義します.
+ */
+ private static final int FMT_NV12 = 1;
+
+ /**
+ * M420 のフォーマットを定義します.
+ */
+ private static final int FMT_M420 = 2;
+
+ /**
+ * I420 のフォーマットを定義します.
+ */
+ private static final int FMT_I420 = 3;
+
+ private ImageUtils() {
+ }
+
+ /**
+ * YUY2 のデータをBitmapに格納します.
+ *
+ * @param bitmap 格納先のBitmap
+ * @param yuv YUY2のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @return 格納に成功した場合はtrue、それ以外はfalse
+ */
+ public static boolean decodeYUY2(final Bitmap bitmap, final byte[] yuv, final int width, final int height) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("bitmap is null.");
+ }
+
+ if (yuv == null) {
+ throw new IllegalArgumentException("yuv is null.");
+ }
+
+ if (bitmap.getWidth() != width) {
+ throw new IllegalArgumentException("width is invalid.");
+ }
+
+ if (bitmap.getHeight() != height) {
+ throw new IllegalArgumentException("height is invalid.");
+ }
+ return nativeDecodeYUV(bitmap, yuv, width, height, FMT_YUY2);
+ }
+
+ /**
+ * NV12 のデータをBitmapに格納します.
+ *
+ * @param bitmap 格納先のBitmap
+ * @param yuv NV12のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @return 格納に成功した場合はtrue、それ以外はfalse
+ */
+ public static boolean decodeNV12(final Bitmap bitmap, final byte[] yuv, final int width, final int height) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("bitmap is null.");
+ }
+
+ if (yuv == null) {
+ throw new IllegalArgumentException("yuv is null.");
+ }
+
+ if (bitmap.getWidth() != width) {
+ throw new IllegalArgumentException("width is invalid.");
+ }
+
+ if (bitmap.getHeight() != height) {
+ throw new IllegalArgumentException("height is invalid.");
+ }
+ return nativeDecodeYUV(bitmap, yuv, width, height, FMT_NV12);
+ }
+
+ /**
+ * M420 のデータをBitmapに格納します.
+ *
+ * @param bitmap 格納先のBitmap
+ * @param yuv M420のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @return 格納に成功した場合はtrue、それ以外はfalse
+ */
+ public static boolean decodeM420(final Bitmap bitmap, final byte[] yuv, final int width, final int height) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("bitmap is null.");
+ }
+
+ if (yuv == null) {
+ throw new IllegalArgumentException("yuv is null.");
+ }
+
+ if (bitmap.getWidth() != width) {
+ throw new IllegalArgumentException("width is invalid.");
+ }
+
+ if (bitmap.getHeight() != height) {
+ throw new IllegalArgumentException("height is invalid.");
+ }
+ return nativeDecodeYUV(bitmap, yuv, width, height, FMT_M420);
+ }
+
+ /**
+ * I420 のデータをBitmapに格納します.
+ *
+ * @param bitmap 格納先のBitmap
+ * @param yuv I420のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @return 格納に成功した場合はtrue、それ以外はfalse
+ */
+ public static boolean decodeI420(final Bitmap bitmap, final byte[] yuv, final int width, final int height) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("bitmap is null.");
+ }
+
+ if (yuv == null) {
+ throw new IllegalArgumentException("yuv is null.");
+ }
+
+ if (bitmap.getWidth() != width) {
+ throw new IllegalArgumentException("width is invalid.");
+ }
+
+ if (bitmap.getHeight() != height) {
+ throw new IllegalArgumentException("height is invalid.");
+ }
+ return nativeDecodeYUV(bitmap, yuv, width, height, FMT_I420);
+ }
+
+ /**
+ * YUV420SemiPlannerのデータをBitmapに格納します.
+ *
+ * @param bitmap 格納先のBitmap
+ * @param yuv420sp YUV420SemiPlannerのデータ
+ * @param width 横幅
+ * @param height 縦幅
+ */
+ public static void decodeYUV420SP(Bitmap bitmap, byte[] yuv420sp, int width, int height) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("bitmap is null.");
+ }
+
+ if (yuv420sp == null) {
+ throw new IllegalArgumentException("yuv420sp is null.");
+ }
+
+ if (bitmap.getWidth() != width) {
+ throw new IllegalArgumentException("width is invalid.");
+ }
+
+ if (bitmap.getHeight() != height) {
+ throw new IllegalArgumentException("height is invalid.");
+ }
+ nativeDecodeYUV420SP(bitmap, yuv420sp, width, height);
+ }
+
+ public static void copyJpegHuffTable(byte[] src, int srcLength, byte[] dest) {
+ if (srcLength + 432 > dest.length) {
+ throw new IllegalArgumentException("dest array size is small.");
+ }
+ nativeCopyJpeg(src, srcLength, dest);
+ }
+
+ private static native boolean nativeDecodeYUV(Bitmap bitmap, byte[] yuv422, int width, int height, int type);
+ private static native void nativeDecodeYUV420SP(Bitmap bitmap, byte[] yuv420sp, int width, int height);
+ private static native void nativeCopyJpeg(byte[] src, int srcLength, byte[] dest);
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Option.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Option.java
new file mode 100644
index 0000000000..25a3ab1124
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Option.java
@@ -0,0 +1,1752 @@
+/*
+ Option.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+import org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl;
+import org.deviceconnect.android.libuvc.UVCCameraNative.Control;
+import org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl;
+import org.deviceconnect.android.libuvc.UVCCameraNative.RequestType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_AE_MODE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_AE_PRIORITY_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_EXPOSURE_TIME_ABSOLUTE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_FOCUS_ABSOLUTE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_FOCUS_AUTO_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_IRIS_ABSOLUTE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_ROLL_ABSOLUTE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_SCANNING_MODE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.CameraTerminalControl.CT_ZOOM_ABSOLUTE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_BRIGHTNESS_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_CONTRAST_AUTO_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_CONTRAST_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_GAIN_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_GAMMA_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_HUE_AUTO_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_HUE_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_SATURATION_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_SHARPNESS_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_WHITE_BALANCE_COMPONENT_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL;
+import static org.deviceconnect.android.libuvc.UVCCameraNative.ProcessingUnitControl.PU_WHITE_BALANCE_TEMPERATURE_CONTROL;
+
+/**
+ * UVCCameraの設定を行うクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class Option {
+ /**
+ * スキャニングモード.
+ */
+ public enum ScanningMode {
+ /**
+ * インターレース.
+ */
+ Interlaced,
+
+ /**
+ * プログレッシブ.
+ */
+ Progressive
+ }
+
+ /**
+ * 露出モード.
+ */
+ public enum ExposureMode {
+ /**
+ * マニュアル.
+ */
+ Manual(1),
+
+ /**
+ * オート.
+ */
+ Auto(1 << 1),
+
+ /**
+ * シャッター.
+ */
+ Shutter(1 << 2),
+
+ /**
+ * レンズの口径.
+ */
+ Aperture (1 << 3);
+
+ private int mValue;
+
+ ExposureMode(final int value) {
+ mValue = value;
+ }
+
+ int getValue() {
+ return mValue;
+ }
+
+ private static ExposureMode valueOf(final int value) {
+ for (ExposureMode e : values()) {
+ if (e.mValue == value) {
+ return e;
+ }
+ }
+ return Manual;
+ }
+ }
+
+ /**
+ * カメラターミナルコントロールの情報を格納するマップ.
+ */
+ private final Map mCameraTerminalControls = new HashMap<>();
+
+ /**
+ * プロセシングユニットコントロールの情報を格納するマップ.
+ */
+ private final Map mProcessingUnitControls = new HashMap<>();
+
+ /**
+ * このオプションの設定元のカメラ.
+ */
+ private final UVCCamera mUVCCamera;
+
+ /**
+ * コンストラクタ.
+ * @param camera カメラ
+ */
+ Option(final UVCCamera camera) {
+ mUVCCamera = camera;
+ }
+
+ //// CT ////
+
+ /**
+ * スキャニングモードがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedScanningMode() {
+ return isSupported(CT_SCANNING_MODE_CONTROL);
+ }
+
+ /**
+ * スキャニングモードを設定します.
+ *
+ * @param mode スキャニングモード
+ */
+ public void setScanningMode(final ScanningMode mode) {
+ if (mode == null) {
+ throw new IllegalArgumentException("mode is null.");
+ }
+ setByteControl(CT_SCANNING_MODE_CONTROL, mode == ScanningMode.Progressive ? 1 : 0);
+ }
+
+ /**
+ * 設定されているスキャニングモードを取得します.
+ *
+ * @return スキャニングモード
+ */
+ public ScanningMode getScanningMode() {
+ int sm = getCurSettingData(CT_SCANNING_MODE_CONTROL);
+ if (sm == 0) {
+ return ScanningMode.Interlaced;
+ } else {
+ return ScanningMode.Progressive;
+ }
+ }
+
+ /**
+ * 露出モードがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedExposureMode() {
+ return isSupported(CT_AE_MODE_CONTROL);
+ }
+
+ /**
+ * 露出モードの設定を行います.
+ *
+ * @param mode 露出モード
+ */
+ public void setExposureMode(final ExposureMode mode) {
+ setByteControl(CT_AE_MODE_CONTROL, mode.getValue());
+ }
+
+ /**
+ * 露出モードを取得します.
+ *
+ * @return 露出モード
+ */
+ public ExposureMode getExposureMode() {
+ return ExposureMode.valueOf(getCurSettingData(CT_AE_MODE_CONTROL));
+ }
+
+ /**
+ * 露出プライオリティがサポートされているか確認します.
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedExposurePriority() {
+ return isSupported(CT_AE_PRIORITY_CONTROL);
+ }
+
+ /**
+ * 露出プライオリティの有効・無効を設定します.
+ *
+ * @param exposurePriority 有効にする場合にはtrue、それ以外はfalse
+ */
+ public void setExposurePriority(final boolean exposurePriority) {
+ setByteControl(CT_AE_PRIORITY_CONTROL, exposurePriority ? 1 : 0);
+ }
+
+ /**
+ * 露出プライオリティを取得します.
+ *
+ * @return 有効の場合はtrue、それ以外はfalse
+ */
+ public boolean isExposurePriority() {
+ return isSettingData(CT_AE_PRIORITY_CONTROL);
+ }
+
+ /**
+ * 露出時間がサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedExposureTime() {
+ return isSupported(CT_EXPOSURE_TIME_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * 露出時間を設定します.
+ *
+ * @param exposureTime 露出時間
+ */
+ public void setExposureTime(final int exposureTime) {
+ if (exposureTime < getMinExposureTime() || getMaxExposureTime() < exposureTime) {
+ throw new IllegalArgumentException("exposureTime is invalid.");
+ }
+ setIntControl(CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, exposureTime);
+ }
+
+ /**
+ * 露出時間を取得します.
+ *
+ * @return 露出時間
+ */
+ public int getExposureTime() {
+ return getCurSettingData(CT_EXPOSURE_TIME_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * 露出時間の最大値を取得します.
+ *
+ * @return 露出時間の最大値
+ */
+ public int getMaxExposureTime() {
+ return getMaxSettingData(CT_EXPOSURE_TIME_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * 露出時間の最小値を取得します.
+ *
+ * @return 露出時間の最小値
+ */
+ public int getMinExposureTime() {
+ return getMinSettingData(CT_EXPOSURE_TIME_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * Zoom がサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedZoom() {
+ return isSupported(CT_ZOOM_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * Zoom を設定します.
+ *
+ * @param zoom zoom
+ */
+ public void setZoom(final int zoom) {
+ if (zoom < getMinZoom() || getMaxZoom() < zoom) {
+ throw new IllegalArgumentException("zoom is invalid.");
+ }
+ setShortControl(CT_ZOOM_ABSOLUTE_CONTROL, zoom);
+ }
+
+ /**
+ * 設定されている Zoom の値を取得します.
+ *
+ * @return Zoomの値
+ */
+ public int getZoom() {
+ return getCurSettingData(CT_ZOOM_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * Zoom の最大値を取得します.
+ *
+ * @return Zoomの最大値
+ */
+ public int getMaxZoom() {
+ return getMaxSettingData(CT_ZOOM_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * Zoom の最小値を取得します.
+ *
+ * @return Zoomの最小値
+ */
+ public int getMinZoom() {
+ return getMinSettingData(CT_ZOOM_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * オートフォーカスがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedAutoFocus() {
+ return isSupported(CT_FOCUS_AUTO_CONTROL);
+ }
+
+ /**
+ * オートフォーカスの有効・無効を取得します.
+ *
+ * @return オートフォーカスの有効・無効
+ */
+ public boolean isAutoFocus() {
+ return isSettingData(CT_FOCUS_AUTO_CONTROL);
+ }
+
+ /**
+ * オートフォーカスを設定します.
+ *
+ * @param autoFocus オートフォーカス
+ */
+ public void setAutoFocus(final boolean autoFocus) {
+ setByteControl(CT_FOCUS_AUTO_CONTROL, autoFocus ? 1 : 0);
+ }
+
+ /**
+ * フォーカスがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedFocus() {
+ return isSupported(CT_FOCUS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * フォーカスを設定します.
+ *
+ * @param focus フォーカス
+ */
+ public void setFocus(final int focus) {
+ if (focus < getMinFocus() || getMaxFocus() < focus) {
+ throw new IllegalArgumentException("focus is invalid.");
+ }
+ setShortControl(CT_FOCUS_ABSOLUTE_CONTROL, focus);
+ }
+
+ /**
+ * フォーカスを取得します.
+ *
+ * @return フォーカス
+ */
+ public int getFocus() {
+ return getCurSettingData(CT_FOCUS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * フォーカスの最大値を取得します.
+ *
+ * @return フォーカスの最大値
+ */
+ public int getMaxFocus() {
+ return getMaxSettingData(CT_FOCUS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * フォーカスの最小値を取得します.
+ *
+ * @return フォーカスの最小値
+ */
+ public int getMinFocus() {
+ return getMinSettingData(CT_FOCUS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * アイリス (F値) がサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedIris() {
+ return isSupported(CT_IRIS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * アイリス (F値) を設定します.
+ *
+ * 明るさ調整.
+ *
+ * @param iris アイリス値
+ */
+ public void setIris(final int iris) {
+ if (iris < getMinIris() || getMaxIris() < iris) {
+ throw new IllegalArgumentException("iris is invalid.");
+ }
+ setShortControl(CT_IRIS_ABSOLUTE_CONTROL, iris);
+ }
+
+ /**
+ * アイリス (F値) を取得します.
+ *
+ * @return アイリス (F値)
+ */
+ public int getIris() {
+ return getCurSettingData(CT_IRIS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * アイリス (F値) の最大値を取得します.
+ *
+ * @return アイリス (F値)の最大値
+ */
+ public int getMaxIris() {
+ return getMaxSettingData(CT_IRIS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * アイリス (F値) の最小値を取得します.
+ *
+ * @return アイリス (F値)の最小値
+ */
+ public int getMinIris() {
+ return getMinSettingData(CT_IRIS_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * ロールがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedRoll() {
+ return isSupported(CT_ROLL_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * ロールを設定します.
+ *
+ * @param roll ロール
+ */
+ public void setRoll(final int roll) {
+ if (roll < getMinRoll() || getMaxRoll() < roll) {
+ throw new IllegalArgumentException("roll is invalid.");
+ }
+ setShortControl(CT_ROLL_ABSOLUTE_CONTROL, roll);
+ }
+
+ /**
+ * ロールを取得します.
+ *
+ * @return 設定されているロール
+ */
+ public int getRoll() {
+ return getCurSettingData(CT_ROLL_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * ロールの最大値を取得します.
+ *
+ * @return ロールの最大値
+ */
+ public int getMaxRoll() {
+ return getMaxSettingData(CT_ROLL_ABSOLUTE_CONTROL);
+ }
+
+ /**
+ * ロールの最小値を取得します.
+ *
+ * @return ロールの最小値
+ */
+ public int getMinRoll() {
+ return getMinSettingData(CT_ROLL_ABSOLUTE_CONTROL);
+ }
+
+ //// PU ////
+
+ /**
+ * ブライトネスがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedBrightness() {
+ return isSupported(PU_BRIGHTNESS_CONTROL);
+ }
+
+ /**
+ * ブライトネスを設定します.
+ *
+ * @param brightness ブライトネス
+ */
+ public void setBrightness(final int brightness) {
+ if (brightness < getMinBrightness() || getMaxBrightness() < brightness) {
+ throw new IllegalArgumentException("brightness is invalid.");
+ }
+ setShortControl(PU_BRIGHTNESS_CONTROL, brightness);
+ }
+
+ /**
+ * ブライトネスを取得します.
+ *
+ * @return ブライトネス
+ */
+ public int getBrightness() {
+ return getCurSettingData(PU_BRIGHTNESS_CONTROL);
+ }
+
+ /**
+ * ブライトネスの最大値を取得します.
+ *
+ * @return ブライトネスの最大値
+ */
+ public int getMaxBrightness() {
+ return getMaxSettingData(PU_BRIGHTNESS_CONTROL);
+ }
+
+ /**
+ * ブライトネスの最小値を取得します.
+ *
+ * @return ブライトネスの最小値
+ */
+ public int getMinBrightness() {
+ return getMinSettingData(PU_BRIGHTNESS_CONTROL);
+ }
+
+ /**
+ * コントラストがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedContrast() {
+ return isSupported(PU_BRIGHTNESS_CONTROL);
+ }
+
+ /**
+ * コントラストを設定します.
+ *
+ * @param contrast コントラスト
+ */
+ public void setContrast(final int contrast) {
+ if (contrast < getMinContrast() || getMaxContrast() < contrast) {
+ throw new IllegalArgumentException("contrast is invalid");
+ }
+ setShortControl(PU_CONTRAST_CONTROL, contrast);
+ }
+
+ /**
+ * コントラストを取得します.
+ *
+ * @return コントラスト
+ */
+ public int getContrast() {
+ return getCurSettingData(PU_CONTRAST_CONTROL);
+ }
+
+ /**
+ * コントラストの最大値を取得します.
+ *
+ * @return コントラストの最大値
+ */
+ public int getMaxContrast() {
+ return getMaxSettingData(PU_CONTRAST_CONTROL);
+ }
+
+ /**
+ * コントラストの最小値を取得します.
+ *
+ * @return コントラストの最小値
+ */
+ public int getMinContrast() {
+ return getMinSettingData(PU_CONTRAST_CONTROL);
+ }
+
+ /**
+ * ゲインがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedGain() {
+ return isSupported(PU_GAIN_CONTROL);
+ }
+
+ /**
+ * ゲインを設定します.
+ *
+ * @param gain ゲイン
+ */
+ public void setGain(final int gain) {
+ if (gain < getMinGain() || getMaxGain() < gain) {
+ throw new IllegalArgumentException("gain is invalid");
+ }
+ setShortControl(PU_GAIN_CONTROL, gain);
+ }
+
+ /**
+ * ゲインを取得します.
+ *
+ * @return ゲイン
+ */
+ public int getGain() {
+ return getCurSettingData(PU_GAIN_CONTROL);
+ }
+
+ /**
+ * ゲインの最大値を取得します.
+ *
+ * @return ゲインの最大値
+ */
+ public int getMaxGain() {
+ return getCurSettingData(PU_GAIN_CONTROL);
+ }
+
+ /**
+ * ゲインの最小値を取得します.
+ *
+ * @return ゲインの最小値
+ */
+ public int getMinGain() {
+ return getCurSettingData(PU_GAIN_CONTROL);
+ }
+
+ /**
+ * Hue (色相)がサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedHue() {
+ return isSupported(PU_HUE_CONTROL);
+ }
+
+ /**
+ * Hue を設定します.
+ *
+ * @param hue Hueの値
+ */
+ public void setHue(final int hue) {
+ if (hue < getMinHue() || getMaxHue() < hue) {
+ throw new IllegalArgumentException("hue is invalid.");
+ }
+ setShortControl(PU_HUE_CONTROL, hue);
+ }
+
+ /**
+ * Hue を取得します.
+ *
+ * @return Hue
+ */
+ public int getHue() {
+ return getCurSettingData(PU_HUE_CONTROL);
+ }
+
+ /**
+ * Hue の最大値を取得します.
+ *
+ * @return Hueの最大値
+ */
+ public int getMaxHue() {
+ return getMaxSettingData(PU_HUE_CONTROL);
+ }
+
+ /**
+ * Hue の最小値を取得します.
+ *
+ * @return Hueの最小値
+ */
+ public int getMinHue() {
+ return getMinSettingData(PU_HUE_CONTROL);
+ }
+
+ /**
+ * Saturation がサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedSaturation() {
+ return isSupported(PU_SATURATION_CONTROL);
+ }
+
+ /**
+ * Saturation (彩度)を設定します.
+ *
+ * @param saturation Saturation
+ */
+ public void setSaturation(final int saturation) {
+ if (saturation < getMinSaturation() || getMaxSaturation() < saturation) {
+ throw new IllegalArgumentException("saturation is invalid.");
+ }
+ setShortControl(PU_SATURATION_CONTROL, saturation);
+ }
+
+ /**
+ * Saturation を取得します.
+ *
+ * @return Saturation
+ */
+ public int getSaturation() {
+ return getCurSettingData(PU_SATURATION_CONTROL);
+ }
+
+ /**
+ * Saturation の最大値を取得します.
+ *
+ * @return Saturationの最大値
+ */
+ public int getMaxSaturation() {
+ return getMaxSettingData(PU_SATURATION_CONTROL);
+ }
+
+ /**
+ * Saturation の最小値を取得します.
+ *
+ * @return Saturationの最小値
+ */
+ public int getMinSaturation() {
+ return getMinSettingData(PU_SATURATION_CONTROL);
+ }
+
+ /**
+ * シャープネスがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedSharpness() {
+ return isSupported(PU_SHARPNESS_CONTROL);
+ }
+
+ /**
+ * シャープネスを設定します.
+ *
+ * @param sharpness シャープネス
+ */
+ public void setSharpness(final int sharpness) {
+ if (sharpness < getMinSharpness() || getMaxSharpness() < sharpness) {
+ throw new IllegalArgumentException("sharpness is invalid.");
+ }
+ setShortControl(PU_SHARPNESS_CONTROL, sharpness);
+ }
+
+ /**
+ * シャープネスを取得します.
+ *
+ * @return シャープネス
+ */
+ public int getSharpness() {
+ return getCurSettingData(PU_SHARPNESS_CONTROL);
+ }
+
+ /**
+ * シャープネスの最大値を取得します.
+ *
+ * @return シャープネスの最大値
+ */
+ public int getMaxSharpness() {
+ return getMaxSettingData(PU_SHARPNESS_CONTROL);
+ }
+
+ /**
+ * シャープネスの最小値を取得します.
+ *
+ * @return シャープネスの最小値
+ */
+ public int getMinSharpness() {
+ return getMinSettingData(PU_SHARPNESS_CONTROL);
+ }
+
+ /**
+ * ガンマがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedGamma() {
+ return isSupported(PU_GAMMA_CONTROL);
+ }
+
+ /**
+ * ガンマを設定します.
+ *
+ * @param gamma ガンマ
+ */
+ public void setGamma(final int gamma) {
+ if (gamma < getMinGamma() || getMaxGamma() < gamma) {
+ throw new IllegalArgumentException("gamma is invalid");
+ }
+ setShortControl(PU_GAMMA_CONTROL, gamma);
+ }
+
+ /**
+ * ガンマを取得します.
+ *
+ * @return ガンマ
+ */
+ public int getGamma() {
+ return getCurSettingData(PU_GAMMA_CONTROL);
+ }
+
+ /**
+ * ガンマの最大値を取得します.
+ *
+ * @return ガンマの最大値
+ */
+ public int getMaxGamma() {
+ return getMaxSettingData(PU_GAMMA_CONTROL);
+ }
+
+ /**
+ * ガンマの最小値を取得します.
+ *
+ * @return ガンマの最小値
+ */
+ public int getMinGamma() {
+ return getMinSettingData(PU_GAMMA_CONTROL);
+ }
+
+ /**
+ * ホワイトバランステンパラチャーがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedWhiteBalanceTemperature() {
+ return isSupported(PU_WHITE_BALANCE_TEMPERATURE_CONTROL);
+ }
+
+ /**
+ * ホワイトバランステンパラチャーを設定します.
+ *
+ * @param whiteBalanceTemperature ホワイトバランステンパラチャー
+ */
+ public void setWhiteBalanceTemperature(final int whiteBalanceTemperature) {
+ if (whiteBalanceTemperature < getMinWhiteBalanceTemperature() ||
+ getMaxWhiteBalanceTemperature() < whiteBalanceTemperature) {
+ throw new IllegalArgumentException("whiteBalanceTemperature is invalid.");
+ }
+ setShortControl(PU_WHITE_BALANCE_TEMPERATURE_CONTROL, whiteBalanceTemperature);
+ }
+
+ /**
+ * ホワイトバランステンパラチャーを取得します.
+ *
+ * @return ホワイトバランステンパラチャー
+ */
+ public int getWhiteBalanceTemperature() {
+ return getCurSettingData(PU_WHITE_BALANCE_TEMPERATURE_CONTROL);
+ }
+
+ /**
+ * ホワイトバランステンパラチャーの最大値を取得します.
+ *
+ * @return ホワイトバランステンパラチャーの最大値
+ */
+ public int getMaxWhiteBalanceTemperature() {
+ return getMaxSettingData(PU_WHITE_BALANCE_TEMPERATURE_CONTROL);
+ }
+
+ /**
+ * ホワイトバランステンパラチャーの最小値を取得します.
+ *
+ * @return ホワイトバランステンパラチャーの最小値
+ */
+ public int getMinWhiteBalanceTemperature() {
+ return getMinSettingData(PU_WHITE_BALANCE_TEMPERATURE_CONTROL);
+ }
+
+ /**
+ * オートコントラストがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedAutoContrast() {
+ return isSupported(PU_CONTRAST_AUTO_CONTROL);
+ }
+
+ /**
+ * オートコントラストの有効・無効を取得します.
+ *
+ * @return trueの場合は有効、falseの場合は無効
+ */
+ public boolean isAutoContrast() {
+ return isSettingData(PU_CONTRAST_AUTO_CONTROL);
+ }
+
+ /**
+ * オートコントラストを設定します.
+ *
+ * @param autoContrast 有効にする場合はtrue、無効にする場合にはfalse
+ */
+ public void setAutoContrast(final boolean autoContrast) {
+ setByteControl(PU_CONTRAST_AUTO_CONTROL, autoContrast ? 1 : 0);
+ }
+
+ /**
+ * オート Hue がサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedAutoHue() {
+ return isSupported(PU_HUE_AUTO_CONTROL);
+ }
+
+ /**
+ * オート Hue の有効・無効を取得します.
+ *
+ * @return trueの場合は有効、falseの場合は無効
+ */
+ public boolean isAutoHue() {
+ return isSettingData(PU_HUE_AUTO_CONTROL);
+ }
+
+ /**
+ * オート Hue を設定します.
+ *
+ * @param autoHue 有効にする場合はtrue、無効にする場合にはfalse
+ */
+ public void setAutoHue(final boolean autoHue) {
+ setByteControl(PU_HUE_AUTO_CONTROL, autoHue ? 1 : 0);
+ }
+
+ /**
+ * オートホワイトバランステンパラチャーがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedAutoWhiteBalanceTemperature() {
+ return isSupported(PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL);
+ }
+
+ /**
+ * オートホワイトバランステンパラチャーの有効・無効を取得します.
+ *
+ * @return trueの場合は有効、falseの場合は無効
+ */
+ public boolean isAutoWhiteBalanceTemperature() {
+ return isSettingData(PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL);
+ }
+
+ /**
+ * オートホワイトバランステンパラチャーを設定します.
+ *
+ * @param autoWhiteBalanceTemperature 有効にする場合はtrue、無効にする場合にはfalse
+ */
+ public void setAutoWhiteBalanceTemperature(boolean autoWhiteBalanceTemperature) {
+ setByteControl(PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, autoWhiteBalanceTemperature ? 1 : 0);
+ }
+
+ /**
+ * オートホワイトバランスコンポーネントがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedAutoWhiteBalanceComponent() {
+ return isSupported(PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL);
+ }
+
+ /**
+ * オートホワイトバランスコンポーネントの有効・無効を取得します.
+ *
+ * @return trueの場合は有効、falseの場合は無効
+ */
+ public boolean isAutoWhiteBalanceComponent() {
+ return isSettingData(PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL);
+ }
+
+ /**
+ * オートホワイトバランスコンポーネントを設定します.
+ *
+ * @param autoWhiteBalanceComponent 有効にする場合はtrue、無効にする場合にはfalse
+ */
+ public void setAutoWhiteBalanceComponent(final boolean autoWhiteBalanceComponent) {
+ setByteControl(PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, autoWhiteBalanceComponent ? 1 : 0);
+ }
+
+ /**
+ * ホワイトバランスコンポーネントがサポートされているか確認します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ public boolean isSupportedWhiteBalanceComponent() {
+ return isSupported(PU_WHITE_BALANCE_COMPONENT_CONTROL);
+ }
+
+ /**
+ * ホワイトバランスコンポーネントを設定します.
+ *
+ * @param component ホワイトバランスコンポーネント
+ */
+ public void setWhiteBalanceComponent(final WhiteBalanceComponent component) {
+ Data data = mProcessingUnitControls.get(PU_WHITE_BALANCE_COMPONENT_CONTROL);
+ if (data != null && data.isSupported()) {
+ byte[] value = new byte[4];
+ value[0] = (byte) (component.mBlue & 0xFF);
+ value[1] = (byte) ((component.mBlue >> 8) & 0xFF);
+ value[2] = (byte) (component.mRed & 0xFF);
+ value[3] = (byte) ((component.mRed >> 8) & 0xFF);
+ int result = setArrayControl(PU_WHITE_BALANCE_COMPONENT_CONTROL, value);
+ if (result == UVCCamera.UVC_SUCCESS) {
+ ((ArrayData) data).mCurrentValue[0] = component.mBlue;
+ ((ArrayData) data).mCurrentValue[1] = component.mRed;
+ }
+
+ // TODO 値の設定に失敗した場合の処理
+
+ return;
+ }
+ throw new UnsupportedOperationException(PU_WHITE_BALANCE_COMPONENT_CONTROL + " is not supported.");
+ }
+
+ /**
+ * ホワイトバランスコンポーネントを取得します.
+ *
+ * @return ホワイトバランスコンポーネント
+ */
+ public WhiteBalanceComponent getWhiteBalanceComponent() {
+ Data data = mProcessingUnitControls.get(PU_WHITE_BALANCE_COMPONENT_CONTROL);
+ if (data != null && data.isSupported()) {
+ int[] value = ((ArrayData) data).mCurrentValue;
+ return new WhiteBalanceComponent(value[0], value[1]);
+ }
+ throw new UnsupportedOperationException(PU_WHITE_BALANCE_COMPONENT_CONTROL + " is not supported.");
+ }
+
+ /**
+ * ホワイトバランスコンポーネントの最大値を取得します.
+ *
+ * @return ホワイトバランスコンポーネントの最大値
+ */
+ public WhiteBalanceComponent getMaxWhiteBalanceComponent() {
+ Data data = mProcessingUnitControls.get(PU_WHITE_BALANCE_COMPONENT_CONTROL);
+ if (data != null && data.isSupported()) {
+ int[] value = ((ArrayData) data).mMaxValue;
+ return new WhiteBalanceComponent(value[0], value[1]);
+ }
+ throw new UnsupportedOperationException(PU_WHITE_BALANCE_COMPONENT_CONTROL + " is not supported.");
+ }
+
+ /**
+ * ホワイトバランスコンポーネントの最小値を取得します.
+ *
+ * @return ホワイトバランスコンポーネントの最小値
+ */
+ public WhiteBalanceComponent getMinWhiteBalanceComponent() {
+ Data data = mProcessingUnitControls.get(PU_WHITE_BALANCE_COMPONENT_CONTROL);
+ if (data != null && data.isSupported()) {
+ int[] value = ((ArrayData) data).mMinValue;
+ return new WhiteBalanceComponent(value[0], value[1]);
+ }
+ throw new UnsupportedOperationException(PU_WHITE_BALANCE_COMPONENT_CONTROL + " is not supported.");
+ }
+
+
+ /// CT
+
+ /**
+ * 指定されたカメラターミナルコントロールのサポート状況を取得します.
+ *
+ * @param control コントロール
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ private boolean isSupported(final CameraTerminalControl control) {
+ Data result = mCameraTerminalControls.get(control);
+ return result != null && result.isSupported();
+ }
+
+ /**
+ * 指定されたカメラターミナルコントロールに Int 値(4byte)を設定します.
+ *
+ * @param ctrl コントロール
+ * @param value 設定する値
+ */
+ private void setIntControl(final CameraTerminalControl ctrl, final int value) {
+ Data data = mCameraTerminalControls.get(ctrl);
+ if (data != null) {
+ int result = setInt(ctrl, value);
+ if (result == UVCCamera.UVC_SUCCESS) {
+ ((SettingData) data).mCurrentValue = value;
+ }
+
+ // TODO 値の設定に失敗した場合の処理
+
+ return;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたカメラターミナルコントロールに Short 値(2byte)を設定します.
+ *
+ * @param ctrl コントロール
+ * @param value 設定する値
+ */
+ private void setShortControl(final CameraTerminalControl ctrl, final int value) {
+ Data data = mCameraTerminalControls.get(ctrl);
+ if (data != null) {
+ int result = setShort(ctrl, value);
+ if (result == UVCCamera.UVC_SUCCESS) {
+ ((SettingData) data).mCurrentValue = value;
+ }
+
+ // TODO 値の設定に失敗した場合の処理
+
+ return;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたカメラターミナルコントロールに Byte 値(1byte)を設定します.
+ *
+ * @param ctrl コントロール
+ * @param value 設定する値
+ */
+ private void setByteControl(final CameraTerminalControl ctrl, final int value) {
+ Data data = mCameraTerminalControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ int result = setByte(ctrl, value);
+ if (result == UVCCamera.UVC_SUCCESS) {
+ ((SettingData) data).mCurrentValue = value;
+ }
+
+ // TODO 値の設定に失敗した場合の処理
+
+ return;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたカメラターミナルコントロールの値を取得します.
+ *
+ * @param ctrl コントロール
+ * @return 設定されている値
+ */
+ private int getCurSettingData(final CameraTerminalControl ctrl) {
+ Data data = mCameraTerminalControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mCurrentValue;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたカメラターミナルコントロールの最大値を取得します.
+ *
+ * @param ctrl コントロール
+ * @return 最大値
+ */
+ private int getMaxSettingData(final CameraTerminalControl ctrl) {
+ Data data = mCameraTerminalControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mMaxValue;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたカメラターミナルコントロールの最小値を取得します.
+ *
+ * @param ctrl コントロール
+ * @return 最小値
+ */
+ private int getMinSettingData(final CameraTerminalControl ctrl) {
+ Data data = mCameraTerminalControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mMinValue;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたカメラターミナルコントロールの設定値を boolean で取得します.
+ *
+ * @param ctrl コントロール
+ * @return 1が設定されている場合はtrue、それ以外はfalse
+ */
+ private boolean isSettingData(CameraTerminalControl ctrl) {
+ Data data = mCameraTerminalControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mCurrentValue == 1;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+
+ // PU
+
+
+ /**
+ * 指定されたプロセシングユニットコントロールのサポート状況を取得します.
+ *
+ * @param control コントロール
+ * @return サポートされている場合はtrue、それ以外はfalse
+ */
+ private boolean isSupported(final ProcessingUnitControl control) {
+ Data result = mProcessingUnitControls.get(control);
+ return result != null && result.isSupported();
+ }
+
+ /**
+ * 指定されたプロセシングユニットコントロールに Short 値(2byte)を設定します.
+ *
+ * @param ctrl コントロール
+ * @param value 設定する値(short)
+ */
+ private void setShortControl(final ProcessingUnitControl ctrl, final int value) {
+ Data data = mProcessingUnitControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ int result = setShort(ctrl, value);
+ if (result == UVCCamera.UVC_SUCCESS) {
+ ((SettingData) data).mCurrentValue = value;
+ }
+
+ // TODO 値の設定に失敗した場合の処理
+
+ return;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたプロセシングユニットコントロールに Byte 値(1byte)を設定します.
+ *
+ * @param ctrl コントロール
+ * @param value 設定する値(byte)
+ */
+ private void setByteControl(final ProcessingUnitControl ctrl, final int value) {
+ Data data = mProcessingUnitControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ int result = setByte(ctrl, value);
+ if (result == UVCCamera.UVC_SUCCESS) {
+ ((SettingData) data).mCurrentValue = value;
+ }
+
+ // TODO 値の設定に失敗した場合の処理
+
+ return;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたプロセシングユニットコントロールの値を取得します.
+ *
+ * @param ctrl コントロール
+ * @return 設定されている値
+ */
+ private int getCurSettingData(final ProcessingUnitControl ctrl) {
+ Data data = mProcessingUnitControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mCurrentValue;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたプロセシングユニットコントロールの最大値を取得します.
+ *
+ * @param ctrl コントロール
+ * @return 最大値
+ */
+ private int getMaxSettingData(ProcessingUnitControl ctrl) {
+ Data data = mProcessingUnitControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mMaxValue;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたプロセシングユニットコントロールの最小値を取得します.
+ *
+ * @param ctrl コントロール
+ * @return 最小値
+ */
+ private int getMinSettingData(ProcessingUnitControl ctrl) {
+ Data data = mProcessingUnitControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mMinValue;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+ /**
+ * 指定されたプロセシングユニットコントロールの最小値を取得します.
+ *
+ * @param ctrl コントロール
+ * @return 最小値
+ */
+ private boolean isSettingData(ProcessingUnitControl ctrl) {
+ Data data = mProcessingUnitControls.get(ctrl);
+ if (data != null && data.isSupported()) {
+ return ((SettingData) data).mCurrentValue == 1;
+ }
+ throw new UnsupportedOperationException(ctrl + " is not supported.");
+ }
+
+
+ //// 初期化処理
+
+
+ /**
+ * カメラターミナルコントロールのサポート状況の設定を行います.
+ *
+ * MEMO: NDK側から呼び出されるので、変更する場合には十分に注意すること。
+ *
+ * @param control コントロールID
+ * @param support サポート状況
+ */
+ void putCameraTerminalControls(final int control, final boolean support) {
+ Data data;
+ CameraTerminalControl ct = CameraTerminalControl.valueOf(control);
+ switch (ct) {
+ case CT_EXPOSURE_TIME_ABSOLUTE_CONTROL: {
+ SettingData d = new SettingData();
+ d.mSupported = support;
+ if (support) {
+ d.mMaxValue = getIntMax(ct);
+ d.mMinValue = getIntMin(ct);
+ d.mDefaultValue = getIntDef(ct);
+ d.mCurrentValue = getIntCur(ct);
+ }
+ data = d;
+ } break;
+
+ case CT_ROLL_ABSOLUTE_CONTROL:
+ case CT_IRIS_ABSOLUTE_CONTROL:
+ case CT_FOCUS_ABSOLUTE_CONTROL:
+ case CT_ZOOM_ABSOLUTE_CONTROL: {
+ SettingData d = new SettingData();
+ d.mSupported = support;
+ if (support) {
+ d.mMaxValue = getShortMax(ct);
+ d.mMinValue = getShortMin(ct);
+ d.mDefaultValue = getShortDef(ct);
+ d.mCurrentValue = getShortCur(ct);
+ }
+ data = d;
+ } break;
+
+ case CT_SCANNING_MODE_CONTROL:
+ case CT_AE_MODE_CONTROL:
+ case CT_AE_PRIORITY_CONTROL:
+ case CT_FOCUS_AUTO_CONTROL: {
+ SettingData d = new SettingData();
+ d.mSupported = support;
+ if (support) {
+ d.mDefaultValue = getByteDef(ct);
+ d.mCurrentValue = getByteCur(ct);
+ }
+ data = d;
+ } break;
+
+ default:
+ data = new Data();
+ data.mSupported = support;
+ break;
+ }
+
+ mCameraTerminalControls.put(ct, data);
+ }
+
+ /**
+ * プロセシングユニットコントロールのサポート状況の設定を行います.
+ *
+ * MEMO: NDK側から呼び出されるので、変更する場合には十分に注意すること。
+ *
+ * @param control コントロールID
+ * @param support サポート状況
+ */
+ void putProcessingUnitControls(final int control, final boolean support) {
+ Data data;
+ ProcessingUnitControl pu = ProcessingUnitControl.valueOf(control);
+ switch (pu) {
+ case PU_WHITE_BALANCE_TEMPERATURE_CONTROL:
+ case PU_GAMMA_CONTROL:
+ case PU_SHARPNESS_CONTROL:
+ case PU_SATURATION_CONTROL:
+ case PU_HUE_CONTROL:
+ case PU_GAIN_CONTROL:
+ case PU_CONTRAST_CONTROL:
+ case PU_BRIGHTNESS_CONTROL: {
+ SettingData d = new SettingData();
+ d.mSupported = support;
+ if (support) {
+ d.mMaxValue = getShortMax(pu);
+ d.mMinValue = getShortMin(pu);
+ d.mDefaultValue = getShortDef(pu);
+ d.mCurrentValue = getShortCur(pu);
+ }
+ data = d;
+ } break;
+
+ case PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL:
+ case PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL:
+ case PU_HUE_AUTO_CONTROL:
+ case PU_CONTRAST_AUTO_CONTROL: {
+ SettingData d = new SettingData();
+ d.mSupported = support;
+ if (support) {
+ d.mDefaultValue = getByteDef(pu);
+ d.mCurrentValue = getByteCur(pu);
+ }
+ data = d;
+ } break;
+
+ case PU_WHITE_BALANCE_COMPONENT_CONTROL: {
+ ArrayData d = new ArrayData(2);
+ d.mSupported = support;
+ if (support) {
+ byte[] value = new byte[4];
+ getArrayMax(pu, value);
+ d.mMaxValue[0] = (value[1] & 0xFF) << 8 | (value[0] & 0xFF);
+ d.mMaxValue[1] = (value[3] & 0xFF) << 8 | (value[2] & 0xFF);
+
+ getArrayMin(pu, value);
+ d.mMinValue[0] = (value[1] & 0xFF) << 8 | (value[0] & 0xFF);
+ d.mMinValue[1] = (value[3] & 0xFF) << 8 | (value[2] & 0xFF);
+
+ getArrayDef(pu, value);
+ d.mDefaultValue[0] = (value[1] & 0xFF) << 8 | (value[0] & 0xFF);
+ d.mDefaultValue[1] = (value[3] & 0xFF) << 8 | (value[2] & 0xFF);
+
+ getArrayDef(pu, value);
+ d.mCurrentValue[0] = (value[1] & 0xFF) << 8 | (value[0] & 0xFF);
+ d.mCurrentValue[1] = (value[3] & 0xFF) << 8 | (value[2] & 0xFF);
+ }
+ data = d;
+ } break;
+
+ default:
+ data = new Data();
+ data.mSupported = support;
+ break;
+ }
+
+ mProcessingUnitControls.put(pu, data);
+ }
+
+
+ private int setArrayControl(Control control, byte[] value) {
+ return mUVCCamera.setControl(control, value);
+ }
+
+ private int getArrayMax(Control control, byte[] value) {
+ return getArray(control, RequestType.GET_MAX, value);
+ }
+
+ private int getArrayMin(Control control, byte[] value) {
+ return getArray(control, RequestType.GET_MIN, value);
+ }
+
+ private int getArrayDef(Control control, byte[] value) {
+ return getArray(control, RequestType.GET_DEF, value);
+ }
+
+ private int getArrayCur(Control control, byte[] value) {
+ return getArray(control, RequestType.GET_CUR, value);
+ }
+
+ private int getArray(Control control, RequestType request, byte[] value) {
+ return mUVCCamera.getControl(control, request, value);
+ }
+
+
+
+ private int getIntMax(Control control) {
+ return getInt(control, RequestType.GET_MAX);
+ }
+
+ private int getIntMin(Control control) {
+ return getInt(control, RequestType.GET_MIN);
+ }
+
+ private int getIntDef(Control control) {
+ return getInt(control, RequestType.GET_DEF);
+ }
+
+ private int getIntCur(Control control) {
+ return getInt(control, RequestType.GET_CUR);
+ }
+
+ private int getInt(Control control, RequestType request) {
+ byte[] value = new byte[2];
+ int ret = mUVCCamera.getControl(control, request, value);
+ if (ret != UVCCamera.UVC_SUCCESS) {
+ // TODO 取得に失敗した場合の処理
+ }
+ return ((value[3] & 0xFF) << 24) | ((value[2] & 0xFF) << 16) |
+ ((value[1] & 0xFF) << 8) | (value[0] & 0xFF);
+ }
+
+ private int setInt(Control control, int value) {
+ byte[] buf = new byte[4];
+ buf[0] = (byte) (value & 0xFF);
+ buf[1] = (byte) ((value >> 8) & 0xFF);
+ buf[2] = (byte) ((value >> 16) & 0xFF);
+ buf[3] = (byte) ((value >> 24) & 0xFF);
+ return mUVCCamera.setControl(control, buf);
+ }
+
+
+
+ private int getShortMax(Control control ) {
+ return getShort(control, RequestType.GET_MAX);
+ }
+
+ private int getShortMin(Control control) {
+ return getShort(control, RequestType.GET_MIN);
+ }
+
+ private int getShortDef(Control control) {
+ return getShort(control, RequestType.GET_DEF);
+ }
+
+ private int getShortCur(Control control) {
+ return getShort(control, RequestType.GET_CUR);
+ }
+
+ private int getShort(Control control, RequestType request) {
+ byte[] value = new byte[2];
+ int ret = mUVCCamera.getControl(control, request, value);
+ if (ret != UVCCamera.UVC_SUCCESS) {
+ // TODO 取得に失敗した場合の処理
+ }
+ return (value[1] & 0xFF) << 8 | (value[0] & 0xFF);
+ }
+
+ private int setShort(Control control, int value) {
+ byte[] buf = new byte[2];
+ buf[0] = (byte) (value & 0xFF);
+ buf[1] = (byte) ((value >> 8) & 0xFF);
+ return mUVCCamera.setControl(control, buf);
+ }
+
+
+ /**
+ * デフォルトの値を取得します.
+ *
+ * @param control コントロール
+ * @return 取得した値
+ */
+ private int getByteDef(final Control control) {
+ return getByte(control, RequestType.GET_DEF);
+ }
+
+ /**
+ * 現在の値を取得します.
+ *
+ * @param control コントロール
+ * @return 取得した値
+ */
+ private int getByteCur(Control control) {
+ return getByte(control, RequestType.GET_CUR);
+ }
+
+ /**
+ * 値を取得します.
+ *
+ * @param control コントロール
+ * @param request リクエスト
+ * @return 取得した値
+ */
+ private int getByte(Control control, RequestType request) {
+ byte[] value = new byte[1];
+ int ret = mUVCCamera.getControl(control, request, value);
+ if (ret != UVCCamera.UVC_SUCCESS) {
+ // TODO 取得に失敗した場合の処理
+ }
+ return value[0] & 0xFF;
+ }
+
+ /**
+ * 指定された値を設定します.
+ *
+ * @param control コントロール
+ * @param value 設定する値
+ * @return 0の場合は設定成功、それ以外は失敗
+ */
+ private int setByte(Control control, int value) {
+ byte[] buf = new byte[1];
+ buf[0] = (byte) (value & 0xFF);
+ return mUVCCamera.setControl(control, buf);
+ }
+
+
+ @Override
+ public String toString() {
+ return "CameraTerminalControls: " + mCameraTerminalControls + "\n" +
+ "ProcessingUnitControls: " + mProcessingUnitControls;
+ }
+
+ /**
+ * ホワイトバランスコンポーネントの設定情報を格納するクラス.
+ */
+ public class WhiteBalanceComponent {
+ /**
+ * 青色成分.
+ */
+ int mBlue;
+
+ /**
+ * 赤色成分.
+ */
+ int mRed;
+
+ /**
+ * コンストラクタ.
+ *
+ * @param blue 青色成分
+ * @param red 赤色成分
+ */
+ WhiteBalanceComponent(final int blue, final int red) {
+ mBlue = blue;
+ mRed = red;
+ }
+
+ /**
+ * 青色成分を取得します.
+ *
+ * @return 青色成分
+ */
+ public int getBlue() {
+ return mBlue;
+ }
+
+ /**
+ * 青色成分を設定します
+ * @param blue
+ */
+ public void setBlue(final int blue) {
+ mBlue = blue;
+ }
+
+ /**
+ * 赤色成分を取得します.
+ *
+ * @return 赤色成分
+ */
+ public int getRed() {
+ return mRed;
+ }
+
+ /**
+ * 赤色成分を設定します.
+ *
+ * @param red 赤色成分
+ */
+ public void setRed(final int red) {
+ mRed = red;
+ }
+ }
+
+ /**
+ * UVCからの設定データを格納するクラス.
+ */
+ private class Data {
+ /**
+ * サポート状況.
+ *
+ * サポートしている場合はtrue、それ以外はfalse
+ *
+ */
+ boolean mSupported;
+
+ /**
+ * サポート状況を取得します.
+ *
+ * @return サポートしている場合はtrue、それ以外はfalse
+ */
+ boolean isSupported() {
+ return mSupported;
+ }
+
+ @Override
+ public String toString() {
+ return "{\n" +
+ " Supported: " + mSupported + "\n" +
+ "}\n";
+ }
+ }
+
+ /**
+ * 設定情報を格納するクラス.
+ */
+ private class SettingData extends Data {
+ /**
+ * 設定できる最大値.
+ */
+ int mMaxValue;
+ /**
+ * 設定できる最小.
+ */
+ int mMinValue;
+
+ /**
+ * デフォルトで設定されている値.
+ */
+ int mDefaultValue;
+
+ /**
+ * 現在設定されている値.
+ */
+ int mCurrentValue;
+
+ @Override
+ public String toString() {
+ return "{\n" +
+ " Supported: " + mSupported + "\n" +
+ " Max: " + mMaxValue + "\n" +
+ " Min: " + mMinValue + "\n" +
+ " Default: " + mDefaultValue + "\n" +
+ " Current: " + mCurrentValue + "\n" +
+ "}\n";
+ }
+ }
+
+ /**
+ * 設定情報が配列で格納するクラス.
+ */
+ private class ArrayData extends Data {
+ int[] mMaxValue;
+ int[] mMinValue;
+ int[] mDefaultValue;
+ int[] mCurrentValue;
+
+ ArrayData(int len) {
+ mMaxValue = new int[len];
+ mMinValue = new int[len];
+ mDefaultValue = new int[len];
+ mCurrentValue = new int[len];
+ }
+
+ @Override
+ public String toString() {
+ return "{\n" +
+ " Supported: " + mSupported + "\n" +
+ " Max: " + mMaxValue + "\n" +
+ " Min: " + mMinValue + "\n" +
+ " Default: " + mDefaultValue + "\n" +
+ " Current: " + mCurrentValue + "\n" +
+ "}\n";
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Parameter.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Parameter.java
new file mode 100644
index 0000000000..061d2f994c
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/Parameter.java
@@ -0,0 +1,296 @@
+/*
+ Parameter.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * UVC カメラのプレビューを再生するときに設定するパラメータ.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class Parameter {
+ public static final float QUALITY_HIGH = 0.175f;
+ public static final float QUALITY_MIDDLE = 0.125f;
+ public static final float QUALITY_LOW = 0.100f;
+
+ private float mBPP = QUALITY_MIDDLE;
+ private boolean mUseH264 = false;
+
+ // 以下の変数は、NDK より値が設定されます。
+
+ /**
+ * Video Stream Format Descriptor の bFormatIndex を保持します.
+ */
+ private int mFormatIndex;
+
+ /**
+ * Video Stream Frame Descriptor の bDescriptorSubType を保持します.
+ */
+ private int mFrameType;
+
+ /**
+ * Video Stream Frame Descriptor の bFrameIndex を保持します.
+ */
+ private int mFrameIndex;
+
+ /**
+ * Video Stream Frame Descriptor の mWidth を保持します.
+ */
+ private int mWidth;
+
+ /**
+ * Video Stream Frame Descriptor の mHeight を保持します.
+ */
+ private int mHeight;
+
+ /**
+ * FPS を保持します.
+ */
+ private int mFps;
+
+ /**
+ * Video Stream Frame Descriptor の bFrameIntervalType から FPS を計算して保持します.
+ */
+ private int[] mFpsList;
+
+ /**
+ * 必要に応じて NDK からのパラメータを格納するマップ.
+ *
+ * 現在は値を Long で定義しているが、他の型が必要になったら再度検討すること。
+ */
+ private final HashMap mExtras = new HashMap<>();
+
+ Parameter() {
+ }
+
+ Parameter(Parameter p) {
+ mFormatIndex = p.mFormatIndex;
+ mFrameType = p.mFrameType;
+ mFrameIndex = p.mFrameIndex;
+ mWidth = p.mWidth;
+ mHeight = p.mHeight;
+ mFps = p.mFps;
+ mFpsList = p.mFpsList;
+ mBPP = p.mBPP;
+ mUseH264 = p.mUseH264;
+ for (String key : p.mExtras.keySet()) {
+ mExtras.put(key, p.mExtras.get(key));
+ }
+ }
+
+ /**
+ * NDK 側で使用するフォーマットのインデックスを取得します.
+ *
+ * @return フォーマットのインデックス
+ */
+ int getFormatIndex() {
+ return mFormatIndex;
+ }
+
+ /**
+ * NDK 側で使用するフレームのインデックスを取得します.
+ *
+ * @return フレームのインデックス
+ */
+ int getFrameIndex() {
+ return mFrameIndex;
+ }
+
+ /**
+ * フレームのフォーマットタイプを取得します.
+ *
+ * @return フレームのフォーマットタイプ
+ */
+ public FrameType getFrameType() {
+ return FrameType.valueOf(mFrameType);
+ }
+
+ /**
+ * フレームの横幅を取得します.
+ *
+ * @return 横幅
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * フレームの縦幅を取得します.
+ *
+ * @return 縦幅
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * FPSを取得します.
+ *
+ * @return FPS
+ */
+ public int getFps() {
+ return mFps;
+ }
+
+ /**
+ * FPSを設定します.
+ *
+ * @param fps FPS
+ * @throws IllegalArgumentException サポートしていないFPSが指定された場合に発生
+ */
+ public void setFPS(int fps) {
+ if (mFpsList != null && !hasSupportFps(fps)) {
+ throw new IllegalArgumentException("not support fps.");
+ }
+ mFps = fps;
+ }
+
+ /**
+ * フレームに指定できるFPSのリストを取得します.
+ *
+ * @return fps FPSの一覧
+ */
+ public int[] getSupportedFps() {
+ return mFpsList;
+ }
+
+ /**
+ * キーを指定してフレームが持っているオプションを取得します.
+ *
+ * 指定されたキーに対応するオプションがない場合には null を返却します。
+ *
+ * @param key キー
+ * @return オプションの値
+ */
+ public Long getExtra(final String key) {
+ return mExtras.get(key);
+ }
+
+ /**
+ * キーの一覧を取得します.
+ *
+ * @return キーの一覧
+ */
+ public Set getExtraKeys() {
+ return mExtras.keySet();
+ }
+
+ /**
+ * オプションに値を追加します.
+ *
+ * この関数はNDKから呼び出されるので変更する場合には注意してください。
+ *
+ * @param key キー
+ * @param value 値
+ */
+ void putExtra(final String key, final long value) {
+ mExtras.put(key, value);
+ }
+
+ /**
+ * Bits Per Pixel の係数を設定します.
+ *
+ * 以下の定数を定義しています。
+ *
+ * - {@code QUALITY_HIGH}
+ * - {@code QUALITY_MIDDLE}
+ * - {@code QUALITY_LOW}
+ *
+ *
+ *
+ * 0.01〜0.25の間で設定することができます。
+ * 係数が大きいほど、品質が上がりますが、容量が増えます。
+ *
+ * @param bpp Bits Per Pixel
+ */
+ public void setBPP(float bpp) {
+ if (bpp < 0.01f || bpp > 0.25f) {
+ throw new IllegalArgumentException();
+ }
+ mBPP = bpp;
+ }
+
+ /**
+ * BitRateを取得します.
+ *
+ * @return bitrate
+ */
+ public int getBitRate() {
+ return (int) (mBPP * getFps() * getWidth() * getHeight());
+ }
+
+ /**
+ * H264 Extension Unit を使用するか設定します.
+ *
+ * H264 Extension Unit を持っていない場合は、true を設定することができません。
+ *
+ * @param use 使用する場合は true、それ以外は false
+ */
+ public void setUseH264(boolean use) {
+ if (!mExtras.containsKey("h264")) {
+ return;
+ }
+ mUseH264 = use;
+ }
+
+ /**
+ * H264 Extension Unit を使用するか確認します.
+ *
+ * @return 使用する場合は true、それ以外は false
+ */
+ public boolean isUseH264() {
+ return mUseH264;
+ }
+
+ /**
+ * H264 の Extension Unit が存在するか確認します.
+ *
+ * @return Extension Unit を持っている場合は true、それ以外は false
+ */
+ public boolean hasExtH264() {
+ return mExtras.containsKey("h264");
+ }
+
+ private boolean hasSupportFps(int fps) {
+ for (int f : mFpsList) {
+ if (f == fps) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String toFpsList() {
+ StringBuilder builder = new StringBuilder();
+ if (mFpsList != null) {
+ for (int fps : mFpsList) {
+ if (builder.length() > 0) {
+ builder.append(", ");
+ }
+ builder.append(fps);
+ }
+ }
+ return "[ " + builder.toString() + " ]";
+ }
+
+ @Override
+ public String toString() {
+ return "Param: [ \n" +
+ " formatIndex: " + mFormatIndex + ",\n" +
+ " frameType: " + mFrameType + ",\n" +
+ " frameIndex: " + mFrameIndex + ",\n" +
+ " width: " + mWidth + ",\n" +
+ " height: " + mHeight + ",\n" +
+ " fps: " + mFps + ",\n" +
+ " useH264: " + mUseH264 + ",\n" +
+ " supportedFps: " + toFpsList() + ",\n" +
+ " extra: " + mExtras + "\n" +
+ "]\n";
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCamera.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCamera.java
new file mode 100644
index 0000000000..9e932e7734
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCamera.java
@@ -0,0 +1,661 @@
+/*
+ UVCCamera.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+import android.hardware.usb.UsbDevice;
+import android.os.Build;
+import android.util.Log;
+
+import org.deviceconnect.android.libusb.UsbSerialPort;
+import org.deviceconnect.android.libuvc.utils.QueueThread;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UVCカメラ操作クラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class UVCCamera {
+ /**
+ * 処理に成功を定義します.
+ *
+ * NDK側で定義されている値に合わせる必要があります.
+ *
+ */
+ static final int UVC_SUCCESS = 0;
+
+ /**
+ * UsbSerialPortのインスタンス.
+ */
+ private UsbSerialPort mUsbSerialPort;
+
+ /**
+ * NDK側のポインタを格納する変数.
+ */
+ private long mNativePtr;
+
+ /**
+ * UVCカメラのフレームパラメータをキャッシュするためのリスト.
+ */
+ private final ArrayList mParameters = new ArrayList<>();
+
+ /**
+ * 設定されたフレームパラメータ.
+ */
+ private Parameter mCurrentParameter;
+
+ /**
+ * UVCカメラのオプション.
+ */
+ private Option mOption;
+
+ /**
+ * UVCカメラのプレビュー処理を行うスレッド.
+ */
+ private Thread mPreviewThread;
+
+ /**
+ * フレームを通知するためのスレッド.
+ */
+ private DispatchThread mDispatchThread;
+
+ /**
+ * プレビューのバッファを格納するバッファ.
+ */
+ private final FrameProvider mFrameProvider = new FrameProvider();
+
+ /**
+ * プレビューのフレーム通知を行うコールバック.
+ */
+ private PreviewCallback mPreviewCallback;
+
+ /**
+ * イベントを通知するリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ /**
+ * デバイス名.
+ */
+ private String mDeviceName;
+
+ private String mDeviceId;
+
+ /**
+ * コンストラクタ.
+ * @param port USBデバイス
+ */
+ UVCCamera(final UsbSerialPort port) {
+ if (port == null) {
+ throw new IllegalArgumentException("port is null.");
+ }
+ mUsbSerialPort = port;
+ }
+
+ /**
+ * UVC カメラのプレビューのコールバックを設定します.
+ *
+ * @param callback コールバック
+ */
+ public void setPreviewCallback(final PreviewCallback callback) {
+ mPreviewCallback = callback;
+ }
+
+ /**
+ * UVC カメラのイベントを通知するリスナーを設定します.
+ *
+ * @param listener リスナー
+ */
+ public void setOnEventListener(final OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ /**
+ * UVC カメラの初期化処理を行います.
+ *
+ * @throws IOException 初期化に失敗した場合に発生
+ */
+ void init() throws IOException {
+ checkClosed();
+ checkAndOpenNativePtr();
+
+ mParameters.clear();
+ UVCCameraNative.getParameter(mNativePtr, mParameters);
+
+ mOption = new Option(this);
+ UVCCameraNative.getOption(mNativePtr, mOption);
+
+ mDeviceName = mUsbSerialPort.getDeviceName();
+ if (mDeviceName == null) {
+ // デバイス名が取得できない場合には、他のパラメータを使用してデバイス名を作成
+ if (mUsbSerialPort.getManufacturerName() != null) {
+ mDeviceName = "UVC " + mUsbSerialPort.getManufacturerName();
+ } else {
+ mDeviceName = "UVC " + mUsbSerialPort.getVendorId() + "-" + mUsbSerialPort.getVendorId();
+ }
+ }
+
+ mDeviceId = mUsbSerialPort.getVendorId() + "-" + mUsbSerialPort.getProductId();
+ }
+
+ /**
+ * UVCカメラのユニークなIDを取得します.
+ *
+ * @return デバイスID
+ */
+ public String getDeviceId() {
+ return mDeviceId;
+ }
+
+ /**
+ * UVCカメラのデバイス名を取得します.
+ *
+ * @return デバイス名
+ */
+ public String getDeviceName() {
+ return mDeviceName;
+ }
+ /**
+ * UVC カメラが設定できるパラメータ一覧を取得します.
+ *
+ * @return WebCamパラメータの一覧
+ * @throws IOException 既に close されていた場合に発生
+ */
+ public List getParameter() throws IOException {
+ checkClosed();
+ checkAndOpenNativePtr();
+ return mParameters;
+ }
+
+ /**
+ * UVC カメラが設定できるオプションを取得します.
+ *
+ * @return オプション
+ */
+ public Option getOptions() throws IOException {
+ checkClosed();
+ checkAndOpenNativePtr();
+ return mOption;
+ }
+
+ /**
+ * プレビューで設定されたパラメータを取得します.
+ *
+ * プレビューが行われていない場合は null を返却します。
+ *
+ * @return プレビューで設定されたパラメータ
+ */
+ public Parameter getCurrentParameter() {
+ return mCurrentParameter;
+ }
+
+ /**
+ * UVC カメラのプレビューを開始します.
+ *
+ * @param parameter パラメータ
+ * @throws IllegalArgumentException パラメータが設定されていない場合に発生
+ * @throws IOException 既に close されていた場合に発生
+ */
+ public synchronized void startVideo(final Parameter parameter) throws IOException {
+ if (parameter == null) {
+ throw new IllegalArgumentException("parameter is null.");
+ }
+
+ checkClosed();
+ checkAndOpenNativePtr();
+
+ if (isRunning()) {
+ return;
+ }
+
+ // フレームバッファの初期化
+ mFrameProvider.init(parameter, 30);
+
+ mCurrentParameter = parameter;
+
+ int result = UVCCameraNative.startVideo(mNativePtr, parameter);
+ if (result == UVC_SUCCESS) {
+ startPreviewThread();
+ } else {
+ stopVideo();
+ throw new IOException("Failed to start a preview. result=" + result);
+ }
+ }
+
+ /**
+ * UVC カメラのプレビューを停止します.
+ *
+ * @throws IOException 既に close されていた場合に発生
+ */
+ public synchronized void stopVideo() throws IOException {
+ checkClosed();
+
+ if (mNativePtr != 0) {
+ UVCCameraNative.stopVideo(mNativePtr);
+ }
+
+ stopPreviewThread();
+
+ closeNativePrt();
+
+ mFrameProvider.clear();
+ }
+
+ /**
+ * 静止画撮影を行います.
+ *
+ * @param parameter パラメータ
+ * @param callback 撮影結果を通知するコールバック
+ * @throws IOException 静止画撮影に失敗した場合に発生
+ * @throws IllegalArgumentException 引数にnullが指定された場合に発生
+ */
+ public synchronized void captureStillImage(final Parameter parameter, final PictureCallback callback) throws IOException {
+ if (parameter == null) {
+ throw new IllegalArgumentException("parameter is null.");
+ }
+
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null.");
+ }
+
+ final boolean isPreview = isRunning();
+
+ checkClosed();
+ checkAndOpenNativePtr();
+
+ Thread thread = new Thread(() -> {
+ // UVC の仕様で、bStillCaptureMethod が 2 の場合には、プレビューと
+ // 排他的に静止画を使用する必要があります。
+ // bStillCaptureMethod が 3 の場合には、プレビューを止めなくても
+ // 撮影できますが、処理をまとめるために止めるようにしています。
+ if (isPreview) {
+ try {
+ stopVideo();
+ } catch (IOException e) {
+ // ignore.
+ }
+ }
+
+ Frame frame = null;
+ try {
+ checkAndOpenNativePtr();
+
+ frame = new Frame(parameter, 1);
+ int result = UVCCameraNative.captureStillImage(mNativePtr, frame);
+ if (result == UVC_SUCCESS) {
+ callback.onPicture(frame);
+ } else {
+ callback.onFailed(new UVCCameraException("Failed to take a photo. result=" + result));
+ }
+ } catch (Throwable t) {
+ callback.onFailed(new UVCCameraException(t));
+ } finally {
+ closeNativePrt();
+
+ if (frame != null) {
+ frame.release();
+ }
+ }
+
+ if (isPreview) {
+ try {
+ startVideo(mCurrentParameter);
+ } catch (IOException e) {
+ // プレビュー再開失敗
+ postOnError(new UVCCameraException(e));
+ }
+ }
+ });
+ thread.setName("UVC-Still-Thread");
+ thread.setPriority(Thread.MAX_PRIORITY);
+ thread.start();
+ }
+
+ /**
+ * UVC カメラのプレビューが動作中か取得します.
+ *
+ * @return プレビュー表示中はtrue、それ以外はfalse
+ * @throws IOException 既に close されていた場合に発生
+ */
+ public boolean isRunning() throws IOException {
+ checkClosed();
+ return mNativePtr != 0 && mPreviewThread != null && mDispatchThread != null;
+ }
+
+ /**
+ * UVC カメラを閉じます.
+ *
+ * @throws IOException 既に close されていた場合に発生
+ */
+ public void close() throws IOException {
+ checkClosed();
+
+ if (isRunning()) {
+ stopVideo();
+ }
+
+ mUsbSerialPort.close();
+ mUsbSerialPort = null;
+ }
+
+ /**
+ * UVC カメラが既に閉じられているか確認します.
+ *
+ * @return 既に閉じられている場合はtrue、それ以外はfalse
+ */
+ public boolean isClosed() {
+ return mUsbSerialPort == null;
+ }
+
+ /**
+ * JNI 側の初期化を確認して、初期化されていない場合には初期化します.
+ *
+ * @throws IOException 初期化に失敗した場合に発生
+ */
+ private void checkAndOpenNativePtr() throws IOException {
+ if (mNativePtr == 0) {
+ if (!mUsbSerialPort.isConnected()) {
+ mUsbSerialPort.open();
+ }
+
+ mNativePtr = UVCCameraNative.open(mUsbSerialPort.getFileDescriptor());
+ if (mNativePtr == 0) {
+ throw new IOException("Failed to open uvc device.");
+ }
+
+ // Configuration が複数あった場合は、どちらの Configuration を使用するのかを検討する必要があります。
+ // ここでは、UVC バージョンが大きい方を使用するように設定します。
+ if (mUsbSerialPort.getConfigurationCount() > 1) {
+ int configId = UVCParseDescriptor.getConfigId(mUsbSerialPort.getRawDescriptors());
+ if (configId != 0) {
+ // interface が claim されていると Configuration の設定に失敗するので、
+ // ここでは、一度、全ての interface を release します。
+ UsbDevice device = mUsbSerialPort.getUsbDevice();
+ for (int i = 0; i < device.getInterfaceCount(); i++) {
+ // interface を releaseしてみて、失敗した場合には、OS 側で claim している可能性があります。
+ // そのために、NDK 側で OS で claim している interface を release を行います。
+ if (!mUsbSerialPort.releaseInterface(i)) {
+ UVCCameraNative.detachInterface(mNativePtr, device.getInterface(i).getId());
+ }
+ }
+
+ UVCCameraNative.setConfig(mNativePtr, configId);
+ }
+ }
+ }
+ }
+
+ /**
+ * JNI 側の後始末処理を行います.
+ *
+ */
+ private void closeNativePrt() {
+ if (mNativePtr != 0) {
+ UVCCameraNative.close(mNativePtr);
+ }
+ mNativePtr = 0;
+
+ mUsbSerialPort.close();
+ }
+
+ /**
+ * 既に close されていないか確認します.
+ *
+ * @throws IOException 既に close されていた場合に発生
+ */
+ private void checkClosed() throws IOException {
+ if (mUsbSerialPort == null) {
+ throw new IOException("This device is already closed.");
+ }
+ }
+
+ /**
+ * プレビュー用のスレッドを開始します.
+ */
+ private void startPreviewThread() {
+ if (mPreviewThread != null || mDispatchThread != null) {
+ return;
+ }
+
+ mDispatchThread = new DispatchThread();
+ mDispatchThread.setName("UVC-Dispatch-Thread");
+ mDispatchThread.start();
+
+ mPreviewThread = new Thread(() -> {
+ if (mNativePtr != 0) {
+ postOnStart();
+
+ if (UVCCameraNative.handleEvent(mNativePtr, UVCCamera.this) != UVC_SUCCESS) {
+ // NDK側で UVC のポーリングに失敗したので、スレッドを停止して後始末処理を行う
+ stopPreviewThread();
+ postOnError(new UVCCameraException());
+ }
+
+ postOnStop();
+ }
+ });
+ mPreviewThread.setName("UVC-Preview-Thread");
+ mPreviewThread.start();
+ }
+
+ /**
+ * プレビュー用のスレッドを停止します.
+ */
+ private void stopPreviewThread() {
+ if (mDispatchThread != null) {
+ mDispatchThread.close();
+ mDispatchThread = null;
+ }
+
+ if (mPreviewThread != null) {
+ try {
+ mPreviewThread.interrupt();
+ mPreviewThread.join(1000);
+ } catch (InterruptedException e) {
+ // ignore.
+ }
+ mPreviewThread = null;
+ }
+ }
+
+ /**
+ * コントロールの値を取得します.
+ *
+ * 引数の value には、UVCから取得した値を格納します。
+ * 仕様にあるサイズのbyte配列を送ること。
+ * サイズが異なる場合には、エラーが発生します。
+ *
+ * @param control コントロール
+ * @param request リクエスト
+ * @param value 取得した値を格納する配列
+ * @return 0の場合には取得成功、それ以外は取得失敗
+ */
+ int getControl(final UVCCameraNative.Control control, final UVCCameraNative.RequestType request, final byte[] value) {
+ return UVCCameraNative.getControl(mNativePtr, control, request, value);
+ }
+
+ /**
+ * コントロールの値を取得します.
+ *
+ * @param control コントロール
+ * @param value 設定する値を格納する配列
+ * @return 0の場合には取得成功、それ以外は取得失敗
+ */
+ int setControl(final UVCCameraNative.Control control, final byte[] value) {
+ return UVCCameraNative.setControl(mNativePtr, control, value);
+ }
+
+ /**
+ * 使用していないフレームを取得します.
+ *
+ * MEMO: NDK側から呼び出されるので、変更する場合には十分に注意すること。
+ *
+ *
+ * 指定されたフレームバッファのサイズが Frame のサイズよりも大きい場合には
+ * Frame のバッファをリサイズして大きくします。
+ *
+ *
+ * 使用できるフレームバッファがない場合には null を返却します。
+ *
+ * @param length 使用するフレームバッファのサイズ
+ * @return 使用していないフレームバッファ
+ */
+ Frame getFrame(final int length) {
+ try {
+ return mFrameProvider.getFreeFrame(length);
+ } catch (OutOfMemoryError e) {
+ // NDKから要求されたバッファサイズでメモリ不足になった
+ // 場合は、エラーを通知して、フレームは null を返す。
+ postOnError(new UVCCameraException());
+ }
+ return null;
+ }
+
+ /**
+ * UVC デバイスからフレームバッファの通知を受け取ります.
+ *
+ * MEMO: NDK側から呼び出されるので、変更する場合には十分に注意すること。
+ *
+ *
+ * 指定された ID のフレームバッファにデータが書き込まれています。
+ *
+ * @param id フレームバッファのID
+ * @param length 書き込まれたデータサイズ
+ * @param pts presentation timestamp
+ */
+ void onFrame(final int id, final int length, final long pts) {
+ if (mFrameProvider.isEmpty()) {
+ return;
+ }
+
+ Frame frame = mFrameProvider.getFrameById(id);
+ frame.setLength(length);
+ frame.setPTS(pts);
+
+ if (mDispatchThread != null && !mDispatchThread.isInterrupted()) {
+ mDispatchThread.add(frame);
+ }
+ }
+
+ /**
+ * フレームバッファを配送するスレッド.
+ */
+ private class DispatchThread extends QueueThread {
+ private void exec() throws InterruptedException {
+ Frame frame = get();
+ try {
+ if (mPreviewCallback != null) {
+ mPreviewCallback.onFrame(frame);
+ }
+ } catch (Throwable t) {
+ // ignore.
+ } finally {
+ frame.release();
+ }
+ }
+
+ @Override
+ public void run() {
+ while (!isInterrupted()) {
+ try {
+ exec();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * プレビュー開始イベントを通知します.
+ */
+ private void postOnStart() {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStart();
+ }
+ }
+
+ /**
+ * プレビュー停止イベントを通知します.
+ */
+ private void postOnStop() {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStop();
+ }
+ }
+
+ /**
+ * プレビューでエラーが発生したことを通知します.
+ *
+ * @param e エラー原因の例外
+ */
+ private void postOnError(UVCCameraException e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(e);
+ }
+ }
+
+ /**
+ * UVCカメラのイベントを通知するリスナー.
+ */
+ public interface OnEventListener {
+ /**
+ * UVCカメラのプレビューが開始されたことを通知します.
+ */
+ void onStart();
+
+ /**
+ * UVCカメラのプレビューが停止されたことを通知します.
+ */
+ void onStop();
+
+ /**
+ * UVCカメラのプレビューがエラーが発生したことを通知します.
+ *
+ * @param e エラーの情報を格納した例外
+ */
+ void onError(UVCCameraException e);
+ }
+
+ /**
+ * 写真撮影の結果を通知するコールバック.
+ */
+ public interface PictureCallback {
+ /**
+ * 撮影された写真データの通知を行います。
+ *
+ * @param frame 写真データ
+ */
+ void onPicture(Frame frame);
+
+ /**
+ * 撮影失敗の通知を行います.
+ *
+ * @param e エラーの原因
+ */
+ void onFailed(Exception e);
+ }
+
+ /**
+ * プレビューフレームバッファ通知を行うコールバック.
+ */
+ public interface PreviewCallback {
+ /**
+ * フレームバッファが送られてきたときの通知を行う.
+ *
+ * 使用済みになった Frame は、{@link Frame#release()} を呼び出すこと。
+ * 使用済みになった Frame は、UVCCamera で使い回されます。
+ * 使用済みにならない場合には UVCCamera で使用できる Frame が枯渇して動作しなくなります。
+ *
+ * @param frame フレームバッファ
+ */
+ void onFrame(Frame frame);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraException.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraException.java
new file mode 100644
index 0000000000..8f4bfb53bd
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraException.java
@@ -0,0 +1,38 @@
+/*
+ UVCCameraException.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+/**
+ * UVCCameraで発生するエラーの例外クラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class UVCCameraException extends RuntimeException {
+
+ /**
+ * コンストラクタ.
+ */
+ UVCCameraException() {}
+
+ /**
+ * コンストラクタ.
+ *
+ * @param e 例外
+ */
+ UVCCameraException(final Throwable e) {
+ super(e);
+ }
+
+ /**
+ * コンストラクタ.
+ *
+ * @param message エラーメッセージ
+ */
+ UVCCameraException(final String message) {
+ super(message);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraManager.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraManager.java
new file mode 100644
index 0000000000..5db45a4d58
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraManager.java
@@ -0,0 +1,290 @@
+/*
+ UVCCameraManager.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.usb.UsbConstants;
+import android.os.Build;
+
+import org.deviceconnect.android.libusb.UsbSerialPort;
+import org.deviceconnect.android.libusb.UsbSerialPortManager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.deviceconnect.android.libusb.UsbSerialPortManager.OnUsbEventListener;
+
+/**
+ * UVCカメラを管理するクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class UVCCameraManager {
+ /**
+ * 接続している UVC カメラを Map で保持します.
+ */
+ private final Map mUVCCameras = new HashMap<>();
+
+ /**
+ * USB の管理を行うクラス.
+ */
+ private UsbSerialPortManager mUsbSerialPortManager;
+
+ /**
+ * UVC カメラの接続・切断などのイベントを通知するリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ /**
+ * UsbSerialPortManagerのイベントを受け取るリスナー.
+ */
+ private final OnUsbEventListener mUsbEventListener = new OnUsbEventListener() {
+ @Override
+ public void onAttached(final UsbSerialPort serialPort) {
+ try {
+ if (checkCameraPermission()) {
+ checkOpen(serialPort);
+ } else {
+ notifyError(new RuntimeException("Camera permission deny."));
+ }
+ } catch (Exception e) {
+ notifyError(e);
+ }
+ }
+
+ @Override
+ public void onDetached(final UsbSerialPort serialPort) {
+ try {
+ checkClose(serialPort);
+ } catch (Exception e) {
+ notifyError(e);
+ }
+ }
+
+ @Override
+ public void onError(final Exception e) {
+ notifyError(e);
+ }
+
+ @Override
+ public void onRequestPermission(UsbSerialPortManager.PermissionCallback callback) {
+ if (checkCameraPermission()) {
+ callback.allow();
+ } else {
+ notifyOnRequestPermission(callback);
+ }
+ }
+ };
+
+ /**
+ * コンストラクタ.
+ *
+ * UsbSerialPortManager を内部で作成して使用します。
+ *
+ * @param context コンテキスト
+ * @throws UnsupportedOperationException USBがサポートされていない場合に発生
+ */
+ public UVCCameraManager(final Context context) {
+ this(new UsbSerialPortManager(context));
+ }
+
+ /**
+ * コンストラクタ.
+ *
+ * @param manager USB管理クラス
+ * @throws UnsupportedOperationException USBがサポートされていない場合に発生
+ */
+ public UVCCameraManager(final UsbSerialPortManager manager) {
+ if (manager == null) {
+ throw new IllegalArgumentException("manager is null.");
+ }
+
+ mUsbSerialPortManager = manager;
+ mUsbSerialPortManager.addOnUsbEventListener(UsbConstants.USB_CLASS_MISC, mUsbEventListener);
+ }
+
+ /**
+ * 後始末を行います.
+ */
+ public void dispose() {
+ if (mUsbSerialPortManager != null) {
+ mUsbSerialPortManager.removeOnUsbEventListener(UsbConstants.USB_CLASS_MISC, mUsbEventListener);
+ mUsbSerialPortManager = null;
+ }
+ }
+
+ /**
+ * UVC カメラの接続・切断などのイベントを通知するリスナーを設定します.
+ *
+ * @param listener イベントを通知するリスナー
+ */
+ public void setOnEventListener(final OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ /**
+ * UVC カメラの接続・切断の監視を開始します.
+ */
+ public void startMonitoring() {
+ mUsbSerialPortManager.startUsbMonitoring();
+ }
+
+ /**
+ * UVC カメラの接続・切断の監視を停止します.
+ */
+ public void stopMonitoring() {
+ mUsbSerialPortManager.stopUsbMonitoring();
+ }
+
+ /**
+ * 接続されている UVC カメラのリストを取得します.
+ *
+ * @return UVC カメラのリスト
+ */
+ public synchronized List getUVCCameras() {
+ return new ArrayList<>(mUVCCameras.values());
+ }
+
+ /**
+ * カメラパーミッションの有無を確認します.
+ *
+ * @return カメラパーミッションが許可されている場合はtrue、それ以外はfalse
+ */
+ private boolean checkCameraPermission() {
+ if (mUsbSerialPortManager != null) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return true;
+ }
+ Context context = mUsbSerialPortManager.getContext();
+ return context.checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
+ }
+ return false;
+ }
+
+ /**
+ * UVC カメラと接続します.
+ *
+ * @param serialPort USBデバイス
+ * @return UVCカメラのインスタンス
+ * @throws IOException UVCカメラの接続に失敗した場合に発生
+ */
+ private UVCCamera openUVCCamera(final UsbSerialPort serialPort) throws IOException {
+ UVCCamera uvcCamera = new UVCCamera(serialPort);
+ try {
+ uvcCamera.init();
+ } catch (Exception e) {
+ uvcCamera.close();
+ throw new IOException(e);
+ }
+ return uvcCamera;
+ }
+
+ /**
+ * UVC カメラの接続を確認して、接続に成功した場合にはリスナーに通知を行います.
+ *
+ * @param serialPort USBデバイス
+ * @throws IOException UVCカメラの接続に失敗した場合に発生
+ */
+ private synchronized void checkOpen(final UsbSerialPort serialPort) throws IOException {
+ UVCCamera uvcCamera = openUVCCamera(serialPort);
+ mUVCCameras.put(serialPort, uvcCamera);
+ notifyConnected(uvcCamera);
+ }
+
+ /**
+ * UVC カメラの切断を行い、リスナーに通知を行います.
+ *
+ * @param serialPort USBデバイス
+ * @throws IOException UVCカメラの切断に失敗した場合に発生
+ */
+ private synchronized void checkClose(final UsbSerialPort serialPort) throws IOException {
+ UVCCamera uvcCamera = mUVCCameras.remove(serialPort);
+ if (uvcCamera != null) {
+ uvcCamera.close();
+ notifyDisconnected(uvcCamera);
+ }
+ }
+
+ /**
+ * 接続した UVC カメラを通知します.
+ *
+ * @param uvcCamera UVC カメラ
+ */
+ private void notifyConnected(final UVCCamera uvcCamera) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onConnected(uvcCamera);
+ }
+ }
+
+ /**
+ * 切断した UVC カメラを通知します.
+ *
+ * @param uvcCamera UVC カメラ
+ */
+ private void notifyDisconnected(final UVCCamera uvcCamera) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onDisconnected(uvcCamera);
+ }
+ }
+
+ /**
+ * エラーを通知します.
+ *
+ * @param e 例外
+ */
+ private void notifyError(final Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(e);
+ }
+ }
+
+ /**
+ * パーミッション要求を通知します.
+ *
+ * @param callback パーミッション要求の結果を受け取るコールバック
+ */
+ private void notifyOnRequestPermission(UsbSerialPortManager.PermissionCallback callback) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onRequestPermission(callback);
+ }
+ }
+
+ /**
+ * UVC カメラの接続・切断イベントを通知するリスナー.
+ */
+ public interface OnEventListener {
+ /**
+ * UVC カメラが接続されたことを通知します.
+ * @param uvcCamera UVC カメラ
+ */
+ void onConnected(UVCCamera uvcCamera);
+
+ /**
+ * UVC カメラが切断されたことを通知します.
+ * @param uvcCamera UVC カメラ
+ */
+ void onDisconnected(UVCCamera uvcCamera);
+
+ /**
+ * 監視中に発生したエラーを通知します.
+ * @param e 例外
+ */
+ void onError(Exception e);
+
+ /**
+ * パーミッション要求を通知します.
+ *
+ * @param callback パーミッション要求の結果を通知するコールバック
+ */
+ void onRequestPermission(UsbSerialPortManager.PermissionCallback callback);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraNative.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraNative.java
new file mode 100644
index 0000000000..95b797fd8a
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCCameraNative.java
@@ -0,0 +1,224 @@
+/*
+ UVCCameraNative.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+import java.util.ArrayList;
+
+import static org.deviceconnect.android.libuvc.UVCCameraNative.RequestType.SET_CUR;
+
+/**
+ * NDKとのインターフェース.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+class UVCCameraNative {
+
+ static {
+ System.loadLibrary("native-lib");
+ }
+
+ /**
+ * コントロールを定義します.
+ *
+ * MEMO uvc.h で定義されている値と一致する必要あります。
+ *
+ */
+ enum ControlType {
+ /**
+ * カメラターミナルコントロールを定義します.
+ */
+ TYPE_CT(0),
+
+ /**
+ * プロセシングユニットコントロールを定義します.
+ */
+ TYPE_PU(1),
+
+ /**
+ * エンコードユニットコントロールを定義します.
+ */
+ TYPE_EU(2);
+
+ private int mValue;
+
+ ControlType(final int value) {
+ mValue = value;
+ }
+ }
+
+ /**
+ * Video Class-Specific Request Codes.
+ *
+ * MEMO uvc.h で定義されている値と一致する必要あります。
+ *
+ */
+ enum RequestType {
+ SET_CUR(0x01),
+ SET_CUR_ALL(0x11),
+ GET_CUR(0x81),
+ GET_MIN(0x82),
+ GET_MAX(0x83),
+ GET_RES(0x84),
+ GET_LEN(0x85),
+ GET_INFO(0x86),
+ GET_DEF(0x87);
+
+ private int mValue;
+
+ RequestType(final int value) {
+ mValue = value;
+ }
+ }
+
+ /**
+ * コントロールのインターフェース.
+ */
+ interface Control {
+ /**
+ * コントロールのタイプを取得します.
+ *
+ * @return コントロールのタイプ
+ */
+ ControlType getType();
+
+ /**
+ * コントロールの機能を取得します.
+ *
+ * @return コントロールの機能
+ */
+ int getValue();
+ }
+
+ /**
+ * カメラターミナルコントロール.
+ */
+ enum CameraTerminalControl implements Control {
+ CT_CONTROL_UNDEFINED(0x00),
+ CT_SCANNING_MODE_CONTROL(0x01),
+ CT_AE_MODE_CONTROL(0x02),
+ CT_AE_PRIORITY_CONTROL(0x03),
+ CT_EXPOSURE_TIME_ABSOLUTE_CONTROL(0x04),
+ CT_EXPOSURE_TIME_RELATIVE_CONTROL(0x05),
+ CT_FOCUS_ABSOLUTE_CONTROL(0x06),
+ CT_FOCUS_RELATIVE_CONTROL(0x07),
+ CT_FOCUS_AUTO_CONTROL(0x08),
+ CT_IRIS_ABSOLUTE_CONTROL(0x09),
+ CT_IRIS_RELATIVE_CONTROL(0x0A),
+ CT_ZOOM_ABSOLUTE_CONTROL(0x0B),
+ CT_ZOOM_RELATIVE_CONTROL(0x0C),
+ CT_PANTILT_ABSOLUTE_CONTROL(0x0D),
+ CT_PANTILT_RELATIVE_CONTROL(0x0E),
+ CT_ROLL_ABSOLUTE_CONTROL(0x0F),
+ CT_ROLL_RELATIVE_CONTROL(0x10),
+ CT_PRIVACY_CONTROL(0x11),
+ CT_FOCUS_SIMPLE_CONTROL(0x12),
+ CT_WINDOW_CONTROL(0x13),
+ CT_REGION_OF_INTEREST_CONTROL(0x14);
+
+ private int mValue;
+
+ CameraTerminalControl(final int value) {
+ mValue = value;
+ }
+
+ @Override
+ public ControlType getType() {
+ return ControlType.TYPE_CT;
+ }
+
+ @Override
+ public int getValue() {
+ return mValue;
+ }
+
+ static CameraTerminalControl valueOf(final int value) {
+ for (CameraTerminalControl ct : values()) {
+ if (ct.mValue == value) {
+ return ct;
+ }
+ }
+ return CT_CONTROL_UNDEFINED;
+ }
+ }
+
+ /**
+ * プロセシングユニットコントロール.
+ */
+ enum ProcessingUnitControl implements Control {
+ PU_CONTROL_UNDEFINED(0x00),
+ PU_BACKLIGHT_COMPENSATION_CONTROL(0x01),
+ PU_BRIGHTNESS_CONTROL(0x02),
+ PU_CONTRAST_CONTROL(0x03),
+ PU_GAIN_CONTROL(0x04),
+ PU_POWER_LINE_FREQUENCY_CONTROL(0x05),
+ PU_HUE_CONTROL(0x06),
+ PU_SATURATION_CONTROL(0x07),
+ PU_SHARPNESS_CONTROL(0x08),
+ PU_GAMMA_CONTROL(0x09),
+ PU_WHITE_BALANCE_TEMPERATURE_CONTROL(0x0A),
+ PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL(0x0B),
+ PU_WHITE_BALANCE_COMPONENT_CONTROL(0x0C),
+ PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL(0x0D),
+ PU_DIGITAL_MULTIPLIER_CONTROL(0x0E),
+ PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL(0x0F),
+ PU_HUE_AUTO_CONTROL(0x10),
+ PU_ANALOG_VIDEO_STANDARD_CONTROL(0x11),
+ PU_ANALOG_LOCK_STATUS_CONTROL(0x12),
+ PU_CONTRAST_AUTO_CONTROL(0x13);
+
+ private int mValue;
+
+ ProcessingUnitControl(final int value) {
+ mValue = value;
+ }
+
+ @Override
+ public ControlType getType() {
+ return ControlType.TYPE_PU;
+ }
+
+ @Override
+ public int getValue() {
+ return mValue;
+ }
+
+ static ProcessingUnitControl valueOf(final int value) {
+ for (ProcessingUnitControl pu : values()) {
+ if (pu.mValue == value) {
+ return pu;
+ }
+ }
+ return PU_CONTROL_UNDEFINED;
+ }
+ }
+
+ static int startVideo(long nativePtr, Parameter p) {
+ return startVideo(nativePtr, p.getFormatIndex(), p.getFrameIndex(), p.getFps(), p.isUseH264());
+ }
+
+ static int setControl(long nativePtr, Control control, byte[] value) {
+ return applyControl(nativePtr, control.getType().mValue, control.getValue(), SET_CUR.mValue, value);
+ }
+
+ static int getControl(long nativePtr, Control control, RequestType request, byte[] value) {
+ return applyControl(nativePtr, control.getType().mValue, control.getValue(), request.mValue, value);
+ }
+
+ static native long open(int fd);
+ static native int getParameter(long nativePtr, ArrayList parameters);
+ static native int getOption(long nativePtr, Option option);
+ static native int startVideo(long nativePtr, int formatIndex, int frameIndex, int fps, boolean useH264);
+ static native int stopVideo(long nativePtr);
+ static native int captureStillImage(long nativePtr, Frame frame);
+ static native int close(long nativePtr);
+ static native int handleEvent(long nativePtr, UVCCamera uvcCamera);
+ static native int isRunning(long nativePtr);
+ static native int detachInterface(long nativePtr, int interfaceNum);
+ static native int setConfig(long nativePtr, int configId);
+ static native int applyControl(long nativePtr, int type, int control, int request, byte[] value);
+ static native int getStillCaptureMethod(long nativePtr);
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCParseDescriptor.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCParseDescriptor.java
new file mode 100644
index 0000000000..cfadc88d0c
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/UVCParseDescriptor.java
@@ -0,0 +1,158 @@
+/*
+ UVCParseDescriptor.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc;
+
+import org.deviceconnect.android.libusb.descriptor.Descriptor;
+import org.deviceconnect.android.libusb.descriptor.Scanner;
+
+import java.io.IOException;
+
+/**
+ * UVC のディスクリプタを解析します.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+class UVCParseDescriptor implements Descriptor {
+
+ private static final int CC_VIDEO = 0x0E;
+
+ private static final int SC_VIDEOCONTROL = 0x01;
+ private static final int SC_VIDEOSTREAMING = 0x02;
+
+ private static final int VC_HEADER = 0x01;
+ private static final int VC_INPUT_TERMINAL = 0x02;
+ private static final int VC_OUTPUT_TERMINAL = 0x03;
+ private static final int VC_SELECTOR_UNIT = 0x04;
+ private static final int VC_PROCESSING_UNIT = 0x05;
+ private static final int VC_EXTENSION_UNIT = 0x06;
+ private static final int VC_ENCODING_UNIT = 0x07;
+
+ private UVCParseDescriptor() {}
+
+ /**
+ * アクティブにする Configuration の ID を取得します.
+ *
+ * bcdUVC (UVCバージョン)が一番大きい値を格納しているConfigurationのIDを取得します。
+ *
+ *
+ * THETA S では、bcdUVC が 0x0110 と 0x0150 の2つを持っているので、
+ * 0x0150の Configuration を取得するようにしています。
+ *
+ * @param data ディスクリプタ
+ * @return ConfigurationのID
+ * @throws IOException 解析に失敗した場合に発生
+ */
+ static int getConfigId(final byte[] data) throws IOException {
+ if (data == null) {
+ throw new IOException("data is null.");
+ }
+
+ int activeConfigId = 0;
+
+ int _bConfigurationValue = 0;
+ int _bcdUVC = 0;
+
+ Scanner scanner = new Scanner(data);
+ while (!scanner.isFinish()) {
+ int bLength = scanner.readByte();
+ byte bDescriptorType = scanner.readByte();
+ switch (bDescriptorType) {
+ case CONFIGURATION:
+ int wTotalLength = scanner.readShort();
+ int bNumInterfaces = scanner.readByte();
+ int bConfigurationValue = scanner.readByte();
+ int iConfiguration = scanner.readByte();
+ int bmAttributes = scanner.readByte();
+ int bMaxPower = scanner.readByte();
+ _bConfigurationValue = bConfigurationValue;
+ break;
+
+ case INTERFACE:
+ int bInterfaceNumber = scanner.readByte();
+ int bAlternateSetting = scanner.readByte();
+ int bNumEndpoints = scanner.readByte();
+ int bInterfaceClass = scanner.readByte();
+ int bInterfaceSubClass = scanner.readByte();
+ int bInterfaceProtocol = scanner.readByte();
+ int iInterface = scanner.readByte();
+
+ if (bInterfaceClass == CC_VIDEO) {
+ switch (bInterfaceSubClass) {
+ case SC_VIDEOCONTROL:
+ int bcdUVC = parseVC(scanner);
+ if (bcdUVC > _bcdUVC) {
+ _bcdUVC = bcdUVC;
+ activeConfigId = _bConfigurationValue;
+ }
+ break;
+
+ case SC_VIDEOSTREAMING:
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ scanner.skip(bLength - 2);
+ break;
+ }
+ }
+ return activeConfigId;
+ }
+
+ /**
+ * Video Control から bcdUVC の値を取得します.
+ *
+ * @param scanner ディスクリプタのスキャナ
+ * @return bcdUVCの値
+ * @throws IOException スキャナの読み込みに失敗した場合に発生
+ */
+ private static int parseVC(final Scanner scanner) throws IOException {
+ int bcdUVC = 0;
+ while (!scanner.isFinish()) {
+ int bLength = scanner.readByte();
+ byte bDescriptorType = scanner.readByte();
+ switch (bDescriptorType) {
+ case INTERFACE:
+ scanner.skip(bLength - 2);
+ return bcdUVC;
+
+ case CS_INTERFACE: {
+ int bDescriptorSubtype = scanner.readByte();
+ switch (bDescriptorSubtype) {
+ case VC_HEADER:
+ bcdUVC = scanner.readShort();
+ int wTotalLength = scanner.readShort();
+ int dwClockFrequency = scanner.readInt();
+ int bInCollection = scanner.readByte();
+ for (int i = 0;i < bInCollection; i++) {
+ scanner.readByte();
+ }
+ break;
+ case VC_INPUT_TERMINAL:
+ case VC_OUTPUT_TERMINAL:
+ case VC_SELECTOR_UNIT:
+ case VC_PROCESSING_UNIT:
+ case VC_EXTENSION_UNIT:
+ case VC_ENCODING_UNIT:
+ default:
+ scanner.skip(bLength - 3);
+ break;
+ }
+ } break;
+
+ case ENDPOINT:
+ case CS_ENDPOINT:
+ default:
+ scanner.skip(bLength - 2);
+ break;
+ }
+ }
+ return bcdUVC;
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCDecoder.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCDecoder.java
new file mode 100644
index 0000000000..aa772ef818
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCDecoder.java
@@ -0,0 +1,64 @@
+/*
+ UVCDecoder.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc.decoder;
+
+import android.view.Surface;
+
+import org.deviceconnect.android.libuvc.Frame;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+/**
+ * UVCからのフレームデータをデコードするクラスのインターフェース.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public interface UVCDecoder {
+ /**
+ * デコード先のSurfaceを設定します.
+ * @param surface Surface
+ */
+ void setSurface(Surface surface);
+
+ /**
+ * デコーダで発生したイベントを通知するリスナーを設定します.
+ * @param listener リスナー
+ */
+ void setOnEventListener(OnEventListener listener);
+
+ /**
+ * デコーダを初期化します.
+ *
+ * @param uvcCamera UVCカメラ
+ * @param parameter UVCカメラに設定されているパラメータ
+ */
+ void onInit(UVCCamera uvcCamera, Parameter parameter);
+
+ /**
+ * UVCカメラから送られてくるフレームデータを受け取ります.
+ *
+ * @param frame フレームデータ
+ */
+ void onReceivedFrame(Frame frame);
+
+ /**
+ * デコーダの後始末を行います.
+ */
+ void onRelease();
+
+ /**
+ * デコーダのイベントを通知するリスナー.
+ */
+ interface OnEventListener {
+ /**
+ * デコーダでエラーが発生したことを通知します.
+ *
+ * @param e エラー原因
+ */
+ void onError(Exception e);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCDecoderFactory.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCDecoderFactory.java
new file mode 100644
index 0000000000..0a6f49814d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCDecoderFactory.java
@@ -0,0 +1,63 @@
+/*
+ UVCDecoderFactory.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc.decoder;
+
+import android.os.Build;
+
+import org.deviceconnect.android.libuvc.FrameType;
+import org.deviceconnect.android.libuvc.Parameter;
+
+import java.io.IOException;
+
+/**
+ * UVCのデコーダを作成するファクトリークラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public final class UVCDecoderFactory {
+ /**
+ * コンストラクタ.
+ *
+ * ファクトリークラスなので、インスタンスは作成させない。
+ */
+ private UVCDecoderFactory() {}
+
+ /**
+ * デコーダを作成します.
+ *
+ * @param param デコーダのタイプ
+ * @return UVCDecoderのインスタンス
+ * @throws IOException デコーダの作成に失敗した場合に発生
+ */
+ public static UVCDecoder create(final Parameter param) throws IOException {
+
+ // TODO サポートするフレームタイプを増やしたい場合にはここに追加すること。
+
+ switch (param.getFrameType()) {
+ case H264:
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return new UVCH264Decoder();
+ } else {
+ throw new IOException("Not support frame type. type=" + param.getFrameType());
+ }
+ case MJPEG:
+ if (param.isUseH264()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return new UVCH264Decoder();
+ } else {
+ throw new IOException("Not support frame type. type=" + param.getFrameType());
+ }
+ } else {
+ return new UVCMJPEGDecoder();
+ }
+ case UNCOMPRESSED:
+ return new UVCUncompressedDecoder();
+ default:
+ throw new IOException("Not support frame type. type=" + param.getFrameType());
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCH264Decoder.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCH264Decoder.java
new file mode 100644
index 0000000000..4999ef862d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCH264Decoder.java
@@ -0,0 +1,442 @@
+/*
+ UVCH264Decoder.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc.decoder;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+import android.view.Surface;
+
+import org.deviceconnect.android.libuvc.BuildConfig;
+import org.deviceconnect.android.libuvc.Frame;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+import org.deviceconnect.android.libuvc.utils.QueueThread;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * H264をデコードしてSurfaceに描画するクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+class UVCH264Decoder implements UVCDecoder {
+ private static final boolean DEBUG = BuildConfig.DEBUG;
+ private static final String TAG = "UVC";
+
+ /**
+ * H.264 のマイムタイプを定義.
+ */
+ private static final String MIME_TYPE_H264 = "video/avc";
+
+ /**
+ * UVCパラメータ.
+ */
+ private Parameter mParameter;
+
+ /**
+ * デコードを行うMediaCodec.
+ */
+ private MediaCodec mMediaCodec;
+
+ /**
+ * SPSの情報を格納するバッファ.
+ */
+ private ByteBuffer mCsd0;
+
+ /**
+ * PPSの情報を格納するバッファ.
+ */
+ private ByteBuffer mCsd1;
+
+ /**
+ * デコード処理を行うスレッド.
+ */
+ private WorkThread mWorkThread;
+
+ /**
+ * 描画先のSurface.
+ */
+ private Surface mSurface;
+
+ /**
+ * イベント通知用のリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ @Override
+ public void setSurface(final Surface surface) {
+ mSurface = surface;
+ }
+
+ @Override
+ public void setOnEventListener(final OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ @Override
+ public void onInit(final UVCCamera uvcCamera, final Parameter parameter) {
+ if (mWorkThread != null) {
+ mWorkThread.close();
+ mWorkThread = null;
+ }
+
+ mParameter = parameter;
+ mCsd0 = null;
+ mCsd1 = null;
+ }
+
+ @Override
+ public void onReceivedFrame(final Frame frame) {
+ if (mWorkThread != null) {
+ mWorkThread.add(frame);
+ } else if (searchSPSandPPS(frame)) {
+ try {
+ startMediaCodec();
+ } catch (Exception e) {
+ mCsd0 = null;
+ mCsd1 = null;
+ }
+ } else {
+ frame.release();
+ }
+ }
+
+ @Override
+ public void onRelease() {
+ if (mMediaCodec != null) {
+ mMediaCodec.stop();
+ }
+
+ if (mWorkThread != null) {
+ mWorkThread.close();
+ mWorkThread = null;
+ }
+ }
+
+ /**
+ * エラー通知を行う.
+ * @param e 例外
+ */
+ private void postError(final Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(e);
+ }
+ }
+
+ /**
+ * SPSとPPSのパケットを探して、csd-0とcsd-1のデータを作成します.
+ *
+ * @param frame フレームバッファ
+ * @return SPSとPPSが作成できた場合はtrue、それ以外はfalse
+ */
+ private boolean searchSPSandPPS(final Frame frame) {
+ byte[] d = frame.getBuffer();
+ int length = frame.getLength();
+
+ int startPos = 0;
+ int count = 0;
+ for (int i = 0; i < frame.getLength() - 4; i++) {
+ if (d[i] == 0x00 && d[i + 1] == 0x00 && d[i + 2] == 0x00 && d[i + 3] == 0x01) {
+ if (count > 0) {
+ int type = d[startPos + 4] & 0x1F;
+ switch (type) {
+ case 7: // SPS
+ createCSD0(d, startPos, i - startPos);
+ break;
+ case 8: // PPS
+ createCSD1(d, startPos, i - startPos);
+ break;
+ }
+ }
+ startPos = i;
+ count++;
+ }
+ }
+
+ if (length - startPos > 0) {
+ int type = d[startPos + 4] & 0x1F;
+ switch (type) {
+ case 7: // SPS
+ createCSD0(d, startPos, length - startPos);
+ break;
+ case 8: // PPS
+ createCSD1(d, startPos, length - startPos);
+ break;
+ default:
+ break;
+ }
+ }
+
+ frame.release();
+
+ return mCsd0 != null && mCsd1 != null;
+ }
+
+ /**
+ * csd-0 のデータを作成します.
+ *
+ * @param data データが格納されたフレームバッファ
+ * @param pos データ開始位置
+ * @param length データサイズ
+ */
+ private void createCSD0(final byte[] data, int pos, int length) {
+ if (DEBUG) {
+ StringBuilder t = new StringBuilder("csd-0: ");
+ for (int i = 0; i < length; i++) {
+ t.append(String.format("%02X", data[pos + i]));
+ }
+ Log.d(TAG, t.toString());
+ Log.d(TAG, "pos: " + pos + ", length: " + length);
+ }
+ mCsd0 = ByteBuffer.allocateDirect(length).order(ByteOrder.nativeOrder());
+ mCsd0.put(data, pos, length);
+ mCsd0.flip();
+ }
+
+ /**
+ * csd-1のデータを作成します.
+ *
+ * @param data データが格納されたフレームバッファ
+ * @param pos データ開始位置
+ * @param length データサイズ
+ */
+ private void createCSD1(final byte[] data, int pos, int length) {
+ if (DEBUG) {
+ StringBuilder t = new StringBuilder("csd-1: ");
+ for (int i = 0; i < length; i++) {
+ t.append(String.format("%02X", data[pos + i]));
+ }
+ Log.d(TAG, t.toString());
+ Log.d(TAG, "pos: " + pos + ", length: " + length);
+ }
+ mCsd1 = ByteBuffer.allocateDirect(length).order(ByteOrder.nativeOrder());
+ mCsd1.put(data, pos, length);
+ mCsd1.flip();
+ }
+
+ /**
+ * MediaCodec でエンコードを行うためのスレッドを開始します.
+ */
+ private void startMediaCodec() {
+ mWorkThread = new WorkThread();
+ mWorkThread.setName("UVC-H264-Decode-Thread");
+ mWorkThread.start();
+ }
+
+ /**
+ * 指定されたエンコードがハードウェアか確認します.
+ *
+ * @param info エンコーダ情報
+ * @return ハードウェアエンコーダの場合は true、それ以外の場合は false
+ */
+ private boolean isHardware(MediaCodecInfo info) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ return info.isHardwareAccelerated();
+ } else {
+ String name = info.getName();
+ return name.startsWith("OMX.qcom.") || name.startsWith("OMX.Exynos.");
+ }
+ }
+
+ /**
+ * エンコーダの名前を取得します.
+ *
+ * @return エンコーダ名
+ */
+ private String getMediaCodecName() {
+ String name = null;
+ int cnt = MediaCodecList.getCodecCount();
+ for (int i = 0; i < cnt; i++) {
+ MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+ if (info.isEncoder()) {
+ continue;
+ }
+
+ if (!isHardware(info)) {
+ for (String type : info.getSupportedTypes()) {
+ if (type.equals(MIME_TYPE_H264)) {
+ name = info.getName();
+ }
+ }
+ }
+ }
+ return name;
+ }
+
+ /**
+ * エンコーダの情報をログに出力します.
+ */
+ private void printMediaCodecInfo() {
+ int cnt = MediaCodecList.getCodecCount();
+ for (int i = 0; i < cnt; i++) {
+ MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+ if (info.isEncoder()) {
+ continue;
+ }
+ Log.i(TAG, "CODEC[" + i + "] " + info.getName());
+ for (String type : info.getSupportedTypes()) {
+ Log.i(TAG, " mimeType: " + type);
+ }
+ }
+ }
+
+ /**
+ * 映像のサイズ変更を通知します.
+ *
+ * @param width 横幅
+ * @param height 縦幅
+ */
+ void postSizeChanged(int width, int height) {
+ }
+
+ /**
+ * MediaCodecを作成します.
+ *
+ * @param width 横幅
+ * @param height 縦幅
+ * @param useSoftwareDecoder ソフトウェアデコーダ使用
+ * @throws IOException MediaCodecの作成に失敗した場合に発生
+ */
+ private void createMediaCodec(final int width, final int height, final boolean useSoftwareDecoder) throws IOException {
+ MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE_H264, width, height);
+ format.setByteBuffer("csd-0", mCsd0);
+ format.setByteBuffer("csd-1", mCsd1);
+
+ if (DEBUG) {
+ Log.i(TAG, "createMediaCodec(width: " + width + ", height: " + height + ")");
+ printMediaCodecInfo();
+ }
+
+ if (useSoftwareDecoder) {
+ String name = getMediaCodecName();
+ if (name == null) {
+ throw new IOException("Not support a software decoder.");
+ }
+ mMediaCodec = MediaCodec.createByCodecName(name);
+ } else {
+ mMediaCodec = MediaCodec.createDecoderByType("video/avc");
+ }
+ mMediaCodec.configure(format, mSurface, null, 0);
+ mMediaCodec.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
+ mMediaCodec.start();
+ }
+
+ /**
+ * UVCから送られてきたデータをMediaCodecに渡してデコードを行うスレッド.
+ */
+ private class WorkThread extends QueueThread {
+ /**
+ * ソフトウェア使用フラグ.
+ */
+ private boolean mUseSoftwareDecoder = false;
+
+ @Override
+ public void close() {
+ stopMediaCodec();
+ super.close();
+ }
+
+ @Override
+ public void run() {
+ while (!isInterrupted()) {
+ try {
+ createMediaCodec(mParameter.getWidth(), mParameter.getHeight(), mUseSoftwareDecoder);
+ runMediaCodec();
+ } catch (InterruptedException e) {
+ if (DEBUG) {
+ Log.w(TAG, "InterruptedException.", e);
+ }
+ return;
+ } catch (Exception e) {
+ if (!mUseSoftwareDecoder) {
+ stopMediaCodec();
+
+ // ハードウェアデコーダで失敗した場合は、
+ // ソフトウェアデコーダを使用して もう一度エンコードを行う.
+ mUseSoftwareDecoder = true;
+ } else {
+ if (DEBUG) {
+ Log.e(TAG, "H264 encode occurred an exception.", e);
+ }
+ postError(e);
+ return;
+ }
+ }
+ }
+ }
+
+ private void stopMediaCodec() {
+ if (mMediaCodec != null) {
+ try {
+ mMediaCodec.stop();
+ } catch (Exception e) {
+ // ignore.
+ }
+
+ try {
+ mMediaCodec.release();
+ } catch (Exception e) {
+ // ignore.
+ }
+
+ mMediaCodec = null;
+ }
+ }
+
+ private void runMediaCodec() throws InterruptedException {
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
+
+ while (!isInterrupted()) {
+ Frame frame = get();
+
+ int inIndex = mMediaCodec.dequeueInputBuffer(10000);
+ if (inIndex >= 0) {
+ ByteBuffer buffer = inputBuffers[inIndex];
+ buffer.clear();
+ buffer.put(frame.getBuffer(), 0, frame.getLength());
+ buffer.flip();
+
+ mMediaCodec.queueInputBuffer(inIndex, 0, frame.getLength(), 0, 0);
+ }
+
+ frame.release();
+
+ int outIndex = mMediaCodec.dequeueOutputBuffer(info, 10000);
+ if (outIndex >= 0) {
+ mMediaCodec.releaseOutputBuffer(outIndex, true);
+ } else {
+ switch (outIndex) {
+ case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
+ if (DEBUG) {
+ Log.d(TAG, "INFO_OUTPUT_FORMAT_CHANGED");
+ Log.d(TAG, "New format " + mMediaCodec.getOutputFormat());
+ }
+ MediaFormat mf = mMediaCodec.getOutputFormat();
+ int w = mf.getInteger(MediaFormat.KEY_WIDTH);
+ int h = mf.getInteger(MediaFormat.KEY_HEIGHT);
+ postSizeChanged(w, h);
+ break;
+
+ case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
+ case MediaCodec.INFO_TRY_AGAIN_LATER:
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCMJPEGDecoder.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCMJPEGDecoder.java
new file mode 100644
index 0000000000..8ea50c6ee6
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCMJPEGDecoder.java
@@ -0,0 +1,110 @@
+/*
+ UVCMJPEGDecoder.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc.decoder;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.Paint;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Surface;
+
+import org.deviceconnect.android.libuvc.Frame;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+/**
+ * MotionJPEGをデコードしてSurfaceに描画するクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+class UVCMJPEGDecoder implements UVCDecoder {
+ /**
+ * 描画用の Paint.
+ */
+ private final Paint mPaint = new Paint();
+
+ /**
+ * 描画先の Surface.
+ */
+ private Surface mSurface;
+
+ /**
+ * イベントを通知するリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ @Override
+ public void setSurface(final Surface surface) {
+ mSurface = surface;
+ }
+
+ @Override
+ public void setOnEventListener(final OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ @Override
+ public void onInit(final UVCCamera uvcCamera, final Parameter parameter) {
+ }
+
+ @Override
+ public void onReceivedFrame(final Frame frame) {
+ try {
+ drawJpegFrame(frame);
+ } catch (Throwable t) {
+ // ignore.
+ } finally {
+ frame.release();
+ }
+ }
+
+ @Override
+ public void onRelease() {
+ }
+
+ /**
+ * JPEGのフレームデータを Surface に描画します.
+ *
+ * @param frame フレームデータ
+ */
+ private void drawJpegFrame(final Frame frame) {
+ Canvas canvas = mSurface.lockCanvas(null);
+ if (canvas == null) {
+ postError(new RuntimeException("Failed to get a canvas from surface."));
+ return;
+ }
+
+ // TODO canvas に収まるようにリサイズするべきか?
+
+ try {
+ byte[] buffer = frame.getBuffer();
+ int length = frame.getLength();
+ Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0, length);
+ if (bitmap != null) {
+ canvas.drawBitmap(bitmap, 0, 0, mPaint);
+ bitmap.recycle();
+ }
+ } finally {
+ mSurface.unlockCanvasAndPost(canvas);
+ }
+ }
+
+ /**
+ * エラーを通知します.
+ *
+ * @param e 通知する例外
+ */
+ private void postError(final Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(e);
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCUncompressedDecoder.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCUncompressedDecoder.java
new file mode 100644
index 0000000000..8bc16fe25e
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/decoder/UVCUncompressedDecoder.java
@@ -0,0 +1,124 @@
+/*
+ UVCUncompressedDecoder.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc.decoder;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.view.Surface;
+
+import org.deviceconnect.android.libuvc.Frame;
+import org.deviceconnect.android.libuvc.ImageUtils;
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+
+/**
+ * 無圧縮の画像をSurfaceに描画するクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+class UVCUncompressedDecoder implements UVCDecoder {
+ /**
+ * UVCに渡すパラメータ.
+ */
+ private Parameter mParameter;
+
+ /**
+ * UVCからの映像を格納するBitmap.
+ */
+ private Bitmap mBitmap;
+
+ /**
+ * 描画を行うSurface.
+ */
+ private Surface mSurface;
+
+ /**
+ * イベントを通知するリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ @Override
+ public void setSurface(final Surface surface) {
+ mSurface = surface;
+ }
+
+ @Override
+ public void setOnEventListener(final OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ @Override
+ public void onInit(final UVCCamera uvcCamera, final Parameter parameter) {
+ mParameter = parameter;
+ mBitmap = Bitmap.createBitmap(mParameter.getWidth(), mParameter.getHeight(), Bitmap.Config.ARGB_8888);
+ }
+
+ @Override
+ public void onReceivedFrame(final Frame frame) {
+ try {
+ drawFrame(frame);
+ } finally {
+ frame.release();
+ }
+ }
+
+ @Override
+ public void onRelease() {
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ mBitmap = null;
+ }
+ }
+
+ /**
+ * 非圧縮の画像データを Surface に描画します.
+ *
+ * @param frame フレームバッファ
+ */
+ private void drawFrame(final Frame frame) {
+ Canvas canvas = mSurface.lockCanvas(null);
+ if (canvas == null) {
+ postError(new RuntimeException("Failed to get a canvas from surface."));
+ return;
+ }
+
+ try {
+ if (mBitmap == null) {
+ return;
+ }
+
+ boolean result = false;
+ Long guid = mParameter.getExtra("guid");
+ if (guid == null || guid == 0) {
+ result = ImageUtils.decodeYUY2(mBitmap, frame.getBuffer(), mParameter.getWidth(), mParameter.getHeight());
+ } else if (guid == 1) {
+ result = ImageUtils.decodeNV12(mBitmap, frame.getBuffer(), mParameter.getWidth(), mParameter.getHeight());
+ } else if (guid == 2) {
+ result = ImageUtils.decodeM420(mBitmap, frame.getBuffer(), mParameter.getWidth(), mParameter.getHeight());
+ } else if (guid == 3) {
+ result = ImageUtils.decodeI420(mBitmap, frame.getBuffer(), mParameter.getWidth(), mParameter.getHeight());
+ }
+
+ if (result) {
+ canvas.drawBitmap(mBitmap, 0, 0, null);
+ }
+ } finally {
+ mSurface.unlockCanvasAndPost(canvas);
+ }
+ }
+
+ /**
+ * エラーを通知します.
+ *
+ * @param e 通知する例外
+ */
+ private void postError(final Exception e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(e);
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/player/UVCPlayer.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/player/UVCPlayer.java
new file mode 100644
index 0000000000..4c253459f7
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/player/UVCPlayer.java
@@ -0,0 +1,182 @@
+/*
+ UVCPlayer.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc.player;
+
+import android.view.Surface;
+
+import org.deviceconnect.android.libuvc.Parameter;
+import org.deviceconnect.android.libuvc.UVCCamera;
+import org.deviceconnect.android.libuvc.UVCCameraException;
+import org.deviceconnect.android.libuvc.decoder.UVCDecoder;
+import org.deviceconnect.android.libuvc.decoder.UVCDecoderFactory;
+
+import java.io.IOException;
+
+/**
+ * UVCCameraから送られてくるデータをSurfaceに描画を行うクラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class UVCPlayer {
+ /**
+ * 映像を取得するUVCカメラ.
+ */
+ private UVCCamera mUVCCamera;
+
+ /**
+ * 映像を表示するサーフェイス.
+ */
+ private Surface mSurface;
+
+ /**
+ * UVC からの映像をデコードするクラス.
+ */
+ private UVCDecoder mUVCDecoder;
+
+ /**
+ * UVCPlayerのイベントを通知するリスナー.
+ */
+ private OnEventListener mOnEventListener;
+
+ /**
+ * 表示先のSurfaceを設定します.
+ *
+ * @param surface surface
+ */
+ public void setSurface(final Surface surface) {
+ mSurface = surface;
+ }
+
+ /**
+ * Surfaceに描画を開始します.
+ *
+ * @param uvcCamera UVCカメラ
+ * @param parameter 映像を再生するパラメータ
+ * @throws IOException 再生開始に失敗した場合に発生
+ * @throws IllegalArgumentException uvcCamera, parameterがnullの場合に発生
+ * @throws IllegalStateException UVCカメラが既に動作している場合に発生
+ */
+ public void start(final UVCCamera uvcCamera, final Parameter parameter) throws IOException {
+ if (uvcCamera == null) {
+ throw new IllegalArgumentException("uvcCamera is null.");
+ }
+
+ if (parameter == null) {
+ throw new IllegalArgumentException("parameter is null.");
+ }
+
+ if (uvcCamera.isRunning()) {
+ throw new IllegalStateException("UVCCamera is already running.");
+ }
+
+ if (mSurface == null) {
+ throw new IOException("Surface is not set.");
+ }
+
+ mUVCCamera = uvcCamera;
+
+ mUVCDecoder = UVCDecoderFactory.create(parameter);
+ mUVCDecoder.setSurface(mSurface);
+ mUVCDecoder.setOnEventListener(e -> postError(new UVCPlayerException(e)));
+ mUVCDecoder.onInit(uvcCamera, parameter);
+
+ mUVCCamera.setOnEventListener(new UVCCamera.OnEventListener() {
+ @Override
+ public void onStart() {
+ postStarted();
+ }
+
+ @Override
+ public void onStop() {
+ postStopped();
+ }
+
+ @Override
+ public void onError(final UVCCameraException e) {
+ postError(new UVCPlayerException(e));
+ }
+ });
+ mUVCCamera.setPreviewCallback(frame -> mUVCDecoder.onReceivedFrame(frame));
+ mUVCCamera.startVideo(parameter);
+ }
+
+ /**
+ * 再生を停止します.
+ */
+ public void stop() {
+ try {
+ mUVCCamera.stopVideo();
+ } catch (IOException e) {
+ // ignore.
+ }
+
+ try {
+ mUVCDecoder.onRelease();
+ } catch (Exception e) {
+ // ignore.
+ }
+ }
+
+ /**
+ * UVCPlayerで発生したイベントを通知するリスナーを設定します.
+ * @param listener リスナー
+ */
+ public void setOnEventListener(final OnEventListener listener) {
+ mOnEventListener = listener;
+ }
+
+ /**
+ * 開始イベントをリスナーに通知します.
+ */
+ private void postStarted() {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStarted();
+ }
+ }
+
+ /**
+ * 停止イベントをリスナーに通知します.
+ */
+ private void postStopped() {
+ if (mOnEventListener != null) {
+ mOnEventListener.onStopped();
+ }
+ }
+
+ /**
+ * エラーイベントをリスナーに通知します.
+ *
+ * @param e エラー
+ */
+ private void postError(final UVCPlayerException e) {
+ if (mOnEventListener != null) {
+ mOnEventListener.onError(e);
+ }
+ }
+
+ /**
+ * UVCPlayerで発生したイベントを通知するためのリスナー.
+ */
+ public interface OnEventListener {
+ /**
+ * UVCPlayerの再生が開始されたことを通知します.
+ */
+ void onStarted();
+
+ /**
+ * UVCPlayerの再生が停止されたことを通知します.
+ */
+ void onStopped();
+
+ /**
+ * UVCPlayerでエラーが発生したことを通知します.
+ *
+ * @param e エラー原因の例外
+ */
+ void onError(UVCPlayerException e);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/player/UVCPlayerException.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/player/UVCPlayerException.java
new file mode 100644
index 0000000000..861a8d2306
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/player/UVCPlayerException.java
@@ -0,0 +1,31 @@
+/*
+ UVCPlayerException.java
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+package org.deviceconnect.android.libuvc.player;
+
+/**
+ * UVCPlayerで発生したエラーの例外クラス.
+ *
+ * @author NTT DOCOMO, INC.
+ */
+public class UVCPlayerException extends RuntimeException {
+
+ /**
+ * コンストラクタ.
+ * @param e 例外
+ */
+ UVCPlayerException(final Exception e) {
+ super(e);
+ }
+
+ /**
+ * コンストラクタ.
+ * @param message エラーメッセージ
+ */
+ UVCPlayerException(final String message) {
+ super(message);
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/utils/QueueThread.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/utils/QueueThread.java
new file mode 100644
index 0000000000..6ab81be0c6
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/utils/QueueThread.java
@@ -0,0 +1,36 @@
+package org.deviceconnect.android.libuvc.utils;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+public abstract class QueueThread extends Thread {
+ private final Queue mQueue = new LinkedList<>();
+
+ public synchronized int size() {
+ return mQueue.size();
+ }
+
+ public synchronized void add(T data) {
+ mQueue.offer(data);
+ if (mQueue.size() <= 1) {
+ notifyAll();
+ }
+ }
+
+ public synchronized T get() throws InterruptedException {
+ while (mQueue.peek() == null) {
+ wait();
+ }
+ return mQueue.remove();
+ }
+
+ public synchronized void close() {
+ interrupt();
+
+ try {
+ join(500);
+ } catch (InterruptedException e) {
+ // ignore.
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/utils/WeakReferenceList.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/utils/WeakReferenceList.java
new file mode 100644
index 0000000000..74fd44e59e
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/java/org/deviceconnect/android/libuvc/utils/WeakReferenceList.java
@@ -0,0 +1,65 @@
+package org.deviceconnect.android.libuvc.utils;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class WeakReferenceList implements Iterable {
+ private final List> mList = new ArrayList<>();
+
+ public void add(T value) {
+ mList.add(new WeakReference<>(value));
+ }
+
+ public void remove(T value) {
+ for (Iterator> iterator = mList.iterator(); iterator.hasNext(); ) {
+ WeakReference weakRef = iterator.next();
+ if (weakRef.get() == value) {
+ iterator.remove();
+ }
+ }
+ }
+
+ public void clear() {
+ mList.clear();
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new ListIterator();
+ }
+
+ private class ListIterator implements Iterator {
+ private final Iterator> mIterator;
+ private T mNextValue;
+
+ ListIterator() {
+ mIterator = mList.iterator();
+ mNextValue = get();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mNextValue != null;
+ }
+
+ @Override
+ public T next() {
+ T value = mNextValue;
+ mNextValue = get();
+ return value;
+ }
+
+ private T get() {
+ while (mIterator.hasNext()) {
+ WeakReference value = mIterator.next();
+ if (value.get() != null) {
+ return value.get();
+ }
+ mIterator.remove();
+ }
+ return null;
+ }
+ }
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/common.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/common.h
new file mode 100644
index 0000000000..6f8155982b
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/common.h
@@ -0,0 +1,63 @@
+#ifndef UVC_COMMON_H
+#define UVC_COMMON_H
+
+#include
+#include
+
+#ifdef DEBUG
+#define LOG_TAG "UVC-JNI"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#else
+#define LOGI(...)
+#define LOGD(...)
+#define LOGE(...)
+#define LOGW(...)
+#endif
+
+
+#define SAFE_FREE(p) { free(p); p = NULL; }
+
+
+/**
+ * 操作の結果を定義.
+ */
+typedef enum _uvc_result {
+ /**
+ * 操作に成功.
+ */
+ UVC_SUCCESS = 0,
+
+ /**
+ * 操作に失敗.
+ */
+ UVC_ERROR = -1,
+
+ /**
+ * 既に UVC カメラが動作している場合のエラー定義.
+ */
+ UVC_ALREADY_RUNNING = -2,
+
+ /**
+ * メモリ不足の場合のエラー定義.
+ */
+ UVC_OUT_OF_MEMORY = -3,
+
+ /**
+ * パラメータが不正な場合のエラー定義.
+ */
+ UVC_PARAMETER_INVALID = -4,
+} uvc_result;
+
+/**
+ * UVC用のBOOL定義.
+ */
+enum {
+ UVC_TRUE = 1,
+ UVC_FALSE = 0
+};
+
+
+#endif //UVC_COMMON_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/image-utils.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/image-utils.cpp
new file mode 100644
index 0000000000..69af410e45
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/image-utils.cpp
@@ -0,0 +1,365 @@
+/*
+ image-utils.cpp
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "common.h"
+
+/**
+ * JNI のパッケージ名、クラス名を定義.
+ */
+#define JNI_METHOD_NAME(name) \
+ Java_org_deviceconnect_android_libuvc_ImageUtils_##name
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * JPEGヘッダーで使用されるハフマンテーブルのサイズ.
+ */
+static const size_t huff_tbl_size = 432;
+
+/**
+ * JPEGヘッダーで使用されるハフマンテーブル.
+ */
+static const uint8_t huff_tbl[huff_tbl_size] = {
+ // dc_huff_tbl_0
+ 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b,
+
+ // ac_huff_tbl_0
+ 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
+ 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+ 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
+ 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14,
+ 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15,
+ 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29,
+ 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
+ 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
+ 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa,
+
+ // dc_huff_tbl_1
+ 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b,
+
+ // ac_huff_tbl_1
+ 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+ 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02,
+ 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
+ 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32,
+ 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23,
+ 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24,
+ 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
+ 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4,
+ 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5,
+ 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+ 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa
+};
+
+
+/**
+ * YUVのピクセルフォーマットを定義します。
+ *
+ * Java側でも同じ定義をしていますので、修正する場合には注意してください。
+ *
+ */
+enum {
+ FMT_YUY2 = 0,
+ FMT_NV12 = 1,
+ FMT_M420 = 2,
+ FMT_I420 = 3
+};
+
+
+/**
+ * 指定された値が0〜255に収まるようにクリップします.
+ *
+ * @param a 変換する値
+ * @return 0〜255に収まるように変換された値
+ */
+static uint8_t clip(double a) {
+ if (a < 0) {
+ return 0;
+ } else if (a > 255) {
+ return 255;
+ } else {
+ return (uint8_t) a;
+ }
+}
+
+
+/**
+ * YUVの値をRGBに変換します.
+ *
+ * @param Y 輝度信号
+ * @param U 青色成分の差分信号
+ * @param V 赤色成分の差分信号
+ * @return RGB
+ */
+static uint32_t yuv2rgb(uint8_t Y, uint8_t U, uint8_t V) {
+ uint8_t R = clip(1.164 * (Y - 16) + 1.596 * (V - 128));
+ uint8_t G = clip(1.164 * (Y - 16) - 0.391 * (U - 128) - 0.813 * (V - 128));
+ uint8_t B = clip(1.164 * (Y - 16) + 2.018 * (U - 128));
+ return 0xFF000000 | (B << 16) | (G << 8) | R;
+}
+
+
+/**
+ * YUY2の値をRGBのバッファに格納します.
+ *
+ * @param src YUY2のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @param dst 出力先のバッファ
+ */
+static void yuy2toRGB(uint8_t *src, uint32_t width, uint32_t height, uint32_t *dst) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x += 2) {
+ uint8_t Y0 = *src++;
+ uint8_t U = *src++;
+ uint8_t Y1 = *src++;
+ uint8_t V = *src++;
+ *dst++ = yuv2rgb(Y0, U, V);
+ *dst++ = yuv2rgb(Y1, U, V);
+ }
+ }
+}
+
+
+/**
+ * NV12の値をRGBのバッファに格納します.
+ *
+ * @param src NV12のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @param dst 出力先のバッファ
+ */
+static void nv12toRGB(uint8_t *src, uint32_t width, uint32_t height, uint32_t *dst) {
+ uint8_t *uv = (src + width * height);
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x ++) {
+ int index = y / 2 + x / 2;
+ uint8_t Y = *src++;
+ uint8_t U = *(uv + index);
+ uint8_t V = *(uv + index + 1);
+ *dst++ = yuv2rgb(Y, U, V);
+ }
+ }
+}
+
+
+/**
+ * M420の値をRGBのバッファに格納します.
+ *
+ * @param src M420のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @param dst 出力先のバッファ
+ */
+static void m420toRGB(uint8_t *src, uint32_t width, uint32_t height, uint32_t *dst) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x ++) {
+ int yIndex = x + (y + y / 2) * width;
+ int uvIndex = x / 2 + 2 * width;
+ uint8_t Y = *(src + yIndex);
+ uint8_t U = *(src + yIndex + uvIndex);
+ uint8_t V = *(src + yIndex + uvIndex + 1);
+ *dst++ = yuv2rgb(Y, U, V);
+ }
+ }
+}
+
+
+/**
+ * I420の値をRGBのバッファに格納します.
+ *
+ * @param src I420のデータ
+ * @param width 横幅
+ * @param height 縦幅
+ * @param dst 出力先のバッファ
+ */
+static void i420toRGB(uint8_t *src, uint32_t width, uint32_t height, uint32_t *dst) {
+ uint8_t *uv = (src + width * height);
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x ++) {
+ uint8_t Y = *src++;
+ uint8_t U = *(uv + y);
+ uint8_t V = *(uv + y + 1);
+ *dst++ = yuv2rgb(Y, U, V);
+ }
+ }
+}
+
+
+//////////////////////////////
+
+
+JNIEXPORT jboolean JNICALL JNI_METHOD_NAME(nativeDecodeYUV)(JNIEnv *env, jclass clazz, jobject bitmap, jbyteArray yuvArray, jint width, jint height, jint type) {
+ AndroidBitmapInfo info;
+ uint32_t *pixels;
+
+ if (bitmap == NULL) {
+ return JNI_FALSE;
+ }
+
+ if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
+ return JNI_FALSE;
+ }
+
+ if (info.width != width || info.height != height) {
+ return JNI_FALSE;
+ }
+
+ if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
+ return JNI_FALSE;
+ }
+
+ if (AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels) < 0) {
+ return JNI_FALSE;
+ }
+
+ jboolean b;
+ jbyte *yuv = env->GetByteArrayElements(yuvArray, &b);
+ if (yuv == NULL) {
+ AndroidBitmap_unlockPixels(env, bitmap);
+ return JNI_FALSE;
+ }
+
+ jboolean result = JNI_TRUE;
+ switch (type) {
+ case FMT_YUY2:
+ yuy2toRGB((uint8_t *) yuv, (uint32_t) width, (uint32_t) height, pixels);
+ break;
+ case FMT_NV12:
+ nv12toRGB((uint8_t *) yuv, (uint32_t) width, (uint32_t) height, pixels);
+ break;
+ case FMT_M420:
+ m420toRGB((uint8_t *) yuv, (uint32_t) width, (uint32_t) height, pixels);
+ break;
+ case FMT_I420:
+ i420toRGB((uint8_t *) yuv, (uint32_t) width, (uint32_t) height, pixels);
+ break;
+ default:
+ result = JNI_FALSE;
+ break;
+ }
+
+ env->ReleaseByteArrayElements(yuvArray, yuv, 0);
+
+ AndroidBitmap_unlockPixels(env, bitmap);
+
+ return result;
+}
+
+
+JNIEXPORT void JNICALL JNI_METHOD_NAME(nativeCopyJpeg)(JNIEnv *env, jclass clazz, jbyteArray srcArray, jint srcLength, jbyteArray destArray) {
+ jboolean isCopy;
+ uint8_t *src = (uint8_t *) env->GetByteArrayElements(srcArray, &isCopy);
+ if (src == NULL) {
+ return;
+ }
+
+ uint8_t *dest = (uint8_t *) env->GetByteArrayElements(destArray, &isCopy);
+ if (dest == NULL) {
+ env->ReleaseByteArrayElements(srcArray, (jbyte *) src, 0);
+ return;
+ }
+
+ memcpy(dest, src, 2);
+ memcpy((dest + 2), huff_tbl, huff_tbl_size);
+ memcpy((dest + 2 + huff_tbl_size), src + 2, (size_t) srcLength - 2);
+
+ env->ReleaseByteArrayElements(srcArray, (jbyte *) src, 0);
+ env->ReleaseByteArrayElements(destArray, (jbyte *) dest, 0);
+}
+
+
+JNIEXPORT void JNICALL JNI_METHOD_NAME(nativeDecodeYUV420SP)(JNIEnv *env, jclass clazz, jobject bitmap, jbyteArray yuv420spArray, jint width, jint height) {
+ AndroidBitmapInfo info;
+ int *pixels;
+
+ if (bitmap == NULL) {
+ return;
+ }
+
+ if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
+ return;
+ }
+
+ if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
+ return;
+ }
+
+ if (AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels) < 0) {
+ return;
+ }
+
+ jboolean b;
+ jbyte *yuv420sp = env->GetByteArrayElements(yuv420spArray, &b);
+
+ jint frameSize = width * height;
+ for (int j = 0, yp = 0; j < height; j++) {
+ int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
+ for (int i = 0; i < width; i++, yp++) {
+ int y = (0xff & ((int) yuv420sp[yp])) - 16;
+ if (y < 0) y = 0;
+ if ((i & 1) == 0) {
+ v = (0xff & yuv420sp[uvp++]) - 128;
+ u = (0xff & yuv420sp[uvp++]) - 128;
+ }
+ int y1192 = 1192 * y;
+ int r = (y1192 + 1634 * v);
+ int g = (y1192 - 833 * v - 400 * u);
+ int b = (y1192 + 2066 * u);
+ if (r < 0) r = 0;
+ else if (r > 262143) r = 262143;
+ if (g < 0) g = 0;
+ else if (g > 262143) g = 262143;
+ if (b < 0) b = 0;
+ else if (b > 262143) b = 262143;
+ pixels[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
+ }
+ }
+
+ env->ReleaseByteArrayElements(yuv420spArray, yuv420sp, 0);
+
+ AndroidBitmap_unlockPixels(env, bitmap);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-descriptor.c b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-descriptor.c
new file mode 100644
index 0000000000..de6fdbe7c2
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-descriptor.c
@@ -0,0 +1,1625 @@
+/*
+ uvc-descriptor.c
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#include
+#include
+
+#include "uvc-descriptor.h"
+
+#define BYTE_TO_SHORT(x) ((x++) | ((x++) << 8))
+
+#define BYTE_TO_INT(x) ((x++) | ((x++) << 8) | ((x++) << 16) | ((x++) << 24))
+
+/**
+ * 指定されたサイズでメモリを確保します。
+ *
+ * 確保したメモリは全て 0 で初期化します。
+ *
+ */
+#define CALLOC(x) ((x*) calloc(1, sizeof(x)))
+
+static const uint8_t GUID_YUY2_UNCOMPRESSED[] = {
+ 0x59, 0x55, 0x59, 0x32, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71
+};
+
+static const uint8_t GUID_NV12_UNCOMPRESSED[] = {
+ 0x4E, 0x56, 0x31, 0x32, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71
+};
+
+static const uint8_t GUID_M420_UNCOMPRESSED[] = {
+ 0x4D, 0x34, 0x32, 0x30, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71
+};
+
+static const uint8_t GUID_I420_UNCOMPRESSED[] = {
+ 0x49, 0x34, 0x32, 0x30, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0xAA, 0x0, 0x38, 0x9B, 0x71
+};
+
+static void uvc_add_configuration(struct uvc_descriptor *descriptor,
+ struct uvc_configuration_descriptor *configuration) {
+ if (configuration == NULL) {
+ LOGE("uvc_add_configuration: configuration is NULL.");
+ return;
+ }
+
+ if (descriptor->configuration == NULL) {
+ descriptor->configuration = configuration;
+ } else {
+ struct uvc_configuration_descriptor *c = descriptor->configuration;
+ while (c->next) {
+ c = c->next;
+ }
+ c->next = configuration;
+ }
+}
+
+
+static void uvc_add_video_streaming_interface(struct uvc_configuration_descriptor *configuration,
+ struct uvc_video_streaming_interface *streaming_interface) {
+ if (streaming_interface == NULL) {
+ LOGE("uvc_add_video_streaming_interface: streaming_interface is NULL.");
+ return;
+ }
+
+ if (configuration->streaming_interface == NULL) {
+ configuration->streaming_interface = streaming_interface;
+ } else {
+ struct uvc_video_streaming_interface *s = configuration->streaming_interface;
+ while (s->next) {
+ s = s->next;
+ }
+ s->next = streaming_interface;
+ }
+}
+
+static void uvc_add_vc_input_terminal(struct uvc_video_control_interface *control,
+ struct uvc_vc_input_terminal_descriptor *input_terminal) {
+ if (input_terminal == NULL) {
+ LOGE("uvc_add_vc_input_terminal: input_terminal is NULL.");
+ return;
+ }
+
+ if (control->input_terminal == NULL) {
+ control->input_terminal = input_terminal;
+ } else {
+ struct uvc_vc_input_terminal_descriptor *p = control->input_terminal;
+ while (p->next) {
+ p = p->next;
+ }
+ p->next = input_terminal;
+ }
+}
+
+static void uvc_add_vc_output_terminal(struct uvc_video_control_interface *control,
+ struct uvc_vc_output_terminal_descriptor *output_terminal) {
+ if (output_terminal == NULL) {
+ LOGE("uvc_add_vc_output_terminal: output_terminal is NULL.");
+ return;
+ }
+
+ if (control->output_terminal == NULL) {
+ control->output_terminal = output_terminal;
+ } else {
+ struct uvc_vc_output_terminal_descriptor *p = control->output_terminal;
+ while (p->next) {
+ p = p->next;
+ }
+ p->next = output_terminal;
+ }
+}
+
+
+static void uvc_add_vc_selector_unit(struct uvc_video_control_interface *control,
+ struct uvc_vc_selector_unit_descriptor *selector) {
+ if (selector == NULL) {
+ LOGE("uvc_add_vc_selector_unit: selector is NULL.");
+ return;
+ }
+
+ if (control->selector == NULL) {
+ control->selector = selector;
+ } else {
+ struct uvc_vc_selector_unit_descriptor *p = control->selector;
+ while (p->next) {
+ p = p->next;
+ }
+ p->next = selector;
+ }
+}
+
+static void uvc_add_vc_processing_unit(struct uvc_video_control_interface *control,
+ struct uvc_vc_processing_unit_descriptor *processing) {
+ if (processing == NULL) {
+ LOGE("uvc_add_vc_processing_unit: processing is NULL.");
+ return;
+ }
+
+ if (control->processing == NULL) {
+ control->processing = processing;
+ } else {
+ struct uvc_vc_processing_unit_descriptor *p = control->processing;
+ while (p->next) {
+ p = p->next;
+ }
+ p->next = processing;
+ }
+}
+
+static void uvc_add_vc_extension_unit(struct uvc_video_control_interface *control,
+ struct uvc_vc_extension_unit_descriptor *extension) {
+ if (extension == NULL) {
+ LOGE("uvc_add_vc_extension_unit: extension is NULL.");
+ return;
+ }
+
+ if (control->extension == NULL) {
+ control->extension = extension;
+ } else {
+ struct uvc_vc_extension_unit_descriptor *e = control->extension;
+ while (e->next) {
+ e = e->next;
+ }
+ e->next = extension;
+ }
+}
+
+
+static void uvc_add_vc_encoding_unit(struct uvc_video_control_interface *control,
+ struct uvc_vc_encoding_unit_descriptor *encoding) {
+ if (encoding == NULL) {
+ LOGE("uvc_add_vc_encoding_unit: encoding is NULL.");
+ return;
+ }
+
+ if (control->encoding == NULL) {
+ control->encoding = encoding;
+ } else {
+ struct uvc_vc_encoding_unit_descriptor *e = control->encoding;
+ while (e->next) {
+ e = e->next;
+ }
+ e->next = encoding;
+ }
+}
+
+
+static void uvc_add_vs_format(struct uvc_video_streaming_interface *stream,
+ struct uvc_vs_format_descriptor *format) {
+ if (format == NULL) {
+ LOGE("uvc_add_vs_format: format is NULL.");
+ return;
+ }
+
+ if (stream->format == NULL) {
+ stream->format = format;
+ } else {
+ struct uvc_vs_format_descriptor *e = stream->format;
+ while (e->next) {
+ e = e->next;
+ }
+ e->next = format;
+ }
+}
+
+
+static void uvc_add_vs_frame(struct uvc_vs_format_descriptor *format,
+ struct uvc_vs_frame_descriptor *frame) {
+ if (format == NULL) {
+ LOGE("uvc_add_vs_frame: format is NULL.");
+ return;
+ }
+
+ if (format->frame == NULL) {
+ format->frame = frame;
+ } else {
+ struct uvc_vs_frame_descriptor *e = format->frame;
+ while (e->next) {
+ e = e->next;
+ }
+ e->next = frame;
+ }
+}
+
+static void uvc_add_uvc_streaming_altsetting(struct uvc_configuration_descriptor *configuration,
+ struct uvc_video_streaming_altsetting *altsetting) {
+ if (configuration == NULL) {
+ return;
+ }
+ if (altsetting == NULL) {
+ return;
+ }
+
+ if (configuration->altsetting == NULL) {
+ configuration->altsetting = altsetting;
+ } else {
+ struct uvc_video_streaming_altsetting *a = configuration->altsetting;
+ while (a->next) {
+ a = a->next;
+ }
+ a->next = altsetting;
+ }
+}
+
+static void uvc_add_vs_color_matching(struct uvc_video_streaming_interface *stream,
+ struct uvc_vs_color_matching_descriptor *color) {
+ if (stream == NULL) {
+ return;
+ }
+
+ if (stream->color_matching == NULL) {
+ stream->color_matching = color;
+ } else {
+ struct uvc_vs_color_matching_descriptor *c = stream->color_matching;
+ while (c->next) {
+ c = c->next;
+ }
+ c->next = color;
+ }
+}
+
+static struct uvc_vs_format_descriptor *uvc_get_last_format(struct uvc_video_streaming_interface *streaming) {
+ if (streaming->format) {
+ struct uvc_vs_format_descriptor *s = streaming->format;
+ while (s->next) {
+ s = s->next;
+ }
+ return s;
+ }
+ return NULL;
+}
+
+static struct uvc_device_descriptor *uvc_parse_device_descriptor(uint8_t *buffer) {
+ struct uvc_device_descriptor *device = CALLOC(struct uvc_device_descriptor);
+ if (device == NULL) {
+ LOGE("uvc_parse_device_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ device->bLength = *buffer++;
+ device->bDescriptorType = *buffer++;
+ device->bcdUSB = BYTE_TO_SHORT(*buffer);
+ device->bDeviceClass = *buffer++;
+ device->bDeviceSubClass = *buffer++;
+ device->bDeviceProtocol = *buffer++;
+ device->bMaxPacketSize0 = *buffer++;
+ device->idVendor = BYTE_TO_SHORT(*buffer);
+ device->idProduct = BYTE_TO_SHORT(*buffer);
+ device->bcdDevice = BYTE_TO_SHORT(*buffer);
+ device->iManufacturer = *buffer++;
+ device->iProduct = *buffer++;
+ device->iSerialNumber = *buffer++;
+ device->bNumConfigurations = *buffer;
+ return device;
+}
+
+static struct uvc_configuration_descriptor *uvc_parse_configuration_descriptor(uint8_t *buffer) {
+ struct uvc_configuration_descriptor *config = CALLOC(struct uvc_configuration_descriptor);
+ if (config == NULL) {
+ LOGE("uvc_parse_configuration_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ config->bLength = *buffer++;
+ config->bDescriptorType = *buffer++;
+ config->wTotalLength = BYTE_TO_SHORT(*buffer);
+ config->bNumInterfaces = *buffer++;
+ config->bConfigurationValue = *buffer++;
+ config->iConfiguration = *buffer++;
+ config->bmAttributes = *buffer++;
+ config->bMaxPower = *buffer;
+
+ return config;
+}
+
+static struct uvc_interface_descriptor *uvc_parse_interface_descriptor(uint8_t *buffer) {
+ struct uvc_interface_descriptor *interface = CALLOC(struct uvc_interface_descriptor);
+ if (interface == NULL) {
+ LOGE("uvc_parse_interface_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ interface->bLength = *buffer++;
+ interface->bDescriptorType = *buffer++;
+ interface->bInterfaceNumber = *buffer++;
+ interface->bAlternateSetting = *buffer++;
+ interface->bNumEndpoints = *buffer++;
+ interface->bInterfaceClass = *buffer++;
+ interface->bInterfaceSubClass = *buffer++;
+ interface->bInterfaceProtocol = *buffer++;
+ interface->iInterface = *buffer;
+
+ return interface;
+}
+
+static struct uvc_endpoint_descriptor *uvc_parse_endpoint(uint8_t *buffer) {
+ struct uvc_endpoint_descriptor *endpoint = CALLOC(struct uvc_endpoint_descriptor);
+ if (endpoint == NULL) {
+ LOGE("uvc_endpoint_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ endpoint->bLength = *buffer++;
+ endpoint->bDescriptorType = *buffer++;
+ endpoint->bEndpointAddress = *buffer++;
+ endpoint->bmAttributes = *buffer++;
+ endpoint->wMaxPacketSize = BYTE_TO_SHORT(*buffer);
+ endpoint->bInterval = *buffer;
+
+ return endpoint;
+}
+
+static struct uvc_interrupt_endpoint_descriptor * uvc_parse_interrupt_endpoint(uint8_t *buffer) {
+ struct uvc_interrupt_endpoint_descriptor *endpoint = CALLOC(struct uvc_interrupt_endpoint_descriptor);
+ if (endpoint == NULL) {
+ LOGE("uvc_parse_interrupt_endpoint: Out of memory.");
+ return NULL;
+ }
+
+ endpoint->bLength = *buffer++;
+ endpoint->bDescriptorType = *buffer++;
+ endpoint->bDescriptorSubType = *buffer++;
+ endpoint->wMaxTransferSize = BYTE_TO_SHORT(*buffer);
+ return endpoint;
+}
+
+static struct uvc_interface_association_descriptor *uvc_parse_iad(uint8_t *buffer) {
+ struct uvc_interface_association_descriptor *iad = CALLOC(struct uvc_interface_association_descriptor);
+ if (iad == NULL) {
+ LOGE("uvc_interface_association_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ iad->bLength = *buffer++;
+ iad->bDescriptorType = *buffer++;
+ iad->bFirstInterface = *buffer++;
+ iad->bInterfaceCount = *buffer++;
+ iad->bFunctionClass = *buffer++;
+ iad->bFunctionSubClass = *buffer++;
+ iad->bFunctionProtocol = *buffer++;
+ iad->iFunction = *buffer;
+ return iad;
+}
+
+static void uvc_free_vc_header(struct uvc_vc_header_descriptor *header) {
+ if (header) {
+ SAFE_FREE(header->baInterfaceNr);
+ SAFE_FREE(header);
+ }
+}
+
+static struct uvc_vc_header_descriptor *uvc_parse_vc_header(uint8_t *buffer) {
+ struct uvc_vc_header_descriptor *header = CALLOC(struct uvc_vc_header_descriptor);
+ if (header == NULL) {
+ LOGE("uvc_vc_header_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ header->bLength = *buffer++;
+ header->bDescriptorType = *buffer++;
+ header->bDescriptorSubType = *buffer++;
+ header->bcdUVC = BYTE_TO_SHORT(*buffer);
+ header->wTotalLength = BYTE_TO_SHORT(*buffer);
+ header->dwClockFrequency = BYTE_TO_INT(*buffer);
+ header->bInCollection = *buffer++;
+ header->baInterfaceNr = (uint8_t *) calloc(1, header->bInCollection);
+ if (header->baInterfaceNr == NULL) {
+ LOGE("uvc_vc_header_descriptor: Out of memory.");
+ uvc_free_vc_header(header);
+ return NULL;
+ }
+
+ for (int i = 0; i < header->bInCollection; i++) {
+ header->baInterfaceNr[i] = *buffer++;
+ }
+
+ return header;
+}
+
+static void uvc_free_vc_input_terminal(struct uvc_vc_input_terminal_descriptor *input_terminal) {
+ if (input_terminal) {
+ uvc_free_vc_input_terminal(input_terminal->next);
+ SAFE_FREE(input_terminal->bmControls);
+ SAFE_FREE(input_terminal->bmTransportModes);
+ SAFE_FREE(input_terminal);
+ }
+}
+
+static struct uvc_vc_input_terminal_descriptor *uvc_parse_vc_input_terminal(uint8_t *buffer) {
+ struct uvc_vc_input_terminal_descriptor *input_terminal = CALLOC(struct uvc_vc_input_terminal_descriptor);
+ if (input_terminal == NULL) {
+ LOGE("uvc_vc_input_terminal_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ input_terminal->bLength = *buffer++;
+ input_terminal->bDescriptorType = *buffer++;
+ input_terminal->bDescriptorSubType = *buffer++;
+ input_terminal->bTerminalID = *buffer++;
+ input_terminal->wTerminalType = BYTE_TO_SHORT(*buffer);
+ input_terminal->bAssocTerminal = *buffer++;
+ input_terminal->iTerminal = *buffer++;
+
+ if (input_terminal->wTerminalType == ITT_CAMERA) {
+ input_terminal->wObjectiveFocalLengthMin = BYTE_TO_SHORT(*buffer);
+ input_terminal->wObjectiveFocalLengthMax = BYTE_TO_SHORT(*buffer);
+ input_terminal->wOcularFocalLength = BYTE_TO_SHORT(*buffer);
+ }
+
+ input_terminal->bControlSize = *buffer;
+ input_terminal->bmControls = (uint8_t *) calloc(1, input_terminal->bControlSize);
+ if (input_terminal->bmControls == NULL) {
+ LOGE("uvc_vc_input_terminal_descriptor: Out of memory.");
+ uvc_free_vc_input_terminal(input_terminal);
+ return NULL;
+ }
+
+ for (int i = 0; i < input_terminal->bControlSize; i++) {
+ input_terminal->bmControls[i] = *buffer++;
+ }
+
+ if (input_terminal->wTerminalType == ITT_MEDIA_TRANSPORT_INPUT) {
+ input_terminal->bTransportModeSize = *buffer++;
+ input_terminal->bmTransportModes = (uint8_t *) calloc(1, input_terminal->bTransportModeSize);
+ if (input_terminal->bmTransportModes == NULL) {
+ LOGE("uvc_vc_input_terminal_descriptor: Out of memory.");
+ uvc_free_vc_input_terminal(input_terminal);
+ return NULL;
+ }
+
+ for (int i = 0; i < input_terminal->bTransportModeSize; i++) {
+ input_terminal->bmTransportModes[i] = *buffer++;
+ }
+ }
+
+ return input_terminal;
+}
+
+static void uvc_free_vc_output_terminal(struct uvc_vc_output_terminal_descriptor *output_terminal) {
+ if (output_terminal) {
+ uvc_free_vc_output_terminal(output_terminal->next);
+ SAFE_FREE(output_terminal);
+ }
+}
+
+static struct uvc_vc_output_terminal_descriptor *uvc_parse_vc_output_terminal(uint8_t *buffer) {
+ struct uvc_vc_output_terminal_descriptor *output_terminal = CALLOC(struct uvc_vc_output_terminal_descriptor);
+ if (output_terminal == NULL) {
+ LOGE("uvc_vc_output_terminal_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ output_terminal->bLength = *buffer++;
+ output_terminal->bDescriptorType = *buffer++;
+ output_terminal->bDescriptorSubType = *buffer++;
+ output_terminal->bTerminalID = *buffer++;
+ output_terminal->wTerminalType = BYTE_TO_SHORT(*buffer);
+ output_terminal->bAssocTerminal = *buffer++;
+ output_terminal->bSourceID = *buffer++;
+ output_terminal->iTerminal = *buffer;
+
+ return output_terminal;
+}
+
+static void uvc_free_vc_processing_unit(struct uvc_vc_processing_unit_descriptor *processing) {
+ if (processing) {
+ uvc_free_vc_processing_unit(processing->next);
+ SAFE_FREE(processing->bmControls);
+ SAFE_FREE(processing);
+ }
+}
+
+static struct uvc_vc_processing_unit_descriptor *uvc_parse_vc_processing_unit(uint8_t *buffer) {
+ struct uvc_vc_processing_unit_descriptor *processing =CALLOC(struct uvc_vc_processing_unit_descriptor);
+ if (processing == NULL) {
+ LOGE("uvc_vc_processing_unit_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ processing->bLength = *buffer++;
+ processing->bDescriptorType = *buffer++;
+ processing->bDescriptorSubType = *buffer++;
+ processing->bUnitID = *buffer++;
+ processing->bSourceID = *buffer++;
+ processing->wMaxMultiplier = BYTE_TO_SHORT(*buffer);
+ processing->bControlSize = *buffer++;
+ processing->bmControls = (uint8_t *) calloc(1, processing->bControlSize);
+ if (processing->bmControls == NULL) {
+ LOGE("uvc_vc_processing_unit_descriptor: Out of memory.");
+ uvc_free_vc_processing_unit(processing);
+ return NULL;
+ }
+
+ for (int i = 0; i < processing->bControlSize; i++) {
+ processing->bmControls[i] = *buffer++;
+ }
+ processing->iProcessing = *buffer++;
+ processing->bmVideoStandards = *buffer;
+
+ return processing;
+}
+
+static void uvc_free_vc_extension_unit(struct uvc_vc_extension_unit_descriptor *extension) {
+ if (extension) {
+ uvc_free_vc_extension_unit(extension->next);
+ SAFE_FREE(extension->baSourceID);
+ SAFE_FREE(extension->bmControls);
+ SAFE_FREE(extension);
+ }
+}
+
+static struct uvc_vc_extension_unit_descriptor *uvc_parse_vc_extension_unit(uint8_t *buffer) {
+ struct uvc_vc_extension_unit_descriptor *extension = CALLOC(struct uvc_vc_extension_unit_descriptor);
+ if (extension == NULL) {
+ LOGE("uvc_vc_extension_unit_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ extension->bLength = *buffer++;
+ extension->bDescriptorType = *buffer++;
+ extension->bDescriptorSubType = *buffer++;
+ extension->bUnitID = *buffer++;
+ for (int i = 0; i < 16; i++) {
+ extension->guidExtensionCode[i] = *buffer++;
+ }
+ extension->bNumControls = *buffer++;
+ extension->bNrInPins = *buffer++;
+ extension->baSourceID = (uint8_t *) calloc(1, extension->bNrInPins);
+ if (extension->baSourceID == NULL) {
+ LOGE("uvc_vc_extension_unit_descriptor: Out of memory.");
+ uvc_free_vc_extension_unit(extension);
+ return NULL;
+ }
+
+ for (int i = 0; i < extension->bNrInPins; i++) {
+ extension->baSourceID[i] = *buffer++;
+ }
+
+
+ extension->bControlSize = *buffer++;
+ extension->bmControls = (uint8_t *) calloc(1, extension->bControlSize);
+ if (extension->bmControls == NULL) {
+ LOGE("uvc_vc_extension_unit_descriptor: Out of memory.");
+ uvc_free_vc_extension_unit(extension);
+ return NULL;
+ }
+
+ for (int i = 0; i < extension->bControlSize; i++) {
+ extension->bmControls[i] = *buffer++;
+ }
+ extension->iExtension = *buffer;
+
+ return extension;
+}
+
+static void uvc_free_vc_selector_unit(struct uvc_vc_selector_unit_descriptor *selector) {
+ if (selector) {
+ uvc_free_vc_selector_unit(selector->next);
+ SAFE_FREE(selector->baSourceID);
+ SAFE_FREE(selector);
+ }
+}
+
+static struct uvc_vc_selector_unit_descriptor *uvc_parse_vc_selector_unit(uint8_t *buffer) {
+ struct uvc_vc_selector_unit_descriptor *selector = CALLOC(struct uvc_vc_selector_unit_descriptor);
+ if (selector == NULL) {
+ LOGE("uvc_vc_selector_unit_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ selector->bLength = *buffer++;
+ selector->bDescriptorType = *buffer++;
+ selector->bDescriptorSubType = *buffer++;
+ selector->bUnitID = *buffer++;
+ selector->bNrInPins = *buffer++;
+ selector->baSourceID = (uint8_t *) calloc(1, selector->bNrInPins);
+ if (selector->baSourceID == NULL) {
+ LOGE("uvc_vc_selector_unit_descriptor: Out of memory.");
+ uvc_free_vc_selector_unit(selector);
+ return NULL;
+ }
+
+ for (int i = 0; i < selector->bNrInPins; i++) {
+ selector->baSourceID[i] = *buffer++;
+ }
+ selector->iSelector = *buffer;
+
+ return selector;
+}
+
+static void uvc_free_vc_encoding_unit(struct uvc_vc_encoding_unit_descriptor *encoding) {
+ if (encoding) {
+ uvc_free_vc_encoding_unit(encoding->next);
+ SAFE_FREE(encoding->bmControls);
+ SAFE_FREE(encoding->bmControlsRuntime);
+ SAFE_FREE(encoding);
+ }
+}
+
+static struct uvc_vc_encoding_unit_descriptor *uvc_parse_vc_encoding_unit(uint8_t *buffer) {
+ struct uvc_vc_encoding_unit_descriptor *encoding = CALLOC(struct uvc_vc_encoding_unit_descriptor);
+ if (encoding == NULL) {
+ LOGE("uvc_vc_encoding_unit_descriptor: Out of memory.");
+ return NULL;
+ }
+
+ encoding->bLength = *buffer++;
+ encoding->bDescriptorType = *buffer++;
+ encoding->bDescriptorSubType = *buffer++;
+ encoding->bUnitID = *buffer++;
+ encoding->bSourceID = *buffer++;
+ encoding->iEncoding = *buffer++;
+ encoding->bControlSize = *buffer++;
+ encoding->bmControls = (uint8_t *) calloc(1, encoding->bControlSize);
+ if (encoding->bmControls == NULL) {
+ uvc_free_vc_encoding_unit(encoding);
+ return NULL;
+ }
+
+ for (int i = 0; i < encoding->bControlSize; i++) {
+ encoding->bmControls[i] = *buffer++;
+ }
+
+ encoding->bmControlsRuntime = (uint8_t *) calloc(1, encoding->bControlSize);
+ if (encoding->bmControlsRuntime == NULL) {
+ uvc_free_vc_encoding_unit(encoding);
+ return NULL;
+ }
+
+ for (int i = 0; i < encoding->bControlSize; i++) {
+ encoding->bmControlsRuntime[i] = *buffer++;
+ }
+
+ return encoding;
+}
+
+/////////
+
+
+static void uvc_free_vs_header(struct uvc_vs_header_descriptor *header) {
+ if (header) {
+ SAFE_FREE(header->bmaControls);
+ SAFE_FREE(header);
+ }
+}
+
+static struct uvc_vs_header_descriptor *uvc_parse_vs_header(uint8_t *buffer) {
+ struct uvc_vs_header_descriptor *header = CALLOC(struct uvc_vs_header_descriptor);
+ if (header == NULL) {
+ LOGE("uvc_parse_vs_header: Out of memory.");
+ return NULL;
+ }
+
+ header->bLength = *buffer++;
+ header->bDescriptorType = *buffer++;
+ header->bDescriptorSubType = *buffer++;
+ header->bNumFormats = *buffer++;
+ header->wTotalLength = BYTE_TO_SHORT(*buffer);
+ header->bEndpointAddress = *buffer++;
+ header->bmInfo = *buffer++;
+ header->bTerminalLink = *buffer++;
+ header->bStillCaptureMethod = *buffer++;
+ header->bTriggerSupport = *buffer++;
+ header->bTriggerUsage = *buffer++;
+ header->bControlSize = *buffer++;
+ header->bmaControls = (uint8_t *) calloc(1, header->bControlSize);
+ if (header->bmaControls == NULL) {
+ LOGE("uvc_parse_vs_header: Out of memory.");
+ uvc_free_vs_header(header);
+ return NULL;
+ }
+
+ for (int i = 0; i < header->bControlSize; i++) {
+ header->bmaControls[i] = *buffer++;
+ }
+
+ return header;
+}
+
+static void uvc_free_vs_frame_mjpeg(struct uvc_vs_frame_mjpeg_descriptor *frame) {
+ if (frame) {
+ SAFE_FREE(frame->dwFrameInterval);
+ SAFE_FREE(frame);
+ }
+}
+
+static struct uvc_vs_frame_descriptor *uvc_parse_vs_frame_mjpeg(uint8_t *buffer) {
+ struct uvc_vs_frame_mjpeg_descriptor *frame = CALLOC(struct uvc_vs_frame_mjpeg_descriptor);
+ if (frame == NULL) {
+ LOGE("uvc_parse_vs_frame_mjpeg: Out of memory.");
+ return NULL;
+ }
+
+ frame->bLength = *buffer++;
+ frame->bDescriptorType = *buffer++;
+ frame->bDescriptorSubType = *buffer++;
+ frame->bFrameIndex = *buffer++;
+ frame->bmCapabilities = *buffer++;
+ frame->wWidth = BYTE_TO_SHORT(*buffer);
+ frame->wHeight = BYTE_TO_SHORT(*buffer);
+ frame->dwMinBitRate = BYTE_TO_INT(*buffer);
+ frame->dwMaxBitRate = BYTE_TO_INT(*buffer);
+ frame->dwMaxVideoFrameBufferSize = BYTE_TO_INT(*buffer);
+ frame->dwDefaultFrameInterval = BYTE_TO_INT(*buffer);
+ frame->bFrameIntervalType = *buffer++;
+ if (frame->bFrameIntervalType == 0) {
+ frame->dwMinFrameInterval = BYTE_TO_INT(*buffer);
+ frame->dwMaxFrameInterval = BYTE_TO_INT(*buffer);
+ frame->dwFrameIntervalStep = BYTE_TO_INT(*buffer);
+ } else {
+ frame->dwFrameInterval = (uint32_t *) calloc(frame->bFrameIntervalType, sizeof(uint32_t));
+ if (frame->dwFrameInterval == NULL) {
+ LOGE("uvc_parse_vs_frame_mjpeg: Out of memory.");
+ uvc_free_vs_frame_mjpeg(frame);
+ return NULL;
+ }
+ for (int i = 0; i < frame->bFrameIntervalType; i++) {
+ frame->dwFrameInterval[i] = BYTE_TO_INT(*buffer);
+ }
+ }
+
+ return (struct uvc_vs_frame_descriptor *) frame;
+}
+
+static struct uvc_vs_format_descriptor *uvc_parse_vs_format_mjpeg(uint8_t *buffer) {
+ struct uvc_vs_format_mjpeg_descriptor *format = CALLOC(struct uvc_vs_format_mjpeg_descriptor);
+ if (format == NULL) {
+ LOGE("uvc_parse_vs_format_mjpeg: Out of memory.");
+ return NULL;
+ }
+
+ format->bLength = *buffer++;
+ format->bDescriptorType = *buffer++;
+ format->bDescriptorSubType = *buffer++;
+ format->bFormatIndex = *buffer++;
+ format->bNumFrameDescriptors = *buffer++;
+ format->bmFlags = *buffer++;
+ format->bDefaultFrameIndex = *buffer++;
+ format->bAspectRatioX = *buffer++;
+ format->bAspectRatioY = *buffer++;
+ format->bmInterlaceFlags = *buffer++;
+ format->bCopyProtect = *buffer;
+
+ return (struct uvc_vs_format_descriptor *) format;
+}
+
+
+static void uvc_free_vs_frame_uncompressed(struct uvc_vs_frame_uncompressed_descriptor *frame) {
+ if (frame) {
+ SAFE_FREE(frame->dwFrameInterval);
+ SAFE_FREE(frame);
+ }
+}
+
+static struct uvc_vs_frame_descriptor *uvc_vs_frame_uncompressed(uint8_t *buffer) {
+ struct uvc_vs_frame_uncompressed_descriptor *frame = CALLOC(struct uvc_vs_frame_uncompressed_descriptor);
+ if (frame == NULL) {
+ LOGE("uvc_vs_frame_uncompressed: Out of memory.");
+ return NULL;
+ }
+
+ frame->bLength = *buffer++;
+ frame->bDescriptorType = *buffer++;
+ frame->bDescriptorSubType = *buffer++;
+ frame->bFrameIndex = *buffer++;
+ frame->bmCapabilities = *buffer++;
+ frame->wWidth = BYTE_TO_SHORT(*buffer);
+ frame->wHeight = BYTE_TO_SHORT(*buffer);
+ frame->dwMinBitRate = BYTE_TO_INT(*buffer);
+ frame->dwMaxBitRate = BYTE_TO_INT(*buffer);
+ frame->dwMaxVideoFrameBufferSize = BYTE_TO_INT(*buffer);
+ frame->dwDefaultFrameInterval = BYTE_TO_INT(*buffer);
+ frame->bFrameIntervalType = *buffer++;
+ if (frame->bFrameIntervalType == 0) {
+ frame->dwMinFrameInterval = BYTE_TO_INT(*buffer);
+ frame->dwMaxFrameInterval = BYTE_TO_INT(*buffer);
+ frame->dwFrameIntervalStep = BYTE_TO_INT(*buffer);
+ } else {
+ frame->dwFrameInterval = (uint32_t *) calloc(frame->bFrameIntervalType, sizeof(uint32_t));
+ if (frame->dwFrameInterval == NULL) {
+ uvc_free_vs_frame_uncompressed(frame);
+ return NULL;
+ }
+ for (int i = 0; i < frame->bFrameIntervalType; i++) {
+ frame->dwFrameInterval[i] = BYTE_TO_INT(*buffer);
+ }
+ }
+
+ return (struct uvc_vs_frame_descriptor *) frame;
+}
+
+static struct uvc_vs_format_descriptor *uvc_vs_format_uncompressed(uint8_t *buffer) {
+ struct uvc_vs_format_uncompressed_descriptor *format = CALLOC(struct uvc_vs_format_uncompressed_descriptor);
+ if (format == NULL) {
+ LOGE("uvc_vs_format_uncompressed: Out of memory.");
+ return NULL;
+ }
+
+ format->bLength = *buffer++;
+ format->bDescriptorType = *buffer++;
+ format->bDescriptorSubType = *buffer++;
+ format->bFormatIndex = *buffer++;
+ format->bNumFrameDescriptors = *buffer++;
+ for (int i = 0; i < 16; i++) {
+ format->guidFormat[i] = *buffer++;
+ }
+ format->bBitsPerPixel = *buffer++;
+ format->bDefaultFrameIndex = *buffer++;
+ format->bAspectRatioX = *buffer++;
+ format->bAspectRatioY = *buffer++;
+ format->bmInterlaceFlags = *buffer++;
+ format->bCopyProtect = *buffer;
+
+ return (struct uvc_vs_format_descriptor *) format;
+}
+
+
+static struct uvc_vs_format_descriptor *uvc_parse_vs_format_h264(uint8_t *buffer) {
+ struct uvc_vs_format_h264_descriptor *format = CALLOC(struct uvc_vs_format_h264_descriptor);
+ if (format == NULL) {
+ LOGE("uvc_parse_vs_format_h264: Out of memory.");
+ return NULL;
+ }
+
+ format->bLength = *buffer++;
+ format->bDescriptorType = *buffer++;
+ format->bDescriptorSubType = *buffer++;
+ format->bFormatIndex = *buffer++;
+ format->bNumFrameDescriptors = *buffer++;
+ format->bDefaultFrameIndex = *buffer++;
+ format->bMaxCodecConfigDelay = *buffer++;
+ format->bmSupportedSliceModes = *buffer++;
+ format->bmSupportedSyncFrameTypes = *buffer++;
+ format->bResuolutionScaling = *buffer++;
+ format->Reserved1 = *buffer++;
+ format->bmSupportedRateControlModes = *buffer++;
+ format->wMaxMBperSecOneResolutionNoScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecTwoResolutionsNoScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecThreeResolutionsNoScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecFourResolutionsNoScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecOneResolutionTemporalScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecTwoResolutionsTemporalScalablility = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecThreeResolutionsTemporalScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecFourResolutionsTemporalScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecOneResolutionTemporalQualityScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecTwoResolutionsTemporalQualityScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecThreeResolutionsTemporalQualityScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecFourResolutionsTemporalQualityScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecOneResolutionsTemporalSpatialScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecTwoResolutionsTemporalSpatialScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecThreeResolutionsTemporalSpatialScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecFourResolutionsTemporalSpatialScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecOneResolutionFullScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecTwoResolutionsFullScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecThreeResolutionsFullScalability = BYTE_TO_SHORT(*buffer);
+ format->wMaxMBperSecFourResolutionsFullScalability = BYTE_TO_SHORT(*buffer);
+
+ return (struct uvc_vs_format_descriptor *) format;
+}
+
+static void uvc_free_vs_frame_h264(struct uvc_vs_frame_h264_descriptor *frame) {
+ if (frame) {
+ SAFE_FREE(frame->dwFrameInterval);
+ SAFE_FREE(frame);
+ }
+}
+
+static struct uvc_vs_frame_descriptor *uvc_parse_vs_frame_h264(uint8_t *buffer) {
+ struct uvc_vs_frame_h264_descriptor *frame = CALLOC(struct uvc_vs_frame_h264_descriptor);
+ if (frame == NULL) {
+ LOGE("uvc_parse_vs_frame_h264: Out of memory.");
+ return NULL;
+ }
+
+ frame->bLength = *buffer++;
+ frame->bDescriptorType = *buffer++;
+ frame->bDescriptorSubType = *buffer++;
+ frame->bFrameIndex = *buffer++;
+ frame->wWidth = BYTE_TO_SHORT(*buffer);
+ frame->wHeight = BYTE_TO_SHORT(*buffer);
+ frame->wSARwidth = BYTE_TO_SHORT(*buffer);
+ frame->wSARheight = BYTE_TO_SHORT(*buffer);
+ frame->wProfie = BYTE_TO_SHORT(*buffer);
+ frame->bLevelIDC = *buffer++;
+ frame->wConstrainedToolset = BYTE_TO_SHORT(*buffer);
+ frame->bmSupportedUsages = BYTE_TO_INT(*buffer);
+ frame->bmCapabilities = BYTE_TO_SHORT(*buffer);
+ frame->bmSVCCapabilities = BYTE_TO_INT(*buffer);
+ frame->bmMVCCapabilities = BYTE_TO_INT(*buffer);
+ frame->dwMinBitRate = BYTE_TO_INT(*buffer);
+ frame->dwMaxBitRate = BYTE_TO_INT(*buffer);
+ frame->dwDefaultFrameInterval = BYTE_TO_INT(*buffer);
+ frame->bNumFrameIntervals = *buffer++;
+ frame->dwFrameInterval = (uint32_t *) calloc(frame->bNumFrameIntervals, sizeof(uint32_t));
+ if (frame->dwFrameInterval == NULL) {
+ LOGE("uvc_parse_vs_frame_h264: Out of memory.");
+ uvc_free_vs_frame_h264(frame);
+ return NULL;
+ }
+
+ for (int i = 0; i < frame->bNumFrameIntervals; i++) {
+ frame->dwFrameInterval[i] = BYTE_TO_INT(*buffer);
+ }
+
+ return (struct uvc_vs_frame_descriptor *) frame;
+}
+
+static void uvc_free_vs_frame(struct uvc_vs_frame_descriptor *frame) {
+ if (frame) {
+ uvc_free_vs_frame(frame->next);
+
+ // frame の中で個別にメモリを確保している場合があるので、処理を分ける
+ switch (frame->bDescriptorSubType) {
+ default:
+ SAFE_FREE(frame);
+ break;
+
+ case VS_FRAME_UNCOMPRESSED:
+ uvc_free_vs_frame_uncompressed((struct uvc_vs_frame_uncompressed_descriptor *) frame);
+ break;
+
+ case VS_FRAME_MJPEG:
+ uvc_free_vs_frame_mjpeg((struct uvc_vs_frame_mjpeg_descriptor *) frame);
+ break;
+
+ case VS_FRAME_H264:
+ uvc_free_vs_frame_h264((struct uvc_vs_frame_h264_descriptor *) frame);
+ break;
+ }
+ }
+}
+
+static void uvc_free_vs_format(struct uvc_vs_format_descriptor *format) {
+ if (format) {
+ uvc_free_vs_format(format->next);
+ uvc_free_vs_frame(format->frame);
+ SAFE_FREE(format);
+ }
+}
+
+static void uvc_free_vs_still_image_frame(struct uvc_vs_still_image_frame_descriptor *frame_still) {
+ if (frame_still) {
+ SAFE_FREE(frame_still->wWidth);
+ SAFE_FREE(frame_still->wHeight);
+ SAFE_FREE(frame_still->bCompression);
+ SAFE_FREE(frame_still);
+ }
+}
+
+static struct uvc_vs_still_image_frame_descriptor *uvc_parse_vs_still_image_frame(uint8_t *buffer) {
+ struct uvc_vs_still_image_frame_descriptor *frame_still = CALLOC(struct uvc_vs_still_image_frame_descriptor);
+ if (frame_still == NULL) {
+ LOGE("uvc_parse_vs_still_image_frame: Out of memory.");
+ return NULL;
+ }
+
+ frame_still->bLength = *buffer++;
+ frame_still->bDescriptorType = *buffer++;
+ frame_still->bDescriptorSubType = *buffer++;
+ frame_still->bEndpointAddress = *buffer++;
+ frame_still->bNumImageSizePatterns = *buffer++;
+
+ size_t size = (size_t) 2 * frame_still->bNumImageSizePatterns;
+ frame_still->wWidth = (uint16_t *) calloc(1, size);
+ if (frame_still->wWidth == NULL) {
+ LOGE("uvc_parse_vs_still_image_frame: Out of memory.");
+ uvc_free_vs_still_image_frame(frame_still);
+ return NULL;
+ }
+
+ frame_still->wHeight = (uint16_t *) calloc(1, size);
+ if (frame_still->wHeight == NULL) {
+ LOGE("uvc_parse_vs_still_image_frame: Out of memory.");
+ uvc_free_vs_still_image_frame(frame_still);
+ return NULL;
+ }
+
+ for (int i = 0; i < frame_still->bNumImageSizePatterns; i++) {
+ frame_still->wWidth[i] = BYTE_TO_SHORT(*buffer);
+ frame_still->wHeight[i] = BYTE_TO_SHORT(*buffer);
+ }
+ frame_still->bNumCompressionPtn = *buffer++;
+ frame_still->bCompression = (uint8_t *) calloc(1, frame_still->bNumCompressionPtn);
+ if (frame_still->bCompression == NULL) {
+ LOGE("uvc_parse_vs_still_image_frame: Out of memory.");
+ uvc_free_vs_still_image_frame(frame_still);
+ return NULL;
+ }
+
+ for (int i = 0; i < frame_still->bNumCompressionPtn; i++) {
+ frame_still->bCompression[i] = *buffer;
+ }
+
+ return frame_still;
+}
+
+static struct uvc_vs_color_matching_descriptor *uvc_parse_vs_color_matching(uint8_t *buffer) {
+ struct uvc_vs_color_matching_descriptor *color = CALLOC(struct uvc_vs_color_matching_descriptor);
+ if (color == NULL) {
+ LOGE("uvc_parse_vs_color_matching: Out of memory.");
+ return NULL;
+ }
+
+ color->bLength = *buffer++;
+ color->bDescriptorType = *buffer++;
+ color->bDescriptorSubType = *buffer++;
+ color->bColorPrimaries = *buffer++;
+ color->bTransferCharacteristics = *buffer++;
+ color->bMatrixCoefficients = *buffer;
+
+ return color;
+}
+
+static void uvc_free_vs_color_matching(struct uvc_vs_color_matching_descriptor *color) {
+ if (color) {
+ uvc_free_vs_color_matching(color->next);
+ SAFE_FREE(color);
+ }
+}
+
+static uint32_t uvc_parse_vc_descriptor(struct uvc_video_control_interface *control, uint8_t *buffer, int32_t length) {
+ uint32_t skipLength = 0;
+ while (length > 0) {
+ uint8_t bLength = *buffer;
+ uint8_t bDescriptorType = *(buffer + 1);
+
+ switch (bDescriptorType) {
+ case INTERFACE:
+ // 次の Interface がきたので終了
+ return skipLength;
+
+ case ENDPOINT:
+ control->endpoint = uvc_parse_endpoint(buffer);
+ break;
+
+ case CS_ENDPOINT:
+ control->interrupt_endpoint = uvc_parse_interrupt_endpoint(buffer);
+ break;
+
+ case CS_INTERFACE: {
+ uint8_t bDescriptorSubtype = *(buffer + 2);
+ switch (bDescriptorSubtype) {
+ case VC_HEADER:
+ control->header = uvc_parse_vc_header(buffer);
+ break;
+
+ case VC_INPUT_TERMINAL:
+ uvc_add_vc_input_terminal(control, uvc_parse_vc_input_terminal(buffer));
+ break;
+
+ case VC_OUTPUT_TERMINAL:
+ uvc_add_vc_output_terminal(control, uvc_parse_vc_output_terminal(buffer));
+ break;
+
+ case VC_SELECTOR_UNIT:
+ uvc_add_vc_selector_unit(control, uvc_parse_vc_selector_unit(buffer));
+ break;
+
+ case VC_PROCESSING_UNIT:
+ uvc_add_vc_processing_unit(control, uvc_parse_vc_processing_unit(buffer));
+ break;
+
+ case VC_EXTENSION_UNIT:
+ uvc_add_vc_extension_unit(control, uvc_parse_vc_extension_unit(buffer));
+ break;
+
+ case VC_ENCODING_UNIT:
+ uvc_add_vc_encoding_unit(control, uvc_parse_vc_encoding_unit(buffer));
+ break;
+
+ default:
+ LOGW("UNKNOWN CS INTERFACE (VC). %02X", bDescriptorSubtype);
+ break;
+ }
+ } break;
+
+ default:
+ LOGW("UNKNOWN DESCRIPTOR (VC). %02X", bDescriptorType);
+ break;
+ }
+
+ buffer += bLength;
+ skipLength += bLength;
+ length -= bLength;
+ }
+
+ return skipLength;
+}
+
+
+static void uvc_free_video_control(struct uvc_video_control_interface *control_interface) {
+ if (control_interface) {
+ SAFE_FREE(control_interface->interface);
+
+ uvc_free_vc_header(control_interface->header);
+ uvc_free_vc_input_terminal(control_interface->input_terminal);
+ uvc_free_vc_output_terminal(control_interface->output_terminal);
+ uvc_free_vc_selector_unit(control_interface->selector);
+ uvc_free_vc_processing_unit(control_interface->processing);
+ uvc_free_vc_extension_unit(control_interface->extension);
+ uvc_free_vc_encoding_unit(control_interface->encoding);
+
+ SAFE_FREE(control_interface->endpoint);
+ SAFE_FREE(control_interface->interrupt_endpoint);
+ SAFE_FREE(control_interface);
+ }
+}
+
+
+static uint32_t uvc_parse_vs_descriptor(struct uvc_video_streaming_interface *streaming, uint8_t *buffer, int32_t length) {
+ uint32_t skipLength = 0;
+ while (length > 0) {
+ uint8_t bLength = *buffer;
+ uint8_t bDescriptorType = *(buffer + 1);
+
+ switch (bDescriptorType) {
+ case INTERFACE:
+ // 次の Interface がきたので終了
+ return skipLength;
+
+ case ENDPOINT:
+ streaming->still_endpoint = uvc_parse_endpoint(buffer);
+ break;
+
+ case CS_INTERFACE: {
+ uint8_t bDescriptorSubtype = *(buffer + 2);
+ switch (bDescriptorSubtype) {
+ case VS_INPUT_HEADER:
+ streaming->header = uvc_parse_vs_header(buffer);
+ break;
+
+ case VS_OUTPUT_HEADER:
+ LOGW("VS_OUTPUT_HEADER is not implements yet.");
+ break;
+
+ case VS_FORMAT_MJPEG: {
+ struct uvc_vs_format_descriptor *format = uvc_parse_vs_format_mjpeg(buffer);
+ if (format) {
+ format->streaming_interface = streaming;
+ uvc_add_vs_format(streaming, format);
+ }
+ } break;
+
+ case VS_FRAME_MJPEG:
+ uvc_add_vs_frame(uvc_get_last_format(streaming), uvc_parse_vs_frame_mjpeg(buffer));
+ break;
+
+ case VS_FORMAT_UNCOMPRESSED: {
+ struct uvc_vs_format_descriptor *format = uvc_vs_format_uncompressed(buffer);
+ if (format) {
+ format->streaming_interface = streaming;
+ uvc_add_vs_format(streaming, format);
+ }
+ } break;
+
+ case VS_FRAME_UNCOMPRESSED:
+ uvc_add_vs_frame(uvc_get_last_format(streaming), uvc_vs_frame_uncompressed(buffer));
+ break;
+
+ case VS_FORMAT_H264: {
+ struct uvc_vs_format_descriptor *format = uvc_parse_vs_format_h264(buffer);
+ if (format) {
+ format->streaming_interface = streaming;
+ uvc_add_vs_format(streaming, format);
+ }
+ } break;
+
+ case VS_FRAME_H264:
+ uvc_add_vs_frame(uvc_get_last_format(streaming), uvc_parse_vs_frame_h264(buffer));
+ break;
+
+ case VS_FORMAT_MPEG2TS:
+ LOGW("VS_FORMAT_MPEG2TS is not implements yet.");
+ break;
+
+ case VS_FORMAT_VP8:
+ LOGW("VS_FORMAT_VP8 is not implements yet.");
+ break;
+
+ case VS_FRAME_VP8:
+ LOGW("VS_FRAME_VP8 is not implements yet.");
+ break;
+
+ case VS_STILL_IMAGE_FRAME:
+ streaming->frame_still = uvc_parse_vs_still_image_frame(buffer);
+ break;
+
+ case VS_COLORFORMAT:
+ uvc_add_vs_color_matching(streaming, uvc_parse_vs_color_matching(buffer));
+ break;
+
+ default:
+ LOGW("UNKNOWN CS_INTERFACE (VS). %02X", bDescriptorSubtype);
+ break;
+ }
+ } break;
+
+ default:
+ LOGW("UNKNOWN DESCRIPTOR (VS). %02X", bDescriptorType);
+ break;
+ }
+
+ buffer += bLength;
+ skipLength += bLength;
+ length -= bLength;
+ }
+ return skipLength;
+}
+
+
+static void uvc_free_altsetting_descriptor(struct uvc_video_streaming_altsetting *altsetting) {
+ if (altsetting) {
+ uvc_free_altsetting_descriptor(altsetting->next);
+ SAFE_FREE(altsetting->interface);
+ SAFE_FREE(altsetting->video_endpoint);
+ SAFE_FREE(altsetting->still_endpoint);
+ SAFE_FREE(altsetting);
+ }
+}
+
+static uint32_t uvc_parse_altsetting_descriptor(struct uvc_video_streaming_altsetting *altsetting, uint8_t *buffer, int32_t length) {
+ uint32_t skipLength = 0;
+ while (length > 0) {
+ uint8_t bLength = *buffer;
+ uint8_t bDescriptorType = *(buffer + 1);
+
+ switch (bDescriptorType) {
+ case INTERFACE:
+ // 次の Interface がきたので終了
+ return skipLength;
+
+ case ENDPOINT: {
+ struct uvc_endpoint_descriptor *endpoint = uvc_parse_endpoint(buffer);
+ if (endpoint == NULL) {
+ return skipLength;
+ } else if (altsetting->video_endpoint == NULL) {
+ altsetting->video_endpoint = endpoint;
+ } else if (altsetting->still_endpoint == NULL) {
+ altsetting->still_endpoint = endpoint;
+ } else {
+ SAFE_FREE(endpoint);
+ }
+ } break;
+
+ default:
+ LOGW("UNKNOWN DESCRIPTOR (ALTSETTING). %02X", bDescriptorType);
+ break;
+ }
+
+ buffer += bLength;
+ skipLength += bLength;
+ length -= bLength;
+ }
+ return skipLength;
+}
+
+
+
+static void uvc_free_video_streaming(struct uvc_video_streaming_interface *streaming_interface) {
+ if (streaming_interface) {
+ uvc_free_video_streaming(streaming_interface->next);
+
+ SAFE_FREE(streaming_interface->interface);
+
+ uvc_free_vs_header(streaming_interface->header);
+ uvc_free_vs_format(streaming_interface->format);
+ uvc_free_vs_still_image_frame(streaming_interface->frame_still);
+ uvc_free_vs_color_matching(streaming_interface->color_matching);
+
+ SAFE_FREE(streaming_interface->still_endpoint);
+ SAFE_FREE(streaming_interface);
+ }
+}
+
+
+static uint32_t uvc_parse_descriptor_configuration(struct uvc_configuration_descriptor *configuration, uint8_t *buffer, int32_t length) {
+ uint32_t skipLength = 0;
+ while (length > 0) {
+ uint32_t bLength = *buffer;
+ uint8_t bDescriptorType = *(buffer + 1);
+
+ switch (bDescriptorType) {
+ case CONFIGURATION:
+ // 次の Configuration がきたので終了
+ return skipLength;
+
+ case INTERFACE: {
+ struct uvc_interface_descriptor *interface = uvc_parse_interface_descriptor(buffer);
+ if (interface == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ } else if (interface->bInterfaceClass == CC_VIDEO) {
+ switch (interface->bInterfaceSubClass) {
+ case SC_VIDEOCONTROL: {
+ // INTERFACE の分だけ移動しておく
+ buffer += bLength;
+ skipLength += bLength;
+ length -= bLength;
+
+ configuration->control_interface = CALLOC(struct uvc_video_control_interface);
+ if (configuration->control_interface == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+
+ configuration->control_interface->interface = interface;
+ bLength = uvc_parse_vc_descriptor(configuration->control_interface, buffer, length);
+ } break;
+
+ case SC_VIDEOSTREAMING: {
+ // INTERFACE の分だけ移動しておく
+ buffer += bLength;
+ skipLength += bLength;
+ length -= bLength;
+
+ if (interface->bAlternateSetting == 0) {
+ struct uvc_video_streaming_interface *streaming_interface = CALLOC(struct uvc_video_streaming_interface);
+ if (streaming_interface == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+
+ streaming_interface->interface = interface;
+ bLength = uvc_parse_vs_descriptor(streaming_interface, buffer, length);
+ uvc_add_video_streaming_interface(configuration, streaming_interface);
+ } else {
+ struct uvc_video_streaming_altsetting *altsetting = CALLOC(struct uvc_video_streaming_altsetting);
+ if (altsetting == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+
+ altsetting->interface = interface;
+ bLength = uvc_parse_altsetting_descriptor(altsetting, buffer, length);
+ uvc_add_uvc_streaming_altsetting(configuration, altsetting);
+ }
+ } break;
+
+ case SC_VIDEO_INTERFACE_COLLECTION:
+ LOGW("SC_VIDEO_INTERFACE_COLLECTION is not implements yet.");
+ SAFE_FREE(interface);
+ break;
+
+ default:
+ LOGW("UNKNOWN bInterfaceSubClass: %02X", interface->bInterfaceSubClass);
+ SAFE_FREE(interface);
+ break;
+ }
+ } else {
+ LOGW("UNKNOWN bInterfaceClass. %02X", interface->bInterfaceClass);
+ SAFE_FREE(interface);
+ }
+ } break;
+
+
+ case STRING: {
+ LOGW("STRING DESCRIPTOR.");
+ } break;
+
+ case ENDPOINT:
+ LOGW("ENDPOINT.");
+ break;
+
+ case CS_INTERFACE:
+ LOGW("CS_INTERFACE. sub:%02X", *(buffer + 2));
+ break;
+
+ default:
+ LOGW("UNKNOWN DESCRIPTOR (CONFIG). %02X", bDescriptorType);
+ break;
+ }
+
+ buffer += bLength;
+ skipLength += bLength;
+ length -= bLength;
+ }
+ return skipLength;
+}
+
+
+static void uvc_free_configuration_descriptor(struct uvc_configuration_descriptor *configuration) {
+ if (configuration) {
+ uvc_free_configuration_descriptor(configuration->next);
+
+ uvc_free_video_control(configuration->control_interface);
+ uvc_free_video_streaming(configuration->streaming_interface);
+ uvc_free_altsetting_descriptor(configuration->altsetting);
+
+ SAFE_FREE(configuration);
+ }
+}
+
+
+////////////////////// public //////////////////////////
+
+
+struct uvc_configuration_descriptor *uvc_get_configuration_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id) {
+ if (descriptor == NULL || descriptor->configuration == NULL) {
+ return NULL;
+ }
+
+ struct uvc_configuration_descriptor *config = descriptor->configuration;
+ while (config) {
+ if (config->bConfigurationValue == config_id) {
+ return config;
+ }
+ config = config->next;
+ }
+ return NULL;
+}
+
+
+uvc_result uvc_parse_descriptor(struct uvc_descriptor *descriptor, uint8_t *buffer, int32_t length) {
+ if (descriptor == NULL) {
+ LOGE("descriptor is NULL.");
+ return UVC_ERROR;
+ }
+
+ if (buffer == NULL) {
+ LOGE("buffer is NULL.");
+ return UVC_ERROR;
+ }
+
+ while (length > 0) {
+ uint32_t bLength = *buffer;
+ uint8_t bDescriptorType = *(buffer + 1);
+
+ switch (bDescriptorType) {
+ case DEVICE:
+ descriptor->device = uvc_parse_device_descriptor(buffer);
+ if (descriptor->device == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+ break;
+
+ case CONFIGURATION: {
+ struct uvc_configuration_descriptor *configuration = uvc_parse_configuration_descriptor(buffer);
+ if (descriptor->device == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+
+ // CONFIGURATION の分だけ移動しておく
+ buffer += bLength;
+ length -= bLength;
+
+ bLength = uvc_parse_descriptor_configuration(configuration, buffer, length);
+ uvc_add_configuration(descriptor, configuration);
+ } break;
+
+ case INTERFACE_ASSOCIATION:
+ descriptor->interface_association = uvc_parse_iad(buffer);
+ if (descriptor->interface_association == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+ break;
+
+ case STRING: {
+ LOGD("STRING DESCRIPTOR.");
+ } break;
+
+ case ENDPOINT:
+ LOGD("ENDPOINT.");
+ break;
+
+ case CS_INTERFACE:
+ LOGD("CS_INTERFACE. sub:%02X", *(buffer + 2));
+ break;
+
+ default:
+ LOGD("UNKNOWN DESCRIPTOR. %02X", bDescriptorType);
+ break;
+ }
+ buffer += bLength;
+ length -= bLength;
+ }
+
+ return (descriptor->configuration &&
+ descriptor->configuration->control_interface &&
+ descriptor->configuration->streaming_interface) ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+void uvc_dispose_descriptor(struct uvc_descriptor *descriptor) {
+ if (descriptor == NULL) {
+ return;
+ }
+
+ SAFE_FREE(descriptor->device);
+ SAFE_FREE(descriptor->interface_association);
+
+ uvc_free_configuration_descriptor(descriptor->configuration);
+}
+
+
+struct uvc_vs_format_descriptor *uvc_find_format_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id, uint8_t format_index) {
+ if (descriptor == NULL) {
+ return NULL;
+ }
+
+ struct uvc_configuration_descriptor *config = uvc_get_configuration_descriptor(descriptor,
+ config_id);
+ if (config) {
+ struct uvc_video_streaming_interface *streaming_interface = config->streaming_interface;
+ while (streaming_interface) {
+ struct uvc_vs_format_descriptor *format = streaming_interface->format;
+ while (format) {
+ if (format->bFormatIndex == format_index) {
+ return format;
+ }
+ format = format->next;
+ }
+ streaming_interface = streaming_interface->next;
+ }
+ }
+ return NULL;
+}
+
+
+struct uvc_vs_frame_descriptor *uvc_find_frame_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id, uint8_t format_index, uint8_t frame_index) {
+ if (descriptor == NULL) {
+ return NULL;
+ }
+
+ struct uvc_vs_format_descriptor *format = uvc_find_format_descriptor(descriptor, config_id, format_index);
+ if (format == NULL) {
+ return NULL;
+ }
+
+ struct uvc_vs_frame_descriptor *frame = format->frame;
+ while (frame) {
+ if (frame->bFrameIndex == frame_index) {
+ return frame;
+ }
+ frame = frame->next;
+ }
+ return NULL;
+}
+
+
+uint16_t uvc_get_uvc_version(struct uvc_descriptor *descriptor, uint8_t config_id) {
+ if (descriptor == NULL) {
+ return 0;
+ }
+
+ struct uvc_configuration_descriptor *config = uvc_get_configuration_descriptor(descriptor,
+ config_id);
+ if (config && config->control_interface && config->control_interface->header) {
+ return config->control_interface->header->bcdUVC;
+ }
+ return 0;
+}
+
+
+struct uvc_video_control_interface *uvc_get_video_control_interface(struct uvc_descriptor *descriptor, uint8_t config_id) {
+ struct uvc_configuration_descriptor *config = uvc_get_configuration_descriptor(descriptor, config_id);
+ return config ? config->control_interface : NULL;
+}
+
+
+struct uvc_video_streaming_interface *uvc_get_video_streaming_interface(struct uvc_descriptor *descriptor, uint8_t config_id) {
+ struct uvc_configuration_descriptor *config = uvc_get_configuration_descriptor(descriptor, config_id);
+ return config ? config->streaming_interface : NULL;
+}
+
+
+struct uvc_video_streaming_altsetting *uvc_get_video_streaming_altsetting(struct uvc_descriptor *descriptor, uint8_t config_id) {
+ struct uvc_configuration_descriptor *config = uvc_get_configuration_descriptor(descriptor, config_id);
+ return config ? config->altsetting : NULL;
+}
+
+
+enum uvc_uncompressed_format uvc_get_uncompressed_format(struct uvc_vs_format_descriptor *format) {
+ if (format->bDescriptorSubType != VS_FORMAT_UNCOMPRESSED) {
+ return UNKNOWN;
+ }
+
+ struct uvc_vs_format_uncompressed_descriptor *uncompressed = (struct uvc_vs_format_uncompressed_descriptor *) format;
+
+ if (memcmp(uncompressed->guidFormat, GUID_YUY2_UNCOMPRESSED, 16) == 0) {
+ return YUY2;
+ } else if (memcmp(uncompressed->guidFormat, GUID_NV12_UNCOMPRESSED, 16) == 0) {
+ return NV12;
+ } else if (memcmp(uncompressed->guidFormat, GUID_M420_UNCOMPRESSED, 16) == 0) {
+ return M420;
+ } else if (memcmp(uncompressed->guidFormat, GUID_I420_UNCOMPRESSED, 16) == 0) {
+ return I420;
+ }
+ return UNKNOWN;
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-descriptor.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-descriptor.h
new file mode 100644
index 0000000000..5662b18e2c
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-descriptor.h
@@ -0,0 +1,865 @@
+/*
+ uvc-descriptor.h
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#ifndef UVC_DESCRIPTOR_H
+#define UVC_DESCRIPTOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include "common.h"
+
+#include
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include
+#else
+#include
+#endif
+
+
+/**
+ * Video Interface Class Code.
+ */
+enum uvc_interface_class {
+ CC_VIDEO = 0x0E
+};
+
+/**
+ * Video Interface Subclass Codes.
+ */
+enum uvc_interface_subclass {
+ SC_UNDEFINED = 0x00,
+ SC_VIDEOCONTROL = 0x01,
+ SC_VIDEOSTREAMING = 0x02,
+ SC_VIDEO_INTERFACE_COLLECTION = 0x03
+};
+
+/**
+ * Descriptor Types.
+ */
+enum uvc_descriptor_type {
+ DEVICE = 0x01,
+ CONFIGURATION = 0x02,
+ STRING = 0x03,
+ INTERFACE = 0x04,
+ ENDPOINT = 0x05,
+ DEVICE_QUALIFIER = 0x06,
+ OTHER_SPEED_CONFIGURATION = 0x07,
+ INTERFACE_POWER = 0x08,
+
+ // USB 2.0
+ OTG = 0x09,
+ INTERFACE_ASSOCIATION = 0x0B,
+
+ // UVC
+ CS_UNDEFINED = 0x20,
+ CS_DEVICE = 0x21,
+ CS_CONFIGURATION = 0x22,
+ CS_STRING = 0x23,
+ CS_INTERFACE = 0x24,
+ CS_ENDPOINT = 0x25
+};
+
+/**
+ * Video Class-Specific VC Interface Descriptor Subtypes.
+ */
+enum uvc_vc_descriptor_subtype {
+ VC_DESCRIPTOR_UNDEFINED = 0x00,
+ VC_HEADER = 0x01,
+ VC_INPUT_TERMINAL = 0x02,
+ VC_OUTPUT_TERMINAL = 0x03,
+ VC_SELECTOR_UNIT = 0x04,
+ VC_PROCESSING_UNIT = 0x05,
+ VC_EXTENSION_UNIT = 0x06,
+ VC_ENCODING_UNIT = 0x07
+};
+
+
+/**
+ * Video Class-Specific VS Interface Descriptor Subtypes.
+ */
+enum uvc_vs_descriptor_subtype {
+ VS_UNDEFINED = 0x00,
+ VS_INPUT_HEADER = 0x01,
+ VS_OUTPUT_HEADER = 0x02,
+ VS_STILL_IMAGE_FRAME = 0x03,
+ VS_FORMAT_UNCOMPRESSED = 0x04,
+ VS_FRAME_UNCOMPRESSED = 0x05,
+ VS_FORMAT_MJPEG = 0x06,
+ VS_FRAME_MJPEG = 0x07,
+ VS_FORMAT_MPEG2TS = 0x0A,
+ VS_FORMAT_DV = 0x0C,
+ VS_COLORFORMAT = 0x0D,
+ VS_FORMAT_FRAME_BASED = 0x10,
+ VS_FRAME_FRAME_BASED = 0x11,
+ VS_FORMAT_STREAM_BASED = 0x12,
+ VS_FORMAT_H264 = 0x13,
+ VS_FRAME_H264 = 0x14,
+ VS_FORMAT_H264_SIMULCAST = 0x15,
+ VS_FORMAT_VP8 = 0x16,
+ VS_FRAME_VP8 = 0x17,
+ VS_FORMAT_VP8_SIMULCAST = 0x18
+};
+
+/**
+ * Video Class-Specific Endpoint Descriptor Subtypes.
+ */
+enum uvc_ep_descriptor_subtype {
+ EP_UNDEFINED = 0x00,
+ EP_GENERAL = 0x01,
+ EP_ENDPOINT = 0x02,
+ EP_INTERRUPT = 0x03
+};
+
+
+/**
+ * Input Terminal Types.
+ */
+enum uvc_input_terminal_types {
+ ITT_VENDOR_SPECIFIC = 0x0200,
+ ITT_CAMERA = 0x0201,
+ ITT_MEDIA_TRANSPORT_INPUT = 0x0202
+};
+
+
+/**
+ * Camera Terminal Controls.
+ */
+enum uvc_camera_terminal_control {
+ SCANNING_MODE = 1,
+ AUTO_EXPOSURE_MODE = (1 << 1),
+ AUTO_EXPOSURE_PRIORITY = (1 << 2),
+ EXPOSURE_TIME_ABSOLUTE = (1 << 3),
+ EXPOSURE_TIME_RELATIVE = (1 << 4),
+ FOCUS_ABSOLUTE = (1 << 5),
+ FOCUS_RELATIVE = (1 << 6),
+ IRIS_ABSOLUTE = (1 << 7),
+ IRIS_RELATIVE = (1 << 8),
+ ZOOM_ABSOLUTE = (1 << 9),
+ ZOOM_RELATIVE = (1 << 10),
+ PANTILT_ABSOLUTE = (1 << 11),
+ PANTILT_RELATIVE = (1 << 12),
+ ROLL_ABSOLUTE = (1 << 13),
+ ROLL_RELATIVE = (1 << 14),
+ RESERVED_1 = (1 << 15),
+ RESERVED_2 = (1 << 16),
+ FOCUS_AUTO = (1 << 17),
+ PRIVACY = (1 << 18)
+};
+
+
+/**
+ * Processing Unit Controls.
+ */
+enum uvc_processing_control {
+ BRIGHTNESS = 1,
+ CONTRAST = (1 << 1),
+ HUE = (1 << 2),
+ SATURATION = (1 << 3),
+ SHARPNESS = (1 << 4),
+ GAMMA = (1 << 5),
+ WHITE_BALANCE_TEMPERATURE = (1 << 6),
+ WHITE_BALANCE_COMPONENT = (1 << 7),
+ BACKLIGHT_COMPENSATION = (1 << 8),
+ GAIN = (1 << 9),
+ POWER_LINE_FREQUENCY = (1 << 10),
+ HUE_AUTO = (1 << 11),
+ WHITE_BALANCE_TEMPERATURE_AUTO = (1 << 12),
+ WHITE_BALANCE_COMPONENT_AUTO = (1 << 13),
+ DIGITAL_MULTIPLIER = (1 << 14),
+ DIGITAL_MULTIPLIER_LIMIT = (1 << 15),
+ ANALOG_VIDEO_STANDARD = (1 << 16),
+ ANALOG_VIDEO_LOCK_STATUS = (1 << 17)
+};
+
+
+/**
+ * 静止画のメソッドを定義します.
+ */
+enum uvc_vs_still_capture_method {
+ METHOD_0 = 0,
+ METHOD_1 = 1,
+ METHOD_2 = 2,
+ METHOD_3 = 3
+};
+
+
+/**
+ * 無圧縮時のフォーマットを定義します.
+ */
+enum uvc_uncompressed_format {
+ UNKNOWN = -1,
+ YUY2 = 0,
+ NV12 = 1,
+ M420 = 2,
+ I420 = 3
+};
+
+
+/**
+ * Device Descriptor.
+ */
+struct uvc_device_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+};
+
+/**
+ * Configuration Descriptor.
+ */
+struct uvc_configuration_descriptor {
+ struct uvc_configuration_descriptor *next;
+ struct uvc_video_control_interface *control_interface;
+ struct uvc_video_streaming_interface *streaming_interface;
+ struct uvc_video_streaming_altsetting *altsetting;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t wTotalLength;
+ uint8_t bNumInterfaces;
+ uint8_t bConfigurationValue;
+ uint8_t iConfiguration;
+ uint8_t bmAttributes;
+ uint8_t bMaxPower;
+};
+
+/**
+ * String Descriptor.
+ */
+struct uvc_string_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t *wLANGID;
+};
+
+/**
+ * Interface Descriptor.
+ */
+struct uvc_interface_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+};
+
+/**
+ * EndPoint Descriptor.
+ */
+struct uvc_endpoint_descriptor {
+ struct uvc_endpoint_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+};
+
+/**
+ * Interrupt Endpoint Descriptor.
+ */
+struct uvc_interrupt_endpoint_descriptor {
+ struct uvc_interrupt_endpoint_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint16_t wMaxTransferSize;
+};
+
+/**
+ * Interface Association Descriptor.
+ */
+struct uvc_interface_association_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bFirstInterface;
+ uint8_t bInterfaceCount;
+ uint8_t bFunctionClass;
+ uint8_t bFunctionSubClass;
+ uint8_t bFunctionProtocol;
+ uint8_t iFunction;
+};
+
+//// Video Control Descriptor
+
+/**
+ * Class-specific VC Interface Descriptor.
+ */
+struct uvc_vc_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint16_t bcdUVC;
+ uint16_t wTotalLength;
+ uint32_t dwClockFrequency;
+ uint8_t bInCollection;
+ uint8_t *baInterfaceNr;
+};
+
+/**
+ * Input Terminal Descriptor.
+ */
+struct uvc_vc_input_terminal_descriptor {
+ struct uvc_vc_input_terminal_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+
+ // Camera
+ uint16_t wObjectiveFocalLengthMin;
+ uint16_t wObjectiveFocalLengthMax;
+ uint16_t wOcularFocalLength;
+
+ uint8_t bControlSize;
+ uint8_t *bmControls;
+
+ // Media Transport
+ uint8_t bTransportModeSize;
+ uint8_t *bmTransportModes;
+};
+
+/**
+ * Output Terminal Descriptor.
+ */
+struct uvc_vc_output_terminal_descriptor {
+ struct uvc_vc_output_terminal_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t iTerminal;
+};
+
+/**
+ * Selector Unit Descriptor.
+ */
+struct uvc_vc_selector_unit_descriptor {
+ struct uvc_vc_selector_unit_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bUnitID;
+ uint8_t bNrInPins;
+ uint8_t *baSourceID;
+ uint8_t iSelector;
+};
+
+/**
+ * Processing Unit Descriptor.
+ */
+struct uvc_vc_processing_unit_descriptor {
+ struct uvc_vc_processing_unit_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bUnitID;
+ uint8_t bSourceID;
+ uint16_t wMaxMultiplier;
+ uint8_t bControlSize;
+ uint8_t *bmControls;
+ uint8_t iProcessing;
+ uint8_t bmVideoStandards;
+};
+
+/**
+ * Extension Unit Descriptor
+ */
+struct uvc_vc_extension_unit_descriptor {
+ struct uvc_vc_extension_unit_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bUnitID;
+ uint8_t guidExtensionCode[16];
+ uint8_t bNumControls;
+ uint8_t bNrInPins;
+ uint8_t *baSourceID;
+ uint8_t bControlSize;
+ uint8_t *bmControls;
+ uint8_t iExtension;
+};
+
+/**
+ * Encoding Unit Descriptor.
+ */
+struct uvc_vc_encoding_unit_descriptor {
+ struct uvc_vc_encoding_unit_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bUnitID;
+ uint8_t bSourceID;
+ uint8_t iEncoding;
+ uint8_t bControlSize; // The value must be 3.
+ uint8_t *bmControls;
+ uint8_t *bmControlsRuntime;
+};
+
+//// Video Streaming Descriptor
+
+/**
+ * Class-specific VS Header Descriptor (Input).
+ */
+struct uvc_vs_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bNumFormats;
+ uint16_t wTotalLength;
+ uint8_t bEndpointAddress;
+ uint8_t bmInfo;
+ uint8_t bTerminalLink;
+ uint8_t bStillCaptureMethod;
+ uint8_t bTriggerSupport;
+ uint8_t bTriggerUsage;
+ uint8_t bControlSize;
+ uint8_t *bmaControls;
+};
+
+
+struct uvc_video_streaming_interface;
+
+
+/**
+ * Video Stream Format Descriptor で共通部分を定義した構造体.
+ *
+ * 基本的に Format Descriptor が共通でもつデータを先頭に定義しておき
+ * キャストすることで簡単にアクセスできるようにする。
+ *
+ * ただし、各 format で共通部分の配置を変えてしまうとキャストした時に
+ * データが壊れてしまうので、十分に注意すること。
+ *
+ */
+struct uvc_vs_format_descriptor {
+ struct uvc_vs_format_descriptor *next;
+ struct uvc_vs_frame_descriptor *frame;
+ struct uvc_video_streaming_interface *streaming_interface;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bNumFrameDescriptors;
+};
+
+/**
+ * Video Stream Frame Descriptor で共通部分を定義した構造体.
+ *
+ * 基本的に Frame Descriptor が共通でもつデータを先頭に定義しておき
+ * キャストすることで簡単にアクセスできるようにする。
+ *
+ * ただし、各 frame で共通部分の配置を変えてしまうとキャストした時に
+ * データが壊れてしまうので、十分に注意すること。
+ *
+ */
+struct uvc_vs_frame_descriptor {
+ struct uvc_vs_frame_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFrameIndex;
+ uint8_t bmCapabilities;
+ uint16_t wWidth;
+ uint16_t wHeight;
+};
+
+/**
+ * Class-specific VS Format Descriptor (MJPEG).
+ *
+ * struct uvc_vs_format_descriptor と先頭の構造を同じにしています。
+ * 構造の配置が変わってしまうとデータが壊れてしまうので注意すること。
+ */
+struct uvc_vs_format_mjpeg_descriptor {
+ struct uvc_vs_format_mjpeg_descriptor *next;
+ struct uvc_vs_frame_mjpeg_descriptor *frame;
+ struct uvc_video_streaming_interface *streaming_interface;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bNumFrameDescriptors;
+ uint8_t bmFlags;
+ uint8_t bDefaultFrameIndex;
+ uint8_t bAspectRatioX;
+ uint8_t bAspectRatioY;
+ uint8_t bmInterlaceFlags;
+ uint8_t bCopyProtect;
+};
+
+/**
+ * Class-specific VS Frame Descriptor (MJPEG).
+ *
+ * struct uvc_vs_frame_descriptor と先頭の構造を同じにしています。
+ * 構造の配置が変わってしまうとデータが壊れてしまうので注意すること。
+ */
+struct uvc_vs_frame_mjpeg_descriptor {
+ struct uvc_vs_frame_mjpeg_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFrameIndex;
+ uint8_t bmCapabilities;
+ uint16_t wWidth;
+ uint16_t wHeight;
+ uint32_t dwMinBitRate;
+ uint32_t dwMaxBitRate;
+ uint32_t dwMaxVideoFrameBufferSize;
+ uint32_t dwDefaultFrameInterval;
+ uint8_t bFrameIntervalType;
+ uint32_t dwMinFrameInterval;
+ uint32_t dwMaxFrameInterval;
+ uint32_t dwFrameIntervalStep;
+ uint32_t *dwFrameInterval;
+};
+
+/**
+ * Class-specific VS Format Descriptor (Uncompressed).
+ *
+ * struct uvc_vs_format_descriptor と先頭の構造を同じにしています。
+ * 構造の配置が変わってしまうとデータが壊れてしまうので注意すること。
+ */
+struct uvc_vs_format_uncompressed_descriptor {
+ struct uvc_vs_frame_uncompressed_descriptor *frame;
+ struct uvc_vs_format_uncompressed_descriptor *next;
+ struct uvc_video_streaming_interface *streaming_interface;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bNumFrameDescriptors;
+ uint8_t guidFormat[16];
+ uint8_t bBitsPerPixel;
+ uint8_t bDefaultFrameIndex;
+ uint8_t bAspectRatioX;
+ uint8_t bAspectRatioY;
+ uint8_t bmInterlaceFlags;
+ uint8_t bCopyProtect;
+};
+
+/**
+ * Class-specific VS Frame Descriptor (Uncompressed).
+ *
+ * struct uvc_vs_frame_descriptor と先頭の構造を同じにしています。
+ * 構造の配置が変わってしまうとデータが壊れてしまうので注意すること。
+ */
+struct uvc_vs_frame_uncompressed_descriptor {
+ struct uvc_vs_frame_uncompressed_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFrameIndex;
+ uint8_t bmCapabilities;
+ uint16_t wWidth;
+ uint16_t wHeight;
+ uint32_t dwMinBitRate;
+ uint32_t dwMaxBitRate;
+ uint32_t dwMaxVideoFrameBufferSize;
+ uint32_t dwDefaultFrameInterval;
+ uint8_t bFrameIntervalType;
+ uint32_t dwMinFrameInterval;
+ uint32_t dwMaxFrameInterval;
+ uint32_t dwFrameIntervalStep;
+ uint32_t *dwFrameInterval;
+};
+
+/**
+ * H.264 Payload Video Format Descriptor.
+ *
+ * struct uvc_vs_format_descriptor と先頭の構造を同じにしています。
+ * 構造の配置が変わってしまうとデータが壊れてしまうので注意すること。
+ */
+struct uvc_vs_format_h264_descriptor {
+ struct uvc_vs_format_h264_descriptor *next;
+ struct uvc_vs_frame_h264_descriptor *frame;
+ struct uvc_video_streaming_interface *streaming_interface;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bNumFrameDescriptors;
+ uint8_t bDefaultFrameIndex;
+ uint8_t bMaxCodecConfigDelay;
+ uint8_t bmSupportedSliceModes;
+ uint8_t bmSupportedSyncFrameTypes;
+ uint8_t bResuolutionScaling;
+ uint8_t Reserved1;
+ uint8_t bmSupportedRateControlModes;
+ uint16_t wMaxMBperSecOneResolutionNoScalability;
+ uint16_t wMaxMBperSecTwoResolutionsNoScalability;
+ uint16_t wMaxMBperSecThreeResolutionsNoScalability;
+ uint16_t wMaxMBperSecFourResolutionsNoScalability;
+ uint16_t wMaxMBperSecOneResolutionTemporalScalability;
+ uint16_t wMaxMBperSecTwoResolutionsTemporalScalablility;
+ uint16_t wMaxMBperSecThreeResolutionsTemporalScalability;
+ uint16_t wMaxMBperSecFourResolutionsTemporalScalability;
+ uint16_t wMaxMBperSecOneResolutionTemporalQualityScalability;
+ uint16_t wMaxMBperSecTwoResolutionsTemporalQualityScalability;
+ uint16_t wMaxMBperSecThreeResolutionsTemporalQualityScalability;
+ uint16_t wMaxMBperSecFourResolutionsTemporalQualityScalability;
+ uint16_t wMaxMBperSecOneResolutionsTemporalSpatialScalability;
+ uint16_t wMaxMBperSecTwoResolutionsTemporalSpatialScalability;
+ uint16_t wMaxMBperSecThreeResolutionsTemporalSpatialScalability;
+ uint16_t wMaxMBperSecFourResolutionsTemporalSpatialScalability;
+ uint16_t wMaxMBperSecOneResolutionFullScalability;
+ uint16_t wMaxMBperSecTwoResolutionsFullScalability;
+ uint16_t wMaxMBperSecThreeResolutionsFullScalability;
+ uint16_t wMaxMBperSecFourResolutionsFullScalability;
+};
+
+
+/**
+ * H.264 Payload Video Frame Descriptor.
+ *
+ * struct uvc_vs_frame_descriptor と先頭の構造を同じにしています。
+ * 構造の配置が変わってしまうとデータが壊れてしまうので注意すること。
+ */
+struct uvc_vs_frame_h264_descriptor {
+ struct uvc_vs_frame_h264_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFrameIndex;
+ uint8_t placeHolder; // h264 には bmCapabilities がないので、代用として uint8_t を入れる
+ uint16_t wWidth;
+ uint16_t wHeight;
+ uint16_t wSARwidth;
+ uint16_t wSARheight;
+ uint16_t wProfie;
+ uint8_t bLevelIDC;
+ uint16_t wConstrainedToolset;
+ uint32_t bmSupportedUsages;
+ uint16_t bmCapabilities;
+ uint32_t bmSVCCapabilities;
+ uint32_t bmMVCCapabilities;
+ uint32_t dwMinBitRate;
+ uint32_t dwMaxBitRate;
+ uint32_t dwDefaultFrameInterval;
+ uint8_t bNumFrameIntervals;
+ uint32_t *dwFrameInterval;
+};
+
+/**
+ * Class-specific Still Image Frame Descriptor.
+ */
+struct uvc_vs_still_image_frame_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bEndpointAddress;
+ uint8_t bNumImageSizePatterns;
+ uint16_t *wWidth;
+ uint16_t *wHeight;
+ uint8_t bNumCompressionPtn;
+ uint8_t *bCompression;
+};
+
+/**
+ * Class-specific Color Matching Descriptor.
+ */
+struct uvc_vs_color_matching_descriptor {
+ struct uvc_vs_color_matching_descriptor *next;
+
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bColorPrimaries;
+ uint8_t bTransferCharacteristics;
+ uint8_t bMatrixCoefficients;
+};
+
+/**
+ * Video Control I/F.
+ */
+struct uvc_video_control_interface {
+ struct uvc_interface_descriptor *interface;
+ struct uvc_vc_header_descriptor *header;
+ struct uvc_vc_input_terminal_descriptor *input_terminal;
+ struct uvc_vc_output_terminal_descriptor *output_terminal;
+ struct uvc_vc_selector_unit_descriptor *selector;
+ struct uvc_vc_processing_unit_descriptor *processing;
+ struct uvc_vc_extension_unit_descriptor *extension;
+ struct uvc_vc_encoding_unit_descriptor *encoding;
+ struct uvc_endpoint_descriptor *endpoint;
+ struct uvc_interrupt_endpoint_descriptor *interrupt_endpoint;
+};
+
+/**
+ * Video Streaming I/F.
+ *
+ * Alt. Setting 0
+ */
+struct uvc_video_streaming_interface {
+ struct uvc_video_streaming_interface *next;
+
+ struct uvc_interface_descriptor *interface;
+ struct uvc_vs_header_descriptor *header;
+ struct uvc_vs_format_descriptor *format;
+ struct uvc_vs_still_image_frame_descriptor *frame_still;
+ struct uvc_vs_color_matching_descriptor *color_matching;
+ struct uvc_endpoint_descriptor *still_endpoint;
+};
+
+/**
+ * Video Streaming I/F.
+ *
+ * Alt. Setting 1 - n
+ */
+struct uvc_video_streaming_altsetting {
+ struct uvc_video_streaming_altsetting *next;
+
+ struct uvc_interface_descriptor *interface;
+ struct uvc_endpoint_descriptor *video_endpoint;
+ struct uvc_endpoint_descriptor *still_endpoint;
+};
+
+
+/**
+ * UVC Descriptor.
+ */
+struct uvc_descriptor {
+ struct uvc_device_descriptor *device;
+ struct uvc_configuration_descriptor *configuration;
+ struct uvc_interface_association_descriptor *interface_association;
+};
+
+/**
+ * Descriptor のコンフィグを取得します。
+ *
+ * @param descriptor データを格納する構造体
+ * @param config_id コンフィグID
+ *
+ * @return コンフィグレーションのディスクリプタ
+ */
+struct uvc_configuration_descriptor *uvc_get_configuration_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id);
+
+/**
+ * UVC に必要な Descriptor を解釈して構造体にデータを格納します.
+ *
+ * @param descriptor データを格納する構造体
+ * @param buffer Descriptorが格納されているバッファ
+ * @param length バッファサイズ
+ * @return UVC_SUCCESSの場合には解析に成功、それ以外の場合は解析に失敗
+ */
+uvc_result uvc_parse_descriptor(struct uvc_descriptor *descriptor, uint8_t *buffer, int32_t length);
+
+/**
+ * Descriptor で確保したメモリを解放します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ */
+void uvc_dispose_descriptor(struct uvc_descriptor *descriptor);
+
+/**
+ * bcdUVCの値を取得します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id bcdUVCが属しているコンフィギュレーションのID
+ * @return bcdUVCの値
+ */
+uint16_t uvc_get_uvc_version(struct uvc_descriptor *descriptor, uint8_t config_id);
+
+/**
+ * 指定されたコンフィギュレーションのIDに対応する uvc_video_control_interface を取得します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id コンフィギュレーションのID
+ * @return uvc_video_control_interface のポインタ、対応した uvc_video_control_interface が無い場合にはNULLを返却します。
+ */
+struct uvc_video_control_interface *uvc_get_video_control_interface(struct uvc_descriptor *descriptor, uint8_t config_id);
+
+/**
+ * 指定されたコンフィギュレーションのIDに対応する uvc_video_streaming_interface を取得します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id コンフィギュレーションのID
+ * @return uvc_video_streaming_interfaceのポインタ、対応した uvc_video_streaming_interface のポインタが無い場合にはNULLを返却します。
+ */
+struct uvc_video_streaming_interface *uvc_get_video_streaming_interface(struct uvc_descriptor *descriptor, uint8_t config_id);
+
+/**
+ * 指定されたコンフィギュレーションのIDに対応する uvc_video_streaming_altsetting を取得します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id コンフィギュレーションのID
+ * @return uvc_video_streaming_altsetting のポインタ、対応した uvc_video_streaming_altsetting のポインタが無い場合にはNULLを返却します。
+ */
+struct uvc_video_streaming_altsetting *uvc_get_video_streaming_altsetting(struct uvc_descriptor *descriptor, uint8_t config_id);
+
+/**
+ * 指定されたインデックスのフォーマットを取得します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id コンフィギュレーションのID
+ * @param format_index フォーマットインデックス
+ * @return 対応したフォーマットディスクリプタ、対応したフォーマットディスクリプタが見つからない場合にはNULL
+ */
+struct uvc_vs_format_descriptor *uvc_find_format_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id, uint8_t format_index);
+
+/**
+ * 指定されたインデックスのフレームディスクリプタを取得します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id コンフィギュレーションのID
+ * @param format_index フォーマットインデックス
+ * @param frame_index フレームインデックス
+ * @return 対応したフレームディスクリプタ、対応したフレームディスクリプタが見つからない場合にはNULL
+ */
+struct uvc_vs_frame_descriptor *uvc_find_frame_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id, uint8_t format_index, uint8_t frame_index);
+
+/**
+ * 無圧縮のフォーマットを取得します.
+ *
+ * @param format フォーマットのデゥスクリプタ
+ * @return 無圧縮のフォーマット
+ */
+enum uvc_uncompressed_format uvc_get_uncompressed_format(struct uvc_vs_format_descriptor *format);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif //UVC_DESCRIPTOR_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-h264-config.c b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-h264-config.c
new file mode 100644
index 0000000000..7be788ebb2
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-h264-config.c
@@ -0,0 +1,87 @@
+/*
+ uvc-h264-config.c
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#include
+
+#include "uvc-h264-config.h"
+
+static const uint8_t GUID_UVCX_H264_XU[] = {
+ 0x41, 0x76, 0x9e, 0xa2, 0x04, 0xde, 0xe3, 0x47, 0x8b, 0x2b, 0xf4, 0x34, 0x1a, 0xff, 0x00, 0x3b
+};
+
+
+uint8_t uvc_has_h264_extension(struct uvc_descriptor *descriptor, uint8_t config_id) {
+ if (descriptor == NULL) {
+ return UVC_FALSE;
+ }
+
+ struct uvc_configuration_descriptor *config = uvc_get_configuration_descriptor(descriptor, config_id);
+ if (config) {
+ struct uvc_vc_extension_unit_descriptor *extension = config->control_interface->extension;
+ while (extension) {
+ if (memcmp(extension->guidExtensionCode, GUID_UVCX_H264_XU, 16) == 0) {
+ return UVC_TRUE;
+ }
+ extension = extension->next;
+ }
+ }
+
+ return UVC_FALSE;
+}
+
+
+struct uvc_vc_extension_unit_descriptor *uvc_find_extension_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id) {
+ if (descriptor == NULL) {
+ return NULL;
+ }
+
+ struct uvc_configuration_descriptor *config = uvc_get_configuration_descriptor(descriptor, config_id);
+ if (config) {
+ struct uvc_vc_extension_unit_descriptor *extension = config->control_interface->extension;
+ while (extension) {
+ if (memcmp(extension->guidExtensionCode, GUID_UVCX_H264_XU, 16) == 0) {
+ return extension;
+ }
+ extension = extension->next;
+ }
+ }
+
+ return NULL;
+}
+
+
+void uvc_print_h264_extension_unit(struct uvc_h264_extension_unit *config) {
+ LOGI("h264_extension");
+ LOGI(" h264_extension.dwFrameInterval: %d", config->dwFrameInterval);
+ LOGI(" h264_extension.dwBitRate: %d", config->dwBitRate);
+ LOGI(" h264_extension.bmHints: 0x%04X", config->bmHints);
+ LOGI(" h264_extension.wConfigurationIndex: %d", config->wConfigurationIndex);
+ LOGI(" h264_extension.wWidth: %d", config->wWidth);
+ LOGI(" h264_extension.wHeight: %d", config->wHeight);
+ LOGI(" h264_extension.wSliceUnits: %d", config->wSliceUnits);
+ LOGI(" h264_extension.wSliceMode: %d", config->wSliceMode);
+ LOGI(" h264_extension.wProfile: 0x%04X", config->wProfile);
+ LOGI(" h264_extension.wIFramePeriod: %d", config->wIFramePeriod);
+ LOGI(" h264_extension.wEstimatedVideoDelay: %d", config->wEstimatedVideoDelay);
+ LOGI(" h264_extension.wEstimatedMaxConfigDelay: %d", config->wEstimatedMaxConfigDelay);
+ LOGI(" h264_extension.bUsageType: 0x%02X", config->bUsageType);
+ LOGI(" h264_extension.bRateControlMode: 0x%02X", config->bRateControlMode);
+ LOGI(" h264_extension.bTemporalScaleMode: 0x%02X", config->bTemporalScaleMode);
+ LOGI(" h264_extension.bSpatialScaleMode: 0x%02X", config->bSpatialScaleMode);
+ LOGI(" h264_extension.bSNRScaleMode: 0x%02X", config->bSNRScaleMode);
+ LOGI(" h264_extension.bStreamMuxOption: 0x%02X", config->bStreamMuxOption);
+ LOGI(" h264_extension.bStreamFormat: 0x%02X", config->bStreamFormat);
+ LOGI(" h264_extension.bEntropyCABAC: 0x%02X", config->bEntropyCABAC);
+ LOGI(" h264_extension.bTimestamp: 0x%02X", config->bTimestamp);
+ LOGI(" h264_extension.bNumOfReorderFrames: 0x%02X", config->bNumOfReorderFrames);
+ LOGI(" h264_extension.bPreviewFlipped: 0x%02X", config->bPreviewFlipped);
+ LOGI(" h264_extension.bView: 0x%02X", config->bView);
+ LOGI(" h264_extension.bReserved1: 0x%02X", config->bReserved1);
+ LOGI(" h264_extension.bReserved2: 0x%02X", config->bReserved2);
+ LOGI(" h264_extension.bStreamID: 0x%02X", config->bStreamID);
+ LOGI(" h264_extension.bSpatialLayerRatio: 0x%02X", config->bSpatialLayerRatio);
+ LOGI(" h264_extension.wLeakyBucketSize: %d", config->wLeakyBucketSize);
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-h264-config.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-h264-config.h
new file mode 100644
index 0000000000..66ec5a8457
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-h264-config.h
@@ -0,0 +1,192 @@
+/*
+ uvc-h264-config.h
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#ifndef UVC_H264_CONFIG_H
+#define UVC_H264_CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include "uvc-descriptor.h"
+
+
+/**
+ * H.264 用拡張定義.
+ */
+enum {
+ UVCX_VIDEO_UNDEFINED = 0x00,
+ UVCX_VIDEO_CONFIG_PROBE = 0x01,
+ UVCX_VIDEO_CONFIG_COMMIT = 0x02,
+ UVCX_RATE_CONTROL_MODE = 0x03,
+ UVCX_TEMPORAL_SCALE_MODE = 0x04,
+ UVCX_SPATIAL_SCALE_MODE = 0x05,
+ UVCX_SNR_SCALE_MODE = 0x06,
+ UVCX_LTR_BUFFER_SIZE_CONTROL = 0x07,
+ UVCX_LTR_PICTURE_CONTROL = 0x08,
+ UVCX_PICTURE_TYPE_CONTROL = 0x09,
+ UVCX_VERSION = 0x0A,
+ UVCX_ENCODER_RESET = 0x0B,
+ UVCX_FRAMERATE_CONFIG = 0x0C,
+ UVCX_VIDEO_ADVANCE_CONFIG = 0x0D,
+ UVCX_BITRATE_LAYERS = 0x0E,
+ UVCX_QP_STEPS_LAYERS = 0x0F,
+};
+
+enum {
+ UVC_H264_BMHINTS_RESOLUTION = 0x0001,
+ UVC_H264_BMHINTS_PROFILE = 0x0002,
+ UVC_H264_BMHINTS_RATECONTROL = 0x0004,
+ UVC_H264_BMHINTS_USAGE = 0x0008,
+ UVC_H264_BMHINTS_SLICEMODE = 0x0010,
+ UVC_H264_BMHINTS_SLICEUNITS = 0x0020,
+ UVC_H264_BMHINTS_MVCVIEW = 0x0040,
+ UVC_H264_BMHINTS_TEMPORAL = 0x0080,
+ UVC_H264_BMHINTS_SNR = 0x0100,
+ UVC_H264_BMHINTS_SPATIAL = 0x0200,
+ UVC_H264_BMHINTS_SPATIAL_RATIO = 0x0400,
+ UVC_H264_BMHINTS_FRAME_INTERVAL = 0x0800,
+ UVC_H264_BMHINTS_LEAKY_BKT_SIZE = 0x1000,
+ UVC_H264_BMHINTS_BITRATE = 0x2000,
+ UVC_H264_BMHINTS_ENTROPY = 0x4000,
+ UVC_H264_BMHINTS_IFRAMEPERIOD = 0x8000
+};
+
+enum {
+ UVC_H264_SLICEMODE_IGNORED = 0x0000,
+ UVC_H264_SLICEMODE_BITSPERSLICE = 0x0001,
+ UVC_H264_SLICEMODE_MBSPERSLICE = 0x0002,
+ UVC_H264_SLICEMODE_SLICEPERFRAME = 0x0003
+};
+
+enum {
+ UVC_H264_USAGETYPE_REALTIME = 0x01,
+ UVC_H264_USAGETYPE_BROADCAST = 0x02,
+ UVC_H264_USAGETYPE_STORAGE = 0x03,
+ UVC_H264_USAGETYPE_UCCONFIG_0 = 0x04,
+ UVC_H264_USAGETYPE_UCCONFIG_1 = 0x05,
+ UVC_H264_USAGETYPE_UCCONFIG_2Q = 0x06,
+ UVC_H264_USAGETYPE_UCCONFIG_2S = 0x07,
+ UVC_H264_USAGETYPE_UCCONFIG_3 = 0x08,
+};
+
+enum {
+ UVC_H264_RATECONTROL_CBR = 0x01,
+ UVC_H264_RATECONTROL_VBR = 0x02,
+ UVC_H264_RATECONTROL_CONST_QP = 0x03,
+};
+
+enum {
+ UVC_H264_STREAMFORMAT_ANNEXB = 0x00,
+ UVC_H264_STREAMFORMAT_NAL = 0x01,
+};
+
+enum {
+ UVC_H264_ENTROPY_CAVLC = 0x00,
+ UVC_H264_ENTROPY_CABAC = 0x01,
+};
+
+enum {
+ UVC_H264_TIMESTAMP_SEI_DISABLE = 0x00,
+ UVC_H264_TIMESTAMP_SEI_ENABLE = 0x01
+};
+
+enum {
+ UVC_H264_PREFLIPPED_DISABLE = 0x00,
+ UVC_H264_PREFLIPPED_HORIZONTAL = 0x01
+};
+
+enum {
+ UVC_H264_PROFILE_BASELINE = 0x4200,
+ UVC_H264_PROFILE_MAIN = 0x4D00,
+ UVC_H264_PROFILE_HIGH = 0x6400,
+ UVC_H264_PROFILE_SCALABLE_BASELINE = 0x5300,
+ UVC_H264_PROFILE_SCALABLE_HIGH = 0x5600,
+ UVC_H264_PROFILE_MULTIVIEW_HIGH = 0x7600,
+ UVC_H264_PROFILE_STEREO_HIGH = 0x8000,
+ UVC_H264_PROFILE_CONSTRAINED_BASELINE = 0x4240,
+};
+
+enum {
+ UVC_H264_PICTYPE_I_FRAME = 0x00,
+ UVC_H264_PICTYPE_IDR = 0x01,
+ UVC_H264_PICTYPE_IDR_WITH_PPS_SPS = 0x02
+};
+
+enum {
+ UVC_H264_MUX_OPTION_DISABLE = 0x00,
+ UVC_H264_MUX_OPTION_ENABLE = 0x01,
+ UVC_H264_MUX_OPTION_H264 = 0x02,
+ UVC_H264_MUX_OPTION_YUY2 = 0x04,
+ UVC_H264_MUX_OPTION_NV12 = 0x08,
+ UVC_H264_MUX_OPTION_MJPEG_CONTAINER = 0x40,
+};
+
+/**
+ * UVC 1.1 において、H.264 拡張として定義された構造体.
+ */
+struct uvc_h264_extension_unit {
+ uint32_t dwFrameInterval;
+ uint32_t dwBitRate;
+ uint16_t bmHints;
+ uint16_t wConfigurationIndex;
+ uint16_t wWidth;
+ uint16_t wHeight;
+ uint16_t wSliceUnits;
+ uint16_t wSliceMode;
+ uint16_t wProfile;
+ uint16_t wIFramePeriod;
+ uint16_t wEstimatedVideoDelay;
+ uint16_t wEstimatedMaxConfigDelay;
+ uint8_t bUsageType;
+ uint8_t bRateControlMode;
+ uint8_t bTemporalScaleMode;
+ uint8_t bSpatialScaleMode;
+ uint8_t bSNRScaleMode;
+ uint8_t bStreamMuxOption;
+ uint8_t bStreamFormat;
+ uint8_t bEntropyCABAC;
+ uint8_t bTimestamp;
+ uint8_t bNumOfReorderFrames;
+ uint8_t bPreviewFlipped;
+ uint8_t bView;
+ uint8_t bReserved1;
+ uint8_t bReserved2;
+ uint8_t bStreamID;
+ uint8_t bSpatialLayerRatio;
+ uint16_t wLeakyBucketSize;
+};
+
+
+/**
+ * H264 のエクステンションユニットを持っているか確認します.
+ *
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id コンフィギュレーションのID
+ * @return エクステンションがある場合には UVC_TRUE、それ以外は UVC_FALSE
+ */
+uint8_t uvc_has_h264_extension(struct uvc_descriptor *descriptor, uint8_t config_id);
+
+/**
+ * H264 のエクステンションユニットを取得します.
+ * @param descriptor Descriptorのデータを格納した構造体
+ * @param config_id コンフィギュレーションのID
+ * @return エクステンションユニット、存在しない場合には NULL
+ */
+struct uvc_vc_extension_unit_descriptor *uvc_find_extension_descriptor(struct uvc_descriptor *descriptor, uint8_t config_id);
+
+/**
+ * H264 の設定を出力します.
+ *
+ * @param config H264 の設定
+ */
+void uvc_print_h264_extension_unit(struct uvc_h264_extension_unit *config);
+
+#ifdef __cplusplus
+}
+#endif
+#endif //UVC_H264_CONFIG_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-native-lib.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-native-lib.cpp
new file mode 100644
index 0000000000..5d035670e6
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc-native-lib.cpp
@@ -0,0 +1,1137 @@
+/*
+ uvc-native-lib.cpp
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#include
+#include
+#include
+#include
+
+#include "uvc.h"
+
+/**
+ * JNI のパッケージ名、クラス名を定義.
+ */
+#define JNI_METHOD_NAME(name) \
+ Java_org_deviceconnect_android_libuvc_UVCCameraNative_##name
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * JPEGヘッダーで使用されるハフマンテーブルのサイズ.
+ */
+static const size_t huff_tbl_size = 432;
+
+/**
+ * JPEGヘッダーで使用されるハフマンテーブル.
+ */
+static const uint8_t huff_tbl[huff_tbl_size] = {
+ // dc_huff_tbl_0
+ 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b,
+
+ // ac_huff_tbl_0
+ 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
+ 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+ 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
+ 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14,
+ 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15,
+ 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29,
+ 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
+ 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
+ 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa,
+
+ // dc_huff_tbl_1
+ 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b,
+
+ // ac_huff_tbl_1
+ 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+ 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02,
+ 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
+ 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32,
+ 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23,
+ 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24,
+ 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
+ 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4,
+ 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5,
+ 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+ 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa
+};
+
+
+/**
+ * Java側の UVCCamera クラスにアクセスするためのIDを保持する構造体.
+ */
+struct java_uvc_camera {
+ jmethodID onFrame_method_id;
+ jmethodID getFrame_method_id;
+};
+
+
+/**
+ * Java側の Frame クラスにアクセスするためのIDを保持する構造体.
+ */
+struct java_frame {
+ jmethodID getId_method_id;
+ jmethodID getBuffer_method_id;
+ jmethodID resizeBuffer_method_id;
+ jmethodID setLength_method_id;
+ jfieldID parameter_field_id;
+};
+
+
+/**
+ * Java側の Parameter クラスにアクセスするためのIDを保持する構造体.
+ */
+struct java_parameter {
+ jfieldID formatIndexId_field_id;
+ jfieldID frameIndexId_field_id;
+ jfieldID fpsId_field_id;
+};
+
+
+/**
+ * Extension Unit で MJPEG に多重化された H264 のデータを格納する構造体.
+ */
+struct ext_h264_buffer {
+ uint32_t length;
+ uint32_t got_bytes;
+ uint32_t payload_size;
+ uint8_t bytes[0];
+};
+
+/**
+ * uvc_device_handle から受け取るコールバック関数に渡す構造体.
+ */
+struct user_context {
+ JNIEnv *env;
+ jobject obj;
+ uint8_t count;
+ uint8_t use_ext_h264;
+ struct java_uvc_camera uvc;
+ struct java_frame frame;
+ struct java_parameter parameter;
+ struct uvc_device_handle *handle;
+ struct ext_h264_buffer *buffer;
+};
+
+/**
+ * スタートマーカ(Start Of Image).
+ */
+#define SOI 0xD8
+
+/**
+ * エンドマーカ(End Of Image).
+ */
+#define EOI 0xD9
+
+/**
+ * スキャンヘッダー(Start of Scan).
+ */
+#define SOS 0xDA
+#define DHT 0xC4
+
+/**
+ * フレームヘッダー(Start of Frame).
+ */
+#define SOF 0xC0
+
+#define APP_4_MAKER 0xE4
+
+/**
+ * JPEGのセグメントを解釈して、SOIの位置とDHTの有無を確認します.
+ *
+ * @param data JPEGデータ
+ * @param length JPEGデータサイズ
+ * @param soi SOIの位置を格納するポインタ
+ * @param dht DHTの個数を格納するポインタ
+ */
+static void jpeg_parse(uint8_t *data, uint32_t length, uint32_t *soi, uint32_t *dht) {
+ uint32_t len = 0;
+ for (uint32_t i = 0; i < length; i += (2 + len)) {
+ uint8_t d = data[i];
+ if (d == 0xFF) {
+ d = data[i + 1];
+ if (d == SOI) {
+ *soi = i;
+ len = 0;
+ } else {
+ len = (data[i + 2] << 8) | data[i + 3];
+ switch (d) {
+ case DHT: // Huffman Table
+ *dht = (*dht) + 1;
+ break;
+ case SOS: // Start of scan segment
+ i = length;
+ break;
+ default: // Other
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * uvc からの静止画用のフレームバッファ通知を受け取るコールバック関数.
+ *
+ * @param handle uvc を操作するハンドル
+ * @param frame フレームバッファ
+ */
+static void callback_still_frame(void *user, struct uvc_frame *frame) {
+ struct user_context *context = (struct user_context *) user;
+ if (context == NULL) {
+ return;
+ }
+
+ // 最初の15フレームは画面が暗くなっているので無視します。
+ if (context->count < 15) {
+ context->count++;
+ return;
+ }
+
+ // uvc_device_handleの状態を停止状態にする
+ context->handle->running = UVC_VIDEO_STOP;
+
+ jobject frameObj = context->obj;
+ if (frameObj == NULL) {
+ return;
+ }
+
+ uint8_t *data = frame->buf;
+ uint32_t data_size = frame->got_bytes;
+ int insert_dht = UVC_FALSE;
+
+ switch (frame->type) {
+ default:
+ case VS_FRAME_UNCOMPRESSED:
+ case VS_FRAME_H264:
+ break;
+ case VS_FRAME_MJPEG: {
+ uint32_t soi = frame->got_bytes;
+ uint32_t dht = 0;
+ jpeg_parse(frame->buf, frame->got_bytes, &soi, &dht);
+ if (soi == frame->got_bytes) {
+ // SOIが見つからない場合は無視
+ return;
+ }
+
+ if (soi > 0) {
+ data = frame->buf + soi;
+ data_size = frame->got_bytes - soi;
+ }
+
+ // ハフマンテーブルをコピーするので、その分をデータサイズに追加
+ if (dht == 0) {
+ insert_dht = UVC_TRUE;
+ data_size += huff_tbl_size;
+ }
+ } break;
+ }
+
+
+ // Frame#resizeBuffer() と Frame#setLength() でバッファの初期化
+ context->env->CallVoidMethod(frameObj, context->frame.resizeBuffer_method_id, data_size);
+ context->env->CallVoidMethod(frameObj, context->frame.setLength_method_id, data_size);
+
+ // Frame#getBuffer() からバッファを取得
+ jbyteArray byteArray = (jbyteArray) context->env->CallObjectMethod(frameObj, context->frame.getBuffer_method_id);
+ if (byteArray == NULL) {
+ context->env->DeleteLocalRef(frameObj);
+ return;
+ }
+
+ jlong capacity = context->env->GetArrayLength(byteArray);
+ if (data_size > capacity) {
+ context->env->DeleteLocalRef(byteArray);
+ return;
+ }
+
+ jboolean isCopy;
+ uint8_t *dst = (uint8_t *) context->env->GetByteArrayElements(byteArray, &isCopy);
+ if (dst == NULL) {
+ context->env->DeleteLocalRef(byteArray);
+ return;
+ }
+
+ if (insert_dht == UVC_TRUE) {
+ // MotionJPEG の場合に、ハフマンテーブルがない場合には、ここでコピーしておく。
+ // mjpeg_processing_frame でコピーを行うと memcpy が発生してしまうので。
+ memcpy(dst, data, 2);
+ memcpy((dst + 2), huff_tbl, huff_tbl_size);
+ memcpy((dst + 2 + huff_tbl_size), (data + 2), data_size - huff_tbl_size - 2);
+ } else {
+ memcpy(dst, data, data_size);
+ }
+
+ context->env->ReleaseByteArrayElements(byteArray, (jbyte *) dst, 0);
+ context->env->DeleteLocalRef(byteArray);
+}
+
+
+/**
+ * フレームデータの値を Java 側に通知します.
+ *
+ * @param context コンテキスト
+ * @param data フレームデータ
+ * @param data_size データサイズ
+ * @param pts Presentation timestamp
+ * @param insert_dht ハフマンテーブルをコピーする場合はUVC_TRUE、それ以外はUVC_FALSE
+ */
+static void send_frame(struct user_context *context, uint8_t *data, uint32_t data_size, uint32_t pts, int insert_dht) {
+ if (data_size <= 0) {
+ LOGE("@@@ send_frame: data_size is negative.");
+ return;
+ }
+
+ // ハフマンテーブルをコピーするので、その分をデータサイズに追加
+ if (insert_dht == UVC_TRUE) {
+ data_size += huff_tbl_size;
+ }
+
+ jobject frameObj = context->env->CallObjectMethod(context->obj, context->uvc.getFrame_method_id, data_size);
+ if (frameObj == NULL) {
+ LOGE("@@@ send_frame: Failed to get a frame object from java vm.");
+ return;
+ }
+
+ jint id = context->env->CallIntMethod(frameObj, context->frame.getId_method_id);
+ jbyteArray byteArray = (jbyteArray) context->env->CallObjectMethod(frameObj, context->frame.getBuffer_method_id);
+ if (byteArray == NULL) {
+ context->env->DeleteLocalRef(frameObj);
+ return;
+ }
+
+ jlong capacity = context->env->GetArrayLength(byteArray);
+ if (data_size > capacity) {
+ context->env->DeleteLocalRef(byteArray);
+ context->env->DeleteLocalRef(frameObj);
+ return;
+ }
+
+ jboolean isCopy;
+ uint8_t *dst = (uint8_t *) context->env->GetByteArrayElements(byteArray, &isCopy);
+ if (dst == NULL) {
+ context->env->DeleteLocalRef(byteArray);
+ context->env->DeleteLocalRef(frameObj);
+ return;
+ }
+
+ if (insert_dht == UVC_TRUE) {
+ // MotionJPEG の場合に、ハフマンテーブルがない場合には、ここでコピーしておく。
+ // mjpeg_processing_frame でコピーを行うとmemcpyが発生してしまうので。
+ memcpy(dst, data, 2);
+ memcpy((dst + 2), huff_tbl, huff_tbl_size);
+ memcpy((dst + 2 + huff_tbl_size), (data + 2), data_size - huff_tbl_size - 2);
+ } else {
+ memcpy(dst, data, data_size);
+ }
+
+ context->env->CallVoidMethod(context->obj, context->uvc.onFrame_method_id, id, data_size, pts);
+
+ context->env->ReleaseByteArrayElements(byteArray, (jbyte *) dst, 0);
+ context->env->DeleteLocalRef(byteArray);
+ context->env->DeleteLocalRef(frameObj);
+}
+
+
+/**
+ * H264 のフレームデータを処理して Java 側に通知します.
+ *
+ * NALU では、データの開始が 0x000001 または 0x00000001 になります。
+ * Java 側で処理しやすいように、配列の先頭が 0x000001 または 0x00000001 になるように処理を行なってから通知します。
+ *
+ * @param context コンテキスト
+ * @param frame 通知するフレームデータ
+ */
+static void h264_processing_frame(struct user_context *context, struct uvc_frame *frame) {
+ uint8_t *data = frame->buf;
+ uint32_t length = frame->got_bytes;
+ uint32_t start_pos = 0;
+ uint32_t count = 0;
+
+ for (uint32_t i = 0; i < length - 4; i++) {
+ if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0x00 && data[i + 3] == 0x01) {
+ if (count > 0) {
+ send_frame(context, &data[start_pos], i - start_pos, frame->pts, UVC_FALSE);
+ }
+ start_pos = i;
+ count++;
+ }
+ }
+
+ if (length - start_pos > 0) {
+ send_frame(context, &data[start_pos], length - start_pos, frame->pts, UVC_FALSE);
+ }
+}
+
+
+/**
+ * Uncompressed のフレームデータを処理して Java 側に通知します.
+ *
+ * @param context コンテキスト
+ * @param frame 通知するフレームデータ
+ */
+static void uncompressed_processing_frame(struct user_context *context, struct uvc_frame *frame) {
+ send_frame(context, frame->buf, frame->got_bytes, frame->pts, UVC_FALSE);
+}
+
+
+/**
+ * Motion JPEG のフレームデータを処理して Java 側に通知します.
+ *
+ * @param context コンテキスト
+ * @param frame 通知するフレームデータ
+ */
+static void mjpeg_processing_frame(struct user_context *context, struct uvc_frame *frame) {
+ uint32_t soi = frame->got_bytes;
+ uint32_t dht = 0;
+ jpeg_parse(frame->buf, frame->got_bytes, &soi, &dht);
+ if (soi == frame->got_bytes) {
+ // SOIが見つからない場合は無視
+ return;
+ }
+ send_frame(context, frame->buf + soi, frame->got_bytes - soi, frame->pts, dht == 0 ? UVC_TRUE : UVC_FALSE);
+}
+
+/**
+ * MJPEG に格納された H264 のデータを処理して Java 側に通知します.
+ *
+ * USB_Video_Payload_H 264_1 0.pdf 「3.4 Packetization」を参照すること。
+ *
+ * @param context コンテキスト
+ * @param frame 通知するフレームデータ
+ */
+static void ext_h264_processing_frame(struct user_context *context, struct uvc_frame *frame) {
+ uint8_t *data = frame->buf;
+ uint32_t length = frame->length;
+ uint32_t len = 0;
+ for (uint32_t i = 0; i < length; i += (2 + len)) {
+ uint8_t d = data[i];
+ if (d == 0xFF) {
+ d = data[i + 1];
+ if (d == SOI) {
+ len = 0;
+ } else {
+ len = (data[i + 2] << 8) | data[i + 3];
+ switch (d) {
+ case APP_4_MAKER: {
+ if (context->buffer->got_bytes == 0) {
+ uint16_t version = (data[i + 4]) | (data[i + 5] << 8);
+ uint16_t headerLength = (data[i + 6]) | (data[i + 7] << 8);
+ uint32_t streamType = (data[i + 8]) | (data[i + 9] << 8)
+ | (data[i + 10] << 16) | (data[i + 11] << 24);
+ uint16_t width = (data[i + 12] ) | (data[i + 13] << 8);
+ uint16_t height = (data[i + 14]) | (data[i + 15] << 8);
+ uint32_t interval = (data[i + 16]) | (data[i + 17] << 8)
+ | (data[i + 18] << 16) | (data[i + 19] << 24);
+ uint16_t delay = (data[i + 20]) | (data[i + 21] << 8);
+ uint32_t presentationTime = (data[i + 22]) | (data[i + 23] << 8)
+ | (data[i + 24] << 16) | (data[i + 25] << 24);
+ uint32_t payloadSize = (data[i + 26]) | (data[i + 27] << 8)
+ | (data[i + 28] << 16) | (data[i + 29] << 24);
+
+ if (version != 0x0100) {
+ // バージョンが不正
+ LOGE("ext_h264_processing_frame: Not support version: 0x%02X", version);
+ return;
+ }
+
+ // セグメントサイズを計算
+ // Segment Length = Length (2byte) + Header (22byte) + Payload Size (4byte)
+ uint32_t segment_length = len - (2 + headerLength + 4);
+
+ // バッファサイズがペイロードよりも小さい場合には、リサイズします。
+ if (context->buffer->length < payloadSize) {
+ context->buffer = (struct ext_h264_buffer * ) calloc(1,
+ sizeof(struct ext_h264_buffer) + payloadSize);
+ context->buffer->length = payloadSize;
+ }
+
+ context->buffer->payload_size = payloadSize;
+ context->buffer->got_bytes = 0;
+
+ memcpy(&context->buffer->bytes[context->buffer->got_bytes], &data[i + 30], segment_length);
+ context->buffer->got_bytes += segment_length;
+ } else {
+ // セグメントサイズを計算
+ // Segment Length = Length (2byte)
+ uint32_t segmentLength = len - 2;
+
+ memcpy(&context->buffer->bytes[context->buffer->got_bytes], &data[i + 4], segmentLength);
+ context->buffer->got_bytes += segmentLength;
+ }
+
+ if (context->buffer->got_bytes >= context->buffer->payload_size) {
+ context->buffer->got_bytes = 0;
+ send_frame(context, context->buffer->bytes, context->buffer->payload_size, frame->pts, UVC_FALSE);
+ }
+ } break;
+ case SOS: // Start Of Scan
+ // SOS は、イメージデータの先頭に入っています。
+ // これより後ろには、APP 4 Maker は存在しないので、終了します。
+ return;
+ default: // Other
+ break;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * uvc からのフレームバッファの通知を受け取るコールバック関数.
+ *
+ * @param handle uvc を操作するハンドル
+ * @param frame フレームバッファ
+ */
+static void callback_stream_frame(void *user, struct uvc_frame *frame) {
+ struct user_context *context = (struct user_context *) user;
+ if (context) {
+ switch (frame->type) {
+ case VS_FRAME_UNCOMPRESSED:
+ uncompressed_processing_frame(context, frame);
+ break;
+
+ case VS_FRAME_MJPEG:
+ if (context->use_ext_h264) {
+ ext_h264_processing_frame(context, frame);
+ } else {
+ mjpeg_processing_frame(context, frame);
+ }
+ break;
+
+ case VS_FRAME_H264:
+ h264_processing_frame(context, frame);
+ break;
+
+ default:
+ LOGE("@@@ callback_stream_frame: Unknown frame type.");
+ break;
+ }
+ }
+}
+
+
+/**
+ * Frame Descriptor のデータを Java の Parameter クラスに変換します.
+ *
+ * @param env Javaの環境
+ * @param format フォーマット
+ * @param frame フレーム
+ * @param has_ext_h264 Extension Unit が存在する場合はtrue、それ以外はfalse
+ * @return Parameterクラスのインスタンス
+ */
+static jobject create_uvc_parameter(JNIEnv *env, struct uvc_vs_format_descriptor *format, struct uvc_vs_frame_descriptor *frame, jboolean has_ext_h264) {
+ jclass clazz = env->FindClass("org/deviceconnect/android/libuvc/Parameter");
+ jmethodID mid = env->GetMethodID(clazz, "","()V");
+ jmethodID putMethodID = env->GetMethodID(clazz, "putExtra", "(Ljava/lang/String;J)V");
+ jfieldID formatIndexId = env->GetFieldID(clazz, "mFormatIndex", "I");
+ jfieldID frameTypeId = env->GetFieldID(clazz, "mFrameType", "I");
+ jfieldID frameIndexId = env->GetFieldID(clazz, "mFrameIndex", "I");
+ jfieldID widthId = env->GetFieldID(clazz, "mWidth", "I");
+ jfieldID heightId = env->GetFieldID(clazz, "mHeight", "I");
+ jfieldID fpsId = env->GetFieldID(clazz, "mFps", "I");
+ jfieldID fpsListId = env->GetFieldID(clazz, "mFpsList", "[I");
+
+ jobject obj = env->NewObject(clazz, mid);
+ env->SetIntField(obj, formatIndexId, (jint) format->bFormatIndex);
+ env->SetIntField(obj, frameTypeId, (jint) frame->bDescriptorSubType);
+ env->SetIntField(obj, frameIndexId, (jint) frame->bFrameIndex);
+ env->SetIntField(obj, widthId, (jint) frame->wWidth);
+ env->SetIntField(obj, heightId, (jint) frame->wHeight);
+
+ uint32_t defaultFps = 0;
+ uint32_t *fps = NULL;
+ uint32_t length = 0;
+ if (uvc_get_fps_list(frame, &fps, &length, &defaultFps) == UVC_SUCCESS) {
+ jintArray fpsArray = env->NewIntArray(length);
+ env->SetObjectField(obj, fpsListId, fpsArray);
+ env->DeleteLocalRef(fpsArray);
+ SAFE_FREE(fps);
+
+ env->SetIntField(obj, fpsId, (jint) defaultFps);
+ }
+
+ // H264 の場合は wProfile と bLevelIDC の値は Java 側に通知しておく。
+ switch (frame->bDescriptorSubType) {
+ case VS_FRAME_H264: {
+ struct uvc_vs_frame_h264_descriptor *f = (struct uvc_vs_frame_h264_descriptor *) frame;
+
+ const char *wProfileStr = "wProfile";
+ const char *bLevelIDCStr = "bLevelIDC";
+ const char *dwMaxBitRateStr = "dwMaxBitRate";
+
+ jstring wProfile = env->NewStringUTF(wProfileStr);
+ env->CallVoidMethod(obj, putMethodID, wProfile, (jlong) f->wProfie);
+ env->DeleteLocalRef(wProfile);
+
+ jstring bLevelIDC = env->NewStringUTF(bLevelIDCStr);
+ env->CallVoidMethod(obj, putMethodID, bLevelIDC, (jlong) f->bLevelIDC);
+ env->DeleteLocalRef(bLevelIDC);
+
+ jstring dwMaxBitRate = env->NewStringUTF(dwMaxBitRateStr);
+ env->CallVoidMethod(obj, putMethodID, dwMaxBitRate, (jlong) f->dwMaxBitRate);
+ env->DeleteLocalRef(dwMaxBitRate);
+ } break;
+
+ case VS_FRAME_UNCOMPRESSED: {
+ const char *guidStr = "guid";
+ jstring guid = env->NewStringUTF(guidStr);
+ env->CallVoidMethod(obj, putMethodID, guid, (jlong) uvc_get_uncompressed_format(format));
+ env->DeleteLocalRef(guid);
+ } break;
+
+ case VS_FRAME_MJPEG: {
+ if (has_ext_h264) {
+ const char *extensionStr = "h264";
+ jstring extension = env->NewStringUTF(extensionStr);
+ env->CallVoidMethod(obj, putMethodID, extension, (jlong) 1);
+ env->DeleteLocalRef(extension);
+ }
+ } break;
+
+ default:
+ // TODO 他のタイプで必要なパラメータがあれば、ここで通知すること。
+ break;
+ }
+
+ env->DeleteLocalRef(clazz);
+ return obj;
+}
+
+
+/**
+ * bmControls のcontrol目のビットを確認してサポート状況を確認します.
+ *
+ * @param bmControls ディスクリプタから取得したbmControls
+ * @param control 何ビットめか
+ * @return サポートしている場合はJNI_TRUE、それ以外はJNI_FALSE
+ */
+static jboolean is_supported_control(uint8_t bmControls, int control) {
+ return (bmControls & (1 << control)) != 0 ? (jboolean) JNI_TRUE : (jboolean) JNI_FALSE;
+}
+
+
+/**
+ * Parameter クラスのメソッドIDなどの初期化を行います.
+ *
+ * @param env Java環境クラス
+ * @param context 値を格納するコンテキスト
+ * @return 成功した場合にはUVC_SUCCESS、失敗した場合にはUVC_ERROR
+ */
+static uvc_result java_parameter_init(JNIEnv *env, struct user_context *context) {
+ jclass paramClass = env->FindClass("org/deviceconnect/android/libuvc/Parameter");
+ if (paramClass == NULL) {
+ return UVC_ERROR;
+ }
+
+ context->parameter.formatIndexId_field_id = env->GetFieldID(paramClass, "mFormatIndex", "I");
+ context->parameter.frameIndexId_field_id = env->GetFieldID(paramClass, "mFrameIndex", "I");
+ context->parameter.fpsId_field_id = env->GetFieldID(paramClass, "mFps", "I");
+
+ env->DeleteLocalRef(paramClass);
+
+ return (context->parameter.formatIndexId_field_id &&
+ context->parameter.frameIndexId_field_id &&
+ context->parameter.fpsId_field_id) ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+/**
+ * UVCCamera クラスのメソッドIDなどの初期化を行います.
+ *
+ * @param env Java環境クラス
+ * @param context 値を格納するコンテキスト
+ * @return 成功した場合にはUVC_SUCCESS、失敗した場合にはUVC_ERROR
+ */
+static uvc_result java_uvc_camera_init(JNIEnv *env, struct user_context *context) {
+ jclass uvcCamClass = env->FindClass("org/deviceconnect/android/libuvc/UVCCamera");
+ if (uvcCamClass == NULL) {
+ return UVC_ERROR;
+ }
+
+ context->uvc.onFrame_method_id = env->GetMethodID(uvcCamClass, "onFrame", "(IIJ)V");
+ context->uvc.getFrame_method_id = env->GetMethodID(uvcCamClass, "getFrame", "(I)Lorg/deviceconnect/android/libuvc/Frame;");
+
+ env->DeleteLocalRef(uvcCamClass);
+
+ return (context->uvc.onFrame_method_id && context->uvc.getFrame_method_id) ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+/**
+ * Frame クラスのメソッドIDなどの初期化を行います.
+ *
+ * @param env Java環境クラス
+ * @param context 値を格納するコンテキスト
+ * @return 成功した場合にはUVC_SUCCESS、失敗した場合にはUVC_ERROR
+ */
+static uvc_result java_frame_init(JNIEnv *env, struct user_context *context) {
+ jclass frameClass = env->FindClass("org/deviceconnect/android/libuvc/Frame");
+ if (frameClass == NULL) {
+ return UVC_ERROR;
+ }
+
+ context->frame.getId_method_id = env->GetMethodID(frameClass, "getId", "()I");
+ context->frame.getBuffer_method_id = env->GetMethodID(frameClass, "getBuffer", "()[B");
+ context->frame.resizeBuffer_method_id = env->GetMethodID(frameClass, "resizeBuffer", "(I)V");
+ context->frame.setLength_method_id = env->GetMethodID(frameClass, "setLength", "(I)V");
+ context->frame.parameter_field_id = env->GetFieldID(frameClass, "mParameter", "Lorg/deviceconnect/android/libuvc/Parameter;");
+
+ env->DeleteLocalRef(frameClass);
+
+ return (context->frame.getId_method_id &&
+ context->frame.getBuffer_method_id &&
+ context->frame.resizeBuffer_method_id &&
+ context->frame.setLength_method_id) ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+///////////////////
+
+
+JNIEXPORT jlong JNI_METHOD_NAME(open)(JNIEnv *env, jclass clazz, jint fd) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_open(%d)", fd);
+
+ struct uvc_device_handle *handle = uvc_open_device(fd);
+ return (jlong) handle;
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(getParameter)(JNIEnv *env, jclass clazz, jlong nativePtr, jobject array) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_getParameter(%ld)", nativePtr);
+
+ jclass arrayListClazz = env->FindClass("java/util/ArrayList");
+ if (arrayListClazz == NULL) {
+ return UVC_ERROR;
+ }
+
+ jmethodID addMethodID = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z");
+ if (addMethodID == NULL) {
+ return UVC_ERROR;
+ }
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ struct uvc_video_streaming_interface *streaming_interface = uvc_get_active_streaming_interface(handle);
+ struct uvc_vc_extension_unit_descriptor *h264_extension = uvc_get_active_extension_descriptor(handle);
+
+ while (streaming_interface) {
+ struct uvc_vs_format_descriptor *format = streaming_interface->format;
+ while (format) {
+ struct uvc_vs_frame_descriptor *frame = format->frame;
+ while (frame) {
+ jobject element = create_uvc_parameter(env, format, frame, h264_extension != NULL);
+ if (element) {
+ env->CallBooleanMethod(array, addMethodID, element);
+ env->DeleteLocalRef(element);
+ }
+ frame = frame->next;
+ }
+ format = format->next;
+ }
+ streaming_interface = streaming_interface->next;
+ }
+
+ env->DeleteLocalRef(arrayListClazz);
+
+ return UVC_SUCCESS;
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(getOption)(JNIEnv *env, jclass clazz, jlong nativePtr, jobject option) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_getOption(%ld)", nativePtr);
+
+ jclass optionClazz = env->FindClass("org/deviceconnect/android/libuvc/Option");
+ if (optionClazz == NULL) {
+ return UVC_ERROR;
+ }
+
+ jmethodID putCameraMethodID = env->GetMethodID(optionClazz, "putCameraTerminalControls", "(IZ)V");
+ if (putCameraMethodID == NULL) {
+ env->DeleteLocalRef(optionClazz);
+ return UVC_ERROR;
+ }
+
+ jmethodID putProcessingMethodID = env->GetMethodID(optionClazz, "putProcessingUnitControls", "(IZ)V");
+ if (putProcessingMethodID == NULL) {
+ env->DeleteLocalRef(optionClazz);
+ return UVC_ERROR;
+ }
+
+ // サポート状況を Java 側に通知します。
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ struct uvc_video_control_interface *control = uvc_get_active_control_interface(handle);
+ {
+ // p[x][0]: コントロールを示すID
+ // p[x][1]: バイト数 (bmControlsの何バイト目か)
+ // p[x][2]: ビット数 (何ビット目がサポートフラグになっているか)
+ const static int p[20][3] = {
+ {CT_SCANNING_MODE_CONTROL, 0, 0},
+ {CT_AE_MODE_CONTROL, 0, 1},
+ {CT_AE_PRIORITY_CONTROL, 0, 2},
+ {CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, 0, 3},
+ {CT_EXPOSURE_TIME_RELATIVE_CONTROL, 0, 4},
+ {CT_FOCUS_ABSOLUTE_CONTROL, 0, 5},
+ {CT_FOCUS_RELATIVE_CONTROL, 0, 6},
+ {CT_FOCUS_AUTO_CONTROL, 2, 1},
+ {CT_IRIS_ABSOLUTE_CONTROL, 0, 7},
+ {CT_IRIS_RELATIVE_CONTROL, 1, 0},
+ {CT_ZOOM_ABSOLUTE_CONTROL, 1, 1},
+ {CT_ZOOM_RELATIVE_CONTROL, 1, 2},
+ {CT_PANTILT_ABSOLUTE_CONTROL, 1, 3},
+ {CT_PANTILT_RELATIVE_CONTROL, 1, 4},
+ {CT_ROLL_ABSOLUTE_CONTROL, 1, 5},
+ {CT_ROLL_RELATIVE_CONTROL, 1, 6},
+ {CT_PRIVACY_CONTROL, 2, 2},
+ {CT_FOCUS_SIMPLE_CONTROL, 2, 3},
+ {CT_WINDOW_CONTROL, 2, 4},
+ {CT_REGION_OF_INTEREST_CONTROL, 2, 5},
+ };
+ struct uvc_vc_input_terminal_descriptor *input = control->input_terminal;
+ if (input) {
+ for (int i = 0; i < 20; i++) {
+ jint ctrl = p[i][0];
+ jboolean support = is_supported_control(input->bmControls[p[i][1]], p[i][2]);
+ env->CallVoidMethod(option, putCameraMethodID, ctrl, support);
+ }
+ } else {
+ for (int i = 0; i < 20; i++) {
+ env->CallVoidMethod(option, putCameraMethodID, p[i][0], JNI_FALSE);
+ }
+ }
+ }
+
+ {
+ const static int p[19][3] = {
+ {PU_BACKLIGHT_COMPENSATION_CONTROL, 1, 0},
+ {PU_BRIGHTNESS_CONTROL, 0, 0},
+ {PU_CONTRAST_CONTROL, 0, 1},
+ {PU_GAIN_CONTROL, 1, 1},
+ {PU_POWER_LINE_FREQUENCY_CONTROL, 1, 2},
+ {PU_HUE_CONTROL, 0, 2},
+ {PU_SATURATION_CONTROL, 0, 3},
+ {PU_SHARPNESS_CONTROL, 0, 4},
+ {PU_GAMMA_CONTROL, 0, 5},
+ {PU_WHITE_BALANCE_TEMPERATURE_CONTROL, 0, 6},
+ {PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, 1, 4},
+ {PU_WHITE_BALANCE_COMPONENT_CONTROL, 0, 7},
+ {PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, 1, 5},
+ {PU_DIGITAL_MULTIPLIER_CONTROL, 1, 6},
+ {PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, 1, 7},
+ {PU_HUE_AUTO_CONTROL, 1, 3},
+ {PU_ANALOG_VIDEO_STANDARD_CONTROL, 2, 0},
+ {PU_ANALOG_LOCK_STATUS_CONTROL, 2, 1},
+ {PU_CONTRAST_AUTO_CONTROL, 2, 2},
+ };
+ struct uvc_vc_processing_unit_descriptor *processing = control->processing;
+ if (processing) {
+ for (int i = 0; i < 19; i++) {
+ jint ctrl = p[i][0];
+ jboolean support = is_supported_control(processing->bmControls[p[i][1]], p[i][2]);
+ env->CallVoidMethod(option, putProcessingMethodID, ctrl, support);
+ }
+ } else {
+ for (int i = 0; i < 19; i++) {
+ env->CallVoidMethod(option, putProcessingMethodID, p[i][0], JNI_FALSE);
+ }
+ }
+ }
+
+ env->DeleteLocalRef(optionClazz);
+
+ return UVC_SUCCESS;
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(startVideo)(JNIEnv *env, jclass clazz, jlong nativePtr, jint formatIndex, jint frameIndex, jint fps, jboolean useH264) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_startVideo(%ld, %d, %d, %d)", nativePtr, formatIndex, frameIndex, useH264);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ handle->use_ext_h264 = useH264;
+ return uvc_start_video(handle, (uint8_t) formatIndex, (uint8_t) frameIndex, (uint32_t) fps,
+ useH264);
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(stopVideo)(JNIEnv *env, jclass clazz, jlong nativePtr) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_stopVideo(%ld)", nativePtr);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ return uvc_stop_video(handle);
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(captureStillImage)(JNIEnv *env, jclass clazz, jlong nativePtr, jobject frame) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_captureStillImage(%ld)", nativePtr);
+
+ uvc_result result = UVC_ERROR;
+ uint8_t formatIndex;
+ uint8_t frameIndex;
+ uint32_t fps;
+ jobject paramObj;
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ if (handle->running == UVC_VIDEO_RUNNING) {
+ return UVC_ERROR;
+ }
+
+ struct user_context *context = (struct user_context *) calloc(1, sizeof(struct user_context));
+ if (context == NULL) {
+ return UVC_ERROR;
+ }
+
+ context->handle = handle;
+ context->count = 0;
+ context->env = env;
+ context->obj = env->NewGlobalRef(frame);
+ if (context->obj == NULL) {
+ goto END;
+ }
+
+ if (java_parameter_init(env, context) != UVC_SUCCESS) {
+ goto END;
+ }
+
+ if (java_frame_init(env, context) != UVC_SUCCESS) {
+ goto END;
+ }
+
+ handle->user = context;
+ handle->callback = callback_still_frame;
+
+ paramObj = env->GetObjectField(frame, context->frame.parameter_field_id);
+
+ formatIndex = (uint8_t) env->GetIntField(paramObj, context->parameter.formatIndexId_field_id);
+ frameIndex = (uint8_t) env->GetIntField(paramObj, context->parameter.frameIndexId_field_id);
+ fps = (uint32_t) env->GetIntField(paramObj, context->parameter.fpsId_field_id);
+
+ switch (uvc_get_still_capture_method(handle)) {
+ default:
+ case METHOD_0:
+ case METHOD_1:
+ // method が 0,1 の場合には静止画に対応していない。
+ // ここでは、プレビューを1枚だけ取得して、静止画として処理を行う
+ result = uvc_start_video(handle, formatIndex, frameIndex, fps, 0);
+ if (result == UVC_SUCCESS) {
+ result = uvc_handle_event(handle);
+ }
+ break;
+
+ case METHOD_2:
+ case METHOD_3:
+ result = uvc_capture_still_image(handle, formatIndex, frameIndex, 0);
+ if (result == UVC_SUCCESS) {
+ result = uvc_handle_event(handle);
+ }
+ break;
+ }
+
+END:
+ if (context->obj) {
+ env->DeleteGlobalRef(context->obj);
+ }
+ SAFE_FREE(context);
+ handle->user = NULL;
+
+ return result;
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(close)(JNIEnv *env, jclass clazz, jlong nativePtr) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_close(%ld)", nativePtr);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ uvc_close_device(handle);
+ return UVC_SUCCESS;
+}
+
+JNIEXPORT jint JNI_METHOD_NAME(handleEvent)(JNIEnv *env, jclass clazz, jlong nativePtr, jobject obj) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_handleEvent(%ld)", nativePtr);
+
+ uvc_result result = UVC_ERROR;
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+
+ if (handle->running == UVC_VIDEO_STOP) {
+ return UVC_ERROR;
+ }
+
+ struct user_context *context = (struct user_context *) calloc(1, sizeof(struct user_context));
+ if (context == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+
+ if (handle->use_ext_h264) {
+ context->buffer = (struct ext_h264_buffer *) calloc(1, sizeof(struct ext_h264_buffer) + 4096);
+ if (context->buffer == NULL) {
+ SAFE_FREE(context)
+ return UVC_OUT_OF_MEMORY;
+ }
+ context->buffer->got_bytes = 0;
+ context->buffer->payload_size = 0;
+ context->buffer->length = 4096;
+ context->use_ext_h264 = UVC_TRUE;
+ }
+
+ context->env = env;
+ context->obj = env->NewGlobalRef(obj);
+ if (context->obj == NULL) {
+ goto END;
+ }
+
+ if (java_uvc_camera_init(env, context) != UVC_SUCCESS) {
+ goto END;
+ }
+
+ if (java_frame_init(env, context) != UVC_SUCCESS) {
+ goto END;
+ }
+
+ handle->user = context;
+ handle->callback = callback_stream_frame;
+
+ result = uvc_handle_event(handle);
+
+END:
+ if (context->obj) {
+ env->DeleteGlobalRef(context->obj);
+ }
+ if (context->buffer) {
+ SAFE_FREE(context->buffer);
+ }
+ SAFE_FREE(context);
+ handle->user = NULL;
+
+ return result;
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(isRunning)(JNIEnv *env, jclass clazz, jlong nativePtr) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_isRunning(%ld)", nativePtr);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ return handle->running;
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(detachInterface)(JNIEnv *env, jclass clazz, jlong nativePtr, jint interface) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_detachInterface(%ld, %d)", nativePtr, interface);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ return uvc_disconnect_interface(handle, (unsigned int) interface);
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(setConfig)(JNIEnv *env, jclass clazz, jlong nativePtr, jint configId) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_setConfig(%ld)", nativePtr);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ return uvc_set_configuration(handle, (unsigned int) configId);
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(getStillCaptureMethod)(JNIEnv *env, jclass clazz, jlong nativePtr) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_getStillCaptureMethod(%ld)", nativePtr);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ return uvc_get_still_capture_method(handle);
+}
+
+
+JNIEXPORT jint JNI_METHOD_NAME(applyControl)(JNIEnv *env, jclass clazz, jlong nativePtr, jint type, jint control, jint request, jbyteArray valueArray) {
+ LOGI("Java_org_deviceconnect_android_libuvc_UVCCameraNative_applyControl(%ld, %d, %d)", nativePtr, control, request);
+
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) nativePtr;
+ if (handle == NULL) {
+ return UVC_PARAMETER_INVALID;
+ }
+
+ jboolean isCopy;
+ uint8_t *buf = (uint8_t *) env->GetByteArrayElements(valueArray, &isCopy);
+ if (buf == NULL) {
+ return UVC_PARAMETER_INVALID;
+ }
+
+ int capacity = env->GetArrayLength(valueArray);
+ if (capacity <= 0) {
+ env->ReleaseByteArrayElements(valueArray, (jbyte *) buf, 0);
+ return UVC_PARAMETER_INVALID;
+ }
+
+ uvc_result result = UVC_ERROR;
+ switch (request) {
+ case SET_CUR: {
+ switch (type) {
+ case TYPE_CT:
+ result = uvc_set_camera_terminal_control(handle, control, buf, capacity);
+ break;
+ case TYPE_PU:
+ result = uvc_set_processing_unit_control(handle, control, buf, capacity);
+ break;
+ case TYPE_EU:
+ result = uvc_set_encoding_unit_control(handle, control, buf, capacity);
+ break;
+ default:
+ break;
+ }
+ } break;
+
+ case GET_MIN:
+ case GET_MAX:
+ case GET_DEF:
+ case GET_CUR:
+ default: {
+ switch (type) {
+ case TYPE_CT:
+ result = uvc_get_camera_terminal_control(handle, control, request, buf, capacity);
+ break;
+ case TYPE_PU:
+ result = uvc_get_processing_unit_control(handle, control, request, buf, capacity);
+ break;
+ case TYPE_EU:
+ result = uvc_get_encoding_unit_control(handle, control, request, buf, capacity);
+ break;
+ default:
+ break;
+ }
+ } break;
+ }
+
+ env->ReleaseByteArrayElements(valueArray, (jbyte *) buf, 0);
+
+ return result;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc.c b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc.c
new file mode 100644
index 0000000000..a99e3cd15d
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc.c
@@ -0,0 +1,1857 @@
+/*
+ uvc.c
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "uvc.h"
+
+#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8))
+
+#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
+
+#define QW_TO_LONG(p) \
+ ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24) \
+ | ((uint64_t)(p)[4] << 32) | ((uint64_t)(p)[5] << 40) \
+ | ((uint64_t)(p)[6] << 48) | ((uint64_t)(p)[7] << 56))
+
+
+#define BYTE_TO_BW(s, p) (p)[0] = (s);
+
+#define SHORT_TO_SW(s, p) \
+ (p)[0] = ((s) & 0xFF); \
+ (p)[1] = (((s) >> 8) & 0xFF);
+
+#define INT_TO_DW(i, p) \
+ (p)[0] = (i); \
+ (p)[1] = (i) >> 8; \
+ (p)[2] = (i) >> 16; \
+ (p)[3] = (i) >> 24;
+
+#define LONG_TO_QW(i, p) \
+ (p)[0] = (i); \
+ (p)[1] = (i) >> 8; \
+ (p)[2] = (i) >> 16; \
+ (p)[3] = (i) >> 24; \
+ (p)[4] = (i) >> 32; \
+ (p)[5] = (i) >> 40; \
+ (p)[6] = (i) >> 48; \
+ (p)[7] = (i) >> 56;
+
+
+#define UVC_DRIVER_NAME "usbfs"
+
+#define REQ_TYPE_SET 0x21
+#define REQ_TYPE_GET 0xA1
+
+
+static inline int xioctl(int fd, int request, void *arg) {
+ int r;
+
+ do {
+ r = ioctl(fd, request, arg);
+ } while (-1 == r && EINTR == errno);
+
+ return r;
+}
+
+static uint16_t uvc_get_active_version(struct uvc_device_handle *handle) {
+ return uvc_get_uvc_version(&handle->descriptor, handle->active_config);
+}
+
+static uint8_t uvc_get_video_control_interface_number(struct uvc_device_handle *handle) {
+ return uvc_get_active_control_interface(handle)->interface->bInterfaceNumber;
+}
+
+static uint8_t uvc_get_video_streaming_interface_number(struct uvc_device_handle *handle) {
+ return uvc_get_active_streaming_interface(handle)->interface->bInterfaceNumber;
+}
+
+static uvc_result uvc_get_configuration(struct uvc_device_handle *handle, uint8_t *active_config) {
+ struct usbdevfs_ctrltransfer ctrl = {
+ .bRequestType = 0x80,
+ .bRequest = GET_CONFIGURATION,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = 2,
+ .timeout = 1000,
+ .data = active_config
+ };
+ return xioctl(handle->fd, USBDEVFS_CONTROL, &ctrl) == 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_get_capabilities(struct uvc_device_handle *handle, uint32_t *caps) {
+ return xioctl(handle->fd, USBDEVFS_GET_CAPABILITIES, caps) == 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_detach_kernel_driver(struct uvc_device_handle *handle, uint8_t interface) {
+ struct usbdevfs_getdriver getdrv;
+ getdrv.interface = interface;
+ int r = ioctl(handle->fd, USBDEVFS_GETDRIVER, &getdrv);
+ if (r == 0 && strcmp(getdrv.driver, UVC_DRIVER_NAME) == 0) {
+ return UVC_SUCCESS;
+ }
+
+ struct usbdevfs_ioctl command;
+ command.ifno = interface;
+ command.ioctl_code = USBDEVFS_DISCONNECT;
+ command.data = NULL;
+ return xioctl(handle->fd, USBDEVFS_IOCTL, &command) == 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_attach_kernel_driver(struct uvc_device_handle *handle, uint8_t interface) {
+ struct usbdevfs_ioctl command;
+ command.ifno = interface;
+ command.ioctl_code = USBDEVFS_CONNECT;
+ command.data = NULL;
+ return xioctl(handle->fd, USBDEVFS_IOCTL, &command) == 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_claim_interface(struct uvc_device_handle *handle, uint8_t interface) {
+ return xioctl(handle->fd, USBDEVFS_CLAIMINTERFACE, &interface) == 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_release_interface(struct uvc_device_handle *handle, uint8_t interface) {
+ return xioctl(handle->fd, USBDEVFS_RELEASEINTERFACE, &interface) == 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_detach_kernel_driver_and_claim(struct uvc_device_handle *handle, uint8_t interface) {
+ struct usbdevfs_disconnect_claim dc;
+ int r;
+
+ dc.interface = interface;
+ strcpy(dc.driver, UVC_DRIVER_NAME);
+ dc.flags = USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER;
+ r = ioctl(handle->fd, USBDEVFS_DISCONNECT_CLAIM, &dc);
+ if (r == 0 || errno != ENOTTY) {
+ return UVC_SUCCESS;
+ }
+
+ r = uvc_detach_kernel_driver(handle, interface);
+ if (r != 0) {
+ return UVC_ERROR;
+ }
+ return uvc_claim_interface(handle, interface);
+}
+
+static uvc_result uvc_attach_kernel_driver_and_release(struct uvc_device_handle *handle, uint8_t interface) {
+ int r = uvc_release_interface(handle, interface);
+ if (r < 0) {
+ return UVC_ERROR;
+ }
+ uvc_attach_kernel_driver(handle, interface);
+ return UVC_SUCCESS;
+}
+
+static uvc_result uvc_set_interface_alt_setting(struct uvc_device_handle *handle, struct uvc_video_streaming_altsetting *altsetting) {
+ struct usbdevfs_setinterface setintf;
+ setintf.interface = altsetting->interface->bInterfaceNumber;
+ setintf.altsetting = altsetting->interface->bAlternateSetting;
+ return ioctl(handle->fd, USBDEVFS_SETINTERFACE, &setintf) == 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static int uvc_control_transfer(struct uvc_device_handle *handle, uint8_t requestType, uint8_t request, uint16_t wValue, uint16_t wIndex, uint16_t wLength, void *data) {
+ struct usbdevfs_ctrltransfer ctrl = {
+ .bRequestType = requestType,
+ .bRequest = request,
+ .wValue = __le16_to_cpu(wValue),
+ .wIndex = __le16_to_cpu(wIndex),
+ .wLength = __le16_to_cpu(wLength),
+ .timeout = 1000,
+ .data = data
+ };
+ return xioctl(handle->fd, USBDEVFS_CONTROL, &ctrl);
+}
+
+
+///////// Video Probe and Commit /////////////
+
+
+static size_t uvc_video_ctrl_size(struct uvc_device_handle *handle) {
+ uint16_t bcdUVC = uvc_get_active_version(handle);
+ if (bcdUVC >= 0x0150) {
+ return 48;
+ } else if (bcdUVC >= 0x0110) {
+ return 34;
+ } else {
+ return 26;
+ }
+}
+
+
+static uvc_result uvc_get_video_control(struct uvc_device_handle *handle, struct uvc_video_control *video_control, uint8_t request, uint16_t wValue) {
+ uint8_t buf[48];
+ uint16_t wIndex = handle->bInterfaceNumber;
+ uint16_t wLength = uvc_video_ctrl_size(handle);
+
+ int r = uvc_control_transfer(handle, REQ_TYPE_GET, request, wValue, wIndex, wLength, buf);
+ if (r > 0) {
+ video_control->bmHint = SW_TO_SHORT(buf);
+ video_control->bFormatIndex = buf[2];
+ video_control->bFrameIndex = buf[3];
+ video_control->dwFrameInterval = DW_TO_INT(buf + 4);
+ video_control->wKeyFrameRate = SW_TO_SHORT(buf + 8);
+ video_control->wPFrameRate = SW_TO_SHORT(buf + 10);
+ video_control->wCompQuality = SW_TO_SHORT(buf + 12);
+ video_control->wCompWindowSize = SW_TO_SHORT(buf + 14);
+ video_control->wDelay = SW_TO_SHORT(buf + 16);
+ video_control->dwMaxVideoFrameSize = DW_TO_INT(buf + 18);
+ video_control->dwMaxPayloadTransferSize = DW_TO_INT(buf + 22);
+ if (r > 26) {
+ video_control->dwClockFrequency = DW_TO_INT(buf + 26);
+ video_control->bmFramingInfo = buf[30];
+ video_control->bPreferedVersion = buf[31];
+ video_control->bMinVersion = buf[32];
+ video_control->bMaxVersion = buf[33];
+ if (r > 34) {
+ video_control->bUsage = buf[34];
+ video_control->bBitDepthLuma = buf[35];
+ video_control->bmSettings = buf[36];
+ video_control->bMaxNumberOfRefFramesPlus1 = buf[37];
+ video_control->bmRateControlModes = SW_TO_SHORT(buf + 38);
+ video_control->bmLayoutPerStream = QW_TO_LONG(buf + 40);
+ }
+ }
+ }
+
+ return r > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+static uvc_result uvc_set_video_control(struct uvc_device_handle *handle, struct uvc_video_control *video_control, uint16_t wValue) {
+ uint8_t buf[48];
+ uint16_t wIndex = handle->bInterfaceNumber;
+ uint16_t wLength = uvc_video_ctrl_size(handle);
+
+ SHORT_TO_SW(video_control->bmHint, buf);
+ BYTE_TO_BW(video_control->bFormatIndex, buf + 2);
+ BYTE_TO_BW(video_control->bFrameIndex, buf + 3);
+ INT_TO_DW(video_control->dwFrameInterval, buf + 4);
+ SHORT_TO_SW(video_control->wKeyFrameRate, buf + 8);
+ SHORT_TO_SW(video_control->wPFrameRate, buf + 10);
+ SHORT_TO_SW(video_control->wCompQuality, buf + 12);
+ SHORT_TO_SW(video_control->wCompWindowSize, buf + 14);
+ SHORT_TO_SW(video_control->wDelay, buf + 16);
+ INT_TO_DW(video_control->dwMaxVideoFrameSize, buf + 18);
+ INT_TO_DW(video_control->dwMaxPayloadTransferSize, buf + 22);
+ if (wLength > 26) {
+ INT_TO_DW(video_control->dwClockFrequency, buf + 26);
+ BYTE_TO_BW(video_control->bmFramingInfo, buf + 30);
+ BYTE_TO_BW(video_control->bPreferedVersion, buf + 31);
+ BYTE_TO_BW(video_control->bMinVersion, buf + 32);
+ BYTE_TO_BW(video_control->bMaxVersion, buf + 33);
+ if (wLength > 34) {
+ BYTE_TO_BW(video_control->bUsage, buf + 34);
+ BYTE_TO_BW(video_control->bBitDepthLuma, buf + 35);
+ BYTE_TO_BW(video_control->bmSettings, buf + 36);
+ BYTE_TO_BW(video_control->bMaxNumberOfRefFramesPlus1, buf + 37);
+ SHORT_TO_SW(video_control->bmRateControlModes, buf + 38);
+ LONG_TO_QW(video_control->bmLayoutPerStream, buf + 40);
+ }
+ }
+
+ return uvc_control_transfer(handle, REQ_TYPE_SET, SET_CUR, wValue, wIndex, wLength, buf) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+static size_t uvc_len_video_control(struct uvc_device_handle *handle, uint16_t wValue) {
+ uint8_t buf[2];
+ uint16_t wIndex = handle->bInterfaceNumber;
+ uint16_t wLength = 2;
+
+ int r = uvc_control_transfer(handle, REQ_TYPE_GET, GET_LEN, wValue, wIndex, wLength, buf);
+ if (r > 0) {
+ return SW_TO_SHORT(buf);
+ } else {
+ return r;
+ }
+}
+
+static uvc_result uvc_get_probe_video_control(struct uvc_device_handle *handle, struct uvc_video_control *video_control) {
+ return uvc_get_video_control(handle, video_control, GET_CUR, VS_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_get_max_probe_video_control(struct uvc_device_handle *handle, struct uvc_video_control *video_control) {
+ return uvc_get_video_control(handle, video_control, GET_MAX, VS_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_get_min_probe_video_control(struct uvc_device_handle *handle, struct uvc_video_control *video_control) {
+ return uvc_get_video_control(handle, video_control, GET_MIN, VS_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_set_probe_video_control(struct uvc_device_handle *handle, struct uvc_video_control *video_control) {
+ return uvc_set_video_control(handle, video_control, VS_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_set_commit_video_control(struct uvc_device_handle *handle, struct uvc_video_control *video_control) {
+ return uvc_set_video_control(handle, video_control, VS_COMMIT_CONTROL << 8);
+}
+
+static size_t uvc_get_len_probe_video_control(struct uvc_device_handle *handle) {
+ return uvc_len_video_control(handle, VS_PROBE_CONTROL << 8);
+}
+
+
+static uvc_result uvc_get_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config, uint8_t request, uint16_t wValue) {
+ struct uvc_vc_extension_unit_descriptor *extension = uvc_find_extension_descriptor(&handle->descriptor, handle->active_config);
+ uint8_t buf[46];
+ uint16_t wIndex = (extension->bUnitID << 8) | handle->bInterfaceNumber;
+ uint16_t wLength = 46;
+
+ int r = uvc_control_transfer(handle, REQ_TYPE_GET, request, wValue, wIndex, wLength, buf);
+ if (r > 0) {
+ config->dwFrameInterval = DW_TO_INT(buf);
+ config->dwBitRate = DW_TO_INT(buf + 4);
+ config->bmHints = SW_TO_SHORT(buf + 8);
+ config->wConfigurationIndex = SW_TO_SHORT(buf + 10);
+ config->wWidth = SW_TO_SHORT(buf + 12);
+ config->wHeight = SW_TO_SHORT(buf + 14);
+ config->wSliceUnits = SW_TO_SHORT(buf + 16);
+ config->wSliceMode = SW_TO_SHORT(buf + 18);
+ config->wProfile = SW_TO_SHORT(buf + 20);
+ config->wIFramePeriod = SW_TO_SHORT(buf + 22);
+ config->wEstimatedVideoDelay = SW_TO_SHORT(buf + 24);
+ config->wEstimatedMaxConfigDelay = SW_TO_SHORT(buf + 26);
+ config->bUsageType = buf[28];
+ config->bRateControlMode = buf[29];
+ config->bTemporalScaleMode = buf[30];
+ config->bSpatialScaleMode = buf[31];
+ config->bSNRScaleMode = buf[32];
+ config->bStreamMuxOption = buf[33];
+ config->bStreamFormat = buf[34];
+ config->bEntropyCABAC = buf[35];
+ config->bTimestamp = buf[36];
+ config->bNumOfReorderFrames = buf[37];
+ config->bPreviewFlipped = buf[38];
+ config->bView = buf[39];
+ config->bReserved1 = buf[40];
+ config->bReserved2 = buf[41];
+ config->bStreamID = buf[42];
+ config->bSpatialLayerRatio = buf[43];
+ config->wLeakyBucketSize = SW_TO_SHORT(buf + 44);
+ }
+ return r > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+static uvc_result uvc_set_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config, uint16_t wValue) {
+ struct uvc_vc_extension_unit_descriptor *extension = uvc_find_extension_descriptor(&handle->descriptor, handle->active_config);
+ uint8_t buf[46];
+ uint16_t wIndex = (extension->bUnitID << 8) | handle->bInterfaceNumber;
+ uint16_t wLength = 46;
+
+ INT_TO_DW(config->dwFrameInterval, buf)
+ INT_TO_DW(config->dwBitRate, buf + 4)
+ SHORT_TO_SW(config->bmHints, buf + 8)
+ SHORT_TO_SW(config->wConfigurationIndex, buf + 10)
+ SHORT_TO_SW(config->wWidth, buf + 12)
+ SHORT_TO_SW(config->wHeight, buf + 14)
+ SHORT_TO_SW(config->wSliceUnits, buf + 16)
+ SHORT_TO_SW(config->wSliceMode, buf + 18)
+ SHORT_TO_SW(config->wProfile, buf + 20)
+ SHORT_TO_SW(config->wIFramePeriod, buf + 22)
+ SHORT_TO_SW(config->wEstimatedVideoDelay, buf + 24)
+ SHORT_TO_SW(config->wEstimatedMaxConfigDelay, buf + 26)
+ BYTE_TO_BW(config->bUsageType, buf + 28)
+ BYTE_TO_BW(config->bRateControlMode, buf + 29)
+ BYTE_TO_BW(config->bTemporalScaleMode, buf + 30)
+ BYTE_TO_BW(config->bSpatialScaleMode, buf + 31)
+ BYTE_TO_BW(config->bSNRScaleMode, buf + 32)
+ BYTE_TO_BW(config->bStreamMuxOption, buf + 33)
+ BYTE_TO_BW(config->bStreamFormat, buf + 34)
+ BYTE_TO_BW(config->bEntropyCABAC, buf + 35)
+ BYTE_TO_BW(config->bTimestamp, buf + 36)
+ BYTE_TO_BW(config->bNumOfReorderFrames, buf + 37)
+ BYTE_TO_BW(config->bPreviewFlipped, buf + 38)
+ BYTE_TO_BW(config->bView, buf + 39)
+ BYTE_TO_BW(config->bReserved1, buf + 40)
+ BYTE_TO_BW(config->bReserved2, buf + 41)
+ BYTE_TO_BW(config->bStreamID, buf + 42)
+ BYTE_TO_BW(config->bSpatialLayerRatio, buf + 43)
+ SHORT_TO_SW(config->wLeakyBucketSize, buf + 44)
+
+ return uvc_control_transfer(handle, REQ_TYPE_SET, SET_CUR, wValue, wIndex, wLength, buf) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_get_probe_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config) {
+ return uvc_get_h264_extension(handle, config, GET_CUR, UVCX_VIDEO_CONFIG_PROBE << 8);
+}
+
+static uvc_result uvc_get_max_probe_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config) {
+ return uvc_get_h264_extension(handle, config, GET_MAX, UVCX_VIDEO_CONFIG_PROBE << 8);
+}
+
+static uvc_result uvc_get_min_probe_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config) {
+ return uvc_get_h264_extension(handle, config, GET_MIN, UVCX_VIDEO_CONFIG_PROBE << 8);
+}
+
+static uvc_result uvc_get_def_probe_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config) {
+ return uvc_get_h264_extension(handle, config, GET_DEF, UVCX_VIDEO_CONFIG_PROBE << 8);
+}
+
+static uvc_result uvc_set_probe_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config) {
+ return uvc_set_h264_extension(handle, config, UVCX_VIDEO_CONFIG_PROBE << 8);
+}
+
+static uvc_result uvc_commit_probe_h264_extension(struct uvc_device_handle *handle, struct uvc_h264_extension_unit *config) {
+ return uvc_set_h264_extension(handle, config, UVCX_VIDEO_CONFIG_COMMIT << 8);
+}
+
+static size_t uvc_len_probe_h264_extension(struct uvc_device_handle *handle) {
+ struct uvc_vc_extension_unit_descriptor *extension = uvc_find_extension_descriptor(&handle->descriptor, handle->active_config);
+ uint8_t buf[2];
+ uint16_t wIndex = (extension->bUnitID << 8) | handle->bInterfaceNumber;
+ uint16_t wValue = UVCX_VIDEO_CONFIG_PROBE << 8;
+ uint16_t wLength = 2;
+
+ int r = uvc_control_transfer(handle, REQ_TYPE_GET, GET_LEN, wValue, wIndex, wLength, buf);
+ if (r > 0) {
+ return SW_TO_SHORT(buf);
+ } else {
+ return r;
+ }
+}
+
+///////// Still Probe and Commit /////////////
+
+
+static uvc_result uvc_get_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control, uint8_t request, uint16_t wValue) {
+ uint8_t buf[11];
+ uint16_t wIndex = still_control->bInterfaceNumber;
+ uint16_t wLength = 11;
+
+ int r = uvc_control_transfer(handle, REQ_TYPE_GET, request, wValue, wIndex, wLength, buf);
+ if (r > 0) {
+ still_control->bFormatIndex = buf[0];
+ still_control->bFrameIndex = buf[1];
+ still_control->bCompressionIndex = buf[2];
+ still_control->dwMaxVideoFrameSize = DW_TO_INT(buf + 3);
+ still_control->dwMaxPayloadTransferSize = DW_TO_INT(buf + 7);
+ }
+
+ return r > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_set_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control, uint16_t wValue) {
+ uint8_t buf[11];
+ uint16_t wIndex = still_control->bInterfaceNumber;
+ uint16_t wLength = 11;
+
+ BYTE_TO_BW(still_control->bFormatIndex, buf);
+ BYTE_TO_BW(still_control->bFrameIndex, buf + 1);
+ BYTE_TO_BW(still_control->bCompressionIndex, buf + 2);
+ INT_TO_DW(still_control->dwMaxVideoFrameSize, buf + 3);
+ INT_TO_DW(still_control->dwMaxPayloadTransferSize, buf + 7);
+
+ return uvc_control_transfer(handle, REQ_TYPE_SET, SET_CUR, wValue, wIndex, wLength, buf) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+static uvc_result uvc_get_probe_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control) {
+ return uvc_get_still_control(handle, still_control, GET_CUR, VS_STILL_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_get_max_probe_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control) {
+ return uvc_get_still_control(handle, still_control, GET_MAX, VS_STILL_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_get_min_probe_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control) {
+ return uvc_get_still_control(handle, still_control, GET_MIN, VS_STILL_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_get_def_probe_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control) {
+ return uvc_get_still_control(handle, still_control, GET_DEF, VS_STILL_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_set_probe_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control) {
+ return uvc_set_still_control(handle, still_control, VS_STILL_PROBE_CONTROL << 8);
+}
+
+static uvc_result uvc_set_commit_still_control(struct uvc_device_handle *handle, struct uvc_still_control *still_control) {
+ return uvc_set_still_control(handle, still_control, VS_STILL_COMMIT_CONTROL << 8);
+}
+
+
+///////// uvc_frame /////////////
+
+
+/**
+ * UVCから転送されてきたフレームを格納するバッファを作成します.
+ *
+ * @param type フレームバッファのタイプ
+ * @param length バッファサイズ
+ * @return 構造体へのポインタ
+ */
+static struct uvc_frame *uvc_create_frame(uint8_t type, uint32_t length) {
+ struct uvc_frame *frame = (struct uvc_frame *) calloc(1, sizeof(struct uvc_frame) + length);
+ if (frame) {
+ frame->type = type;
+ frame->length = length;
+ }
+ return frame;
+}
+
+/**
+ * UVCから転送されてきたフレームを格納するバッファをリサイズします.
+ *
+ * @param frame リサイズを行うフレームバッファ
+ * @param length バッファサイズ
+ * @return 構造体へのポインタ
+ */
+static struct uvc_frame *uvc_realloc_frame(struct uvc_frame *frame, uint32_t length) {
+ struct uvc_frame *newFrame = (struct uvc_frame *) realloc(frame, sizeof(struct uvc_frame) + length);
+ if (newFrame) {
+ newFrame->length = length;
+ }
+ return newFrame;
+}
+
+///////// uvc_transfer /////////////
+
+/**
+ * uvc_transfer のインスタンスを作成します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param endpoint 転送先のエンドポイント
+ * @param type 送タイプ.
+ * @param length サイズ
+ * @param num_iso_packet アイソクロナス転送用のパケット数.
+ * @param size_iso_packet アイソクロナス転送用のパケットのサイズ.
+ * @return uvc_transfer のインスタンス
+ */
+static struct uvc_transfer *uvc_create_transfer(struct uvc_device_handle *handle, uint8_t endpoint, uint8_t type, uint32_t length, uint8_t num_iso_packet, uint32_t size_iso_packet) {
+ struct uvc_transfer *transfer = (struct uvc_transfer *) calloc(1, sizeof(struct uvc_transfer) + length);
+ if (transfer) {
+ transfer->handle = handle;
+ transfer->endpoint = endpoint;
+ transfer->type = type;
+ transfer->num_iso_packet = num_iso_packet;
+ transfer->size_iso_packet = size_iso_packet;
+ transfer->length = length;
+ }
+ return transfer;
+}
+
+/**
+ * アイソクロナス転送用の USB 要求ブロックをキャンセルします.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param transfer キャンセルするアイソクロナス転送用の要求
+ */
+static void uvc_cancel_iso_transfer(struct uvc_device_handle *handle, struct uvc_transfer *transfer) {
+ if (transfer->urb) {
+ xioctl(handle->fd, USBDEVFS_DISCARDURB, transfer->urb);
+ SAFE_FREE(transfer->urb);
+ }
+}
+
+/**
+ * アイソクロナス転送用の USB 要求ブロックを作成します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param transfer アイソクロナス転送用の要求
+ * @return UVC_SUCCESSの場合はUSB要求の作成に成功、それ以外はUSB要求の作成に失敗
+ */
+static uvc_result uvc_submit_iso_transfer(struct uvc_device_handle *handle, struct uvc_transfer *transfer) {
+ if (transfer->urb == NULL) {
+ transfer->urb = (struct usbdevfs_urb *) calloc(1, sizeof(struct usbdevfs_urb) +
+ transfer->num_iso_packet * sizeof(struct usbdevfs_iso_packet_desc));
+ if (transfer->urb == NULL) {
+ LOGE("@@@@ uvc_submit_iso_transfer: Out of memory.");
+ return UVC_OUT_OF_MEMORY;
+ }
+ }
+
+ memset(transfer->urb, 0x0, sizeof(struct usbdevfs_urb));
+
+ transfer->urb->type = USBDEVFS_URB_TYPE_ISO;
+ transfer->urb->flags = USBDEVFS_URB_ISO_ASAP;
+ transfer->urb->endpoint = transfer->endpoint;
+ transfer->urb->number_of_packets = transfer->num_iso_packet;
+ transfer->urb->buffer = (void *) transfer->buf;
+ transfer->urb->usercontext = transfer;
+ for (int i = 0; i < transfer->num_iso_packet; i++) {
+ transfer->urb->iso_frame_desc[i].length = transfer->size_iso_packet;
+ transfer->urb->iso_frame_desc[i].status = 0;
+ transfer->urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ int r = xioctl(handle->fd, USBDEVFS_SUBMITURB, transfer->urb);
+ if (r < 0) {
+ LOGE("@@@@ uvc_submit_iso_transfer: Failed to ioctl. %d %d", r, errno);
+ SAFE_FREE(transfer->urb);
+ return UVC_ERROR;
+ }
+ return UVC_SUCCESS;
+}
+
+
+/**
+ * バルク転送用の USB 要求ブロックを作成します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param transfer バルク転送用の要求
+ * @return UVC_SUCCESSの場合はUSB要求の作成に成功、それ以外はUSB要求の作成に失敗
+ */
+static uvc_result uvc_submit_bulk_transfer(struct uvc_device_handle *handle, struct uvc_transfer *transfer) {
+ if (handle->running == UVC_VIDEO_STOP) {
+ return UVC_ERROR;
+ }
+
+ if (transfer->urb == NULL) {
+ transfer->urb = (struct usbdevfs_urb *) calloc(1, sizeof(struct usbdevfs_urb));
+ if (transfer->urb == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+ }
+
+ memset(transfer->urb, 0x0, sizeof(struct usbdevfs_urb));
+
+ transfer->urb->type = USBDEVFS_URB_TYPE_BULK;
+ transfer->urb->flags = USBDEVFS_URB_BULK_CONTINUATION;
+ transfer->urb->stream_id = 0;
+ transfer->urb->endpoint = transfer->endpoint;
+ transfer->urb->buffer = (void *) transfer->buf;
+ transfer->urb->buffer_length = transfer->length;
+ transfer->urb->usercontext = transfer;
+
+ int r = ioctl(handle->fd, USBDEVFS_SUBMITURB, transfer->urb);
+ if (r < 0) {
+ LOGE("@@@@ uvc_submit_bulk_transfer: Failed to submit urb. r=%d", r);
+ SAFE_FREE(transfer->urb);
+ return UVC_ERROR;
+ }
+ return UVC_SUCCESS;
+}
+
+
+/**
+ * UVCから転送されてきたフレームバッファを指定されたコールバック関数に通知します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param frame 通知するフレームバッファ
+ */
+static void uvc_notify_frame(struct uvc_device_handle *handle, struct uvc_frame *frame) {
+ if (frame->got_bytes > 0) {
+// LOGD("@@@ uvc_notify_frame: frame[%d].got_bytes=%u length=%u pts=%u stc=%u sof=%u",
+// handle->frame_id, frame->got_bytes, frame->length, frame->pts, frame->stc, frame->sof);
+ handle->callback(handle->user, frame);
+ frame->got_bytes = 0;
+ }
+}
+
+/**
+ * 転送されてきたフレームデータをフレームにコピーします.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param pktbuf パケットデータ
+ * @param actual_length パケットデータサイズ
+ */
+static void uvc_reap_payload(struct uvc_device_handle *handle, struct usbdevfs_urb *urb, uint8_t *pktbuf, uint32_t actual_length) {
+ uint8_t bHeaderLength = pktbuf[0];
+ uint8_t bmHeaderInfo = pktbuf[1];
+
+ if (actual_length <= 2 || actual_length <= bHeaderLength) {
+ return;
+ }
+
+ int frame_id = (bmHeaderInfo & UVC_STREAM_FID);
+ if (bmHeaderInfo & UVC_STREAM_ERR) {
+ ioctl(handle->fd, USBDEVFS_CLEAR_HALT, urb->endpoint);
+ handle->frame[frame_id]->got_bytes = 0;
+ LOGE("@@@ uvc_reap_payload: UVC_STREAM_ERR. frame[%d]", frame_id);
+ } else {
+ // フレームの切り替え処理
+ if (handle->frame_id != frame_id) {
+ // TODO: fid が切り替わったタイミングでフレームを通知して良いか確認すること。
+ uvc_notify_frame(handle, handle->frame[handle->frame_id]);
+ handle->frame_id = frame_id;
+ }
+
+ struct uvc_frame *frame = handle->frame[frame_id];
+ if (frame) {
+ if (bmHeaderInfo & UVC_STREAM_PTS) {
+ if (bHeaderLength >= 6) {
+ frame->pts = DW_TO_INT(pktbuf + 2);
+ }
+ }
+
+ if (bmHeaderInfo & UVC_STREAM_SCR) {
+ if (bHeaderLength >= 12) {
+ frame->stc = DW_TO_INT(pktbuf + 6);
+ // D43 - D47 はReservedで0が設定されているので
+ // ここでは、特にマスク処理はしていない。
+ frame->sof = SW_TO_SHORT(pktbuf + 10);
+ }
+ }
+
+ size_t odd_bytes = actual_length - bHeaderLength;
+
+ if (frame->got_bytes + odd_bytes > frame->length) {
+ LOGW("@@@ uvc_reap_payload: Out of bounds. frame-length=%d got_bytes=%d odd_bytes=%d",
+ frame->length, frame->got_bytes, odd_bytes);
+
+ // フレームサイズを超えてしまった時にはリサイズを行う
+ frame = uvc_realloc_frame(frame, frame->got_bytes + odd_bytes);
+ if (frame == NULL) {
+ LOGE("@@@ uvc_reap_payload: realloc frame[%d]", frame_id);
+ handle->frame[frame_id]->got_bytes = 0;
+ return;
+ }
+ handle->frame[frame_id] = frame;
+ }
+
+ memcpy(frame->buf + frame->got_bytes, pktbuf + bHeaderLength, (size_t) odd_bytes);
+ frame->got_bytes += odd_bytes;
+
+ // End of Frame のフラグがある場合にはフレームの通知を行う
+ if (bmHeaderInfo & UVC_STREAM_EOF) {
+ uvc_notify_frame(handle, frame);
+ }
+ }
+ }
+}
+
+
+/**
+ * アイソクロナス転送で送られてきたデータをフレームに設定します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param urb USB要求ブロック
+ */
+static void uvc_reap_iso_transfer(struct uvc_device_handle *handle, struct usbdevfs_urb *urb) {
+ for (int i = 0; i < urb->number_of_packets; i++) {
+ if (urb->iso_frame_desc[i].status < 0) {
+ LOGW("UVC isochronous frame lost (%d).\n", urb->iso_frame_desc[i].status);
+ continue;
+ }
+
+ uint8_t *pktbuf = urb->buffer + urb->iso_frame_desc[i].length * i;
+ uint32_t actual_length = urb->iso_frame_desc[i].actual_length;
+ uvc_reap_payload(handle, urb, pktbuf, actual_length);
+ }
+}
+
+/**
+ * バルク転送で送られてきたデータをフレームに設定します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param urb USB要求ブロック
+ */
+static void uvc_reap_bulk_transfer(struct uvc_device_handle *handle, struct usbdevfs_urb *urb) {
+ uvc_reap_payload(handle, urb, urb->buffer, (uint32_t) urb->actual_length);
+}
+
+/**
+ * USBからのデータ転送を取得します.
+ *
+ * @param handle UVCデバイスのハンドル
+ */
+static uvc_result uvc_reap_transfer(struct uvc_device_handle *handle) {
+ struct usbdevfs_urb *urb = NULL;
+ int r = xioctl(handle->fd, USBDEVFS_REAPURBNDELAY, &urb);
+ if (r < 0 || urb == NULL) {
+ LOGE("@@@ uvc_reap_transfer: error %d", r);
+ return UVC_ERROR;
+ }
+
+ if (handle->running == UVC_VIDEO_STOP) {
+ LOGW("@@@ uvc_reap_transfer: uvc camera is already stopped.");
+ return UVC_ERROR;
+ }
+
+ if (urb->status != 0) {
+ LOGW("@@@ uvc_reap_transfer: failed to read a uvc.");
+ return UVC_ERROR;
+ }
+
+ struct uvc_transfer *transfer = urb->usercontext;
+ if (transfer) {
+ switch (transfer->type) {
+ case UVC_TRANSFER_TYPE_ISO:
+ uvc_reap_iso_transfer(handle, urb);
+ uvc_submit_iso_transfer(handle, transfer);
+ break;
+
+ case UVC_TRANSFER_TYPE_BULK:
+ uvc_reap_bulk_transfer(handle, urb);
+ uvc_submit_bulk_transfer(handle, transfer);
+ break;
+
+ default:
+ LOGE("@@@ uvc_reap_transfer: Unknown transfer type.");
+ return UVC_ERROR;
+ }
+ }
+
+ return UVC_SUCCESS;
+}
+
+/**
+ * ファイルディスクリプタのイベントを待ち受けて、UVCからのデータ転送を取得します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @return UVC_SUCCESSの場合はポーリングに成功、それ以外は失敗
+ */
+static uvc_result uvc_poll_event(struct uvc_device_handle *handle) {
+ struct pollfd p = {
+ .fd = handle->fd,
+ .events = POLLOUT,
+ .revents = 0
+ };
+
+ int res = poll(&p, 1, 1000);
+ if (res != 1 || p.revents != POLLOUT) {
+ LOGW("@@@ uvc_handle_event_internal: [poll - event %d, res %d, error %d]\n", p.revents, res, errno);
+ return UVC_ERROR;
+ }
+
+ return uvc_reap_transfer(handle);
+}
+
+
+/**
+ * dwFrameIntervalを設定します.
+ *
+ * @param ctrl dwFrameIntervalを設定する構造体
+ * @param fps dwFrameIntervalに設定するFPS
+ * @param min インターバルの最小値
+ * @param max インターバルの最大値
+ * @param step インターバルの間隔
+ * @return UVC_SUCCESSの場合には設定成功、それ以外は設定失敗
+ */
+static uvc_result uvc_set_fps_step(struct uvc_video_control *ctrl, uint32_t fps, uint32_t min, uint32_t max, uint32_t step) {
+ uint32_t interval_100ns = 10000000 / fps;
+ uint32_t interval_offset = interval_100ns - min;
+ if (interval_100ns >= min && interval_100ns <= max && !(interval_offset && (interval_offset % step))) {
+ ctrl->dwFrameInterval = interval_100ns;
+ return UVC_SUCCESS;
+ }
+ return UVC_ERROR;
+}
+
+/**
+ * dwFrameIntervalを設定します.
+ *
+ * @param ctrl dwFrameIntervalを設定する構造体
+ * @param fps dwFrameIntervalに設定するFPS
+ * @param interval_size 設定できるインターバルの個数
+ * @param intervals インターバルの配列
+ * @return UVC_SUCCESSの場合には設定成功、それ以外は設定失敗
+ */
+static uvc_result uvc_set_fps_fixed(struct uvc_video_control *ctrl, uint32_t fps, uint32_t interval_size, uint32_t *intervals) {
+ for (int i = 0; i < interval_size; i++) {
+ uint32_t interval = intervals[i];
+ if (10000000 / interval == fps) {
+ ctrl->dwFrameInterval = interval;
+ return UVC_SUCCESS;
+ }
+ }
+ return UVC_ERROR;
+}
+
+
+/**
+ * UVCのストリーミング開始のネゴシエーションを行います.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param format_index フォーマットインデックス
+ * @param frame_index フレームインデックス
+ * @param fps フレームレート
+ * @return UVC_SUCCESSの場合はネゴシエーションに成功、それ以外はネゴシエーションに失敗
+ */
+static uvc_result uvc_stream_negotiation(struct uvc_device_handle *handle, uint8_t format_index, uint8_t frame_index, uint32_t fps) {
+ uvc_result result;
+ struct uvc_video_control *ctrl = &handle->video_control;
+ struct uvc_vs_format_descriptor *format = uvc_find_format_descriptor(&handle->descriptor, handle->active_config, format_index);
+ struct uvc_vs_frame_descriptor *frame = uvc_find_frame_descriptor(&handle->descriptor, handle->active_config, format_index, frame_index);
+ if (format == NULL || frame == NULL) {
+ LOGE("@@@ uvc_probe_video_control: Not found format or frame descriptor.");
+ return UVC_PARAMETER_INVALID;
+ }
+
+ uint8_t bInterfaceNumber = format->streaming_interface->interface->bInterfaceNumber;
+ handle->bInterfaceNumber = bInterfaceNumber;
+ handle->frame_type = frame->bDescriptorSubType;
+
+ int r = uvc_get_max_probe_video_control(handle, &handle->video_control);
+ if (r != UVC_SUCCESS) {
+ r = uvc_get_min_probe_video_control(handle, &handle->video_control);
+ if (r != UVC_SUCCESS) {
+ r = uvc_get_probe_video_control(handle, &handle->video_control);
+ if (r != UVC_SUCCESS) {
+ LOGE("@@@ uvc_probe_video_control: Failed to get probe video controls.");
+ return UVC_ERROR;
+ }
+ }
+ }
+
+ ctrl->bmHint = (1 << 0);
+ ctrl->bFormatIndex = format_index;
+ ctrl->bFrameIndex = frame_index;
+
+ switch (frame->bDescriptorSubType) {
+ case VS_FRAME_MJPEG: {
+ struct uvc_vs_frame_mjpeg_descriptor *frame_mjpeg = (struct uvc_vs_frame_mjpeg_descriptor *) frame;
+ if (frame_mjpeg->bFrameIntervalType == 0) {
+ ctrl->dwMaxVideoFrameSize = frame_mjpeg->dwMaxVideoFrameBufferSize;
+ uvc_set_fps_step(ctrl, fps,
+ frame_mjpeg->dwMinFrameInterval,
+ frame_mjpeg->dwMaxFrameInterval,
+ frame_mjpeg->dwFrameIntervalStep);
+ } else {
+ uvc_set_fps_fixed(ctrl, fps, frame_mjpeg->bFrameIntervalType, frame_mjpeg->dwFrameInterval);
+ }
+ } break;
+
+ case VS_FRAME_UNCOMPRESSED: {
+ struct uvc_vs_frame_uncompressed_descriptor *frame_uncompressed = (struct uvc_vs_frame_uncompressed_descriptor *) frame;
+ if (frame_uncompressed->bFrameIntervalType == 0) {
+ ctrl->dwMaxVideoFrameSize = frame_uncompressed->dwMaxVideoFrameBufferSize;
+ uvc_set_fps_step(ctrl, fps,
+ frame_uncompressed->dwMinFrameInterval,
+ frame_uncompressed->dwMaxFrameInterval,
+ frame_uncompressed->dwFrameIntervalStep);
+ } else {
+ uvc_set_fps_fixed(ctrl, fps, frame_uncompressed->bFrameIntervalType, frame_uncompressed->dwFrameInterval);
+ }
+ } break;
+
+ case VS_FRAME_H264: {
+ struct uvc_vs_frame_h264_descriptor *frame_h264 = (struct uvc_vs_frame_h264_descriptor *) frame;
+ uvc_set_fps_fixed(ctrl, fps, frame_h264->bNumFrameIntervals, frame_h264->dwFrameInterval);
+ } break;
+
+ default:
+ LOGE("@@ uvc_probe_video_control: unknown frame type.");
+ break;
+ }
+
+ result = uvc_set_probe_video_control(handle, ctrl);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@ uvc_probe_video_control: Failed to set probe video controls.");
+ return UVC_ERROR;
+ }
+
+ result = uvc_get_probe_video_control(handle, ctrl);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@ uvc_probe_video_control: Failed to get probe video controls.");
+ return UVC_ERROR;
+ }
+
+ if (!(ctrl->bFormatIndex == format_index && ctrl->bFrameIndex == frame_index)) {
+ LOGE("@@ uvc_probe_video_control: Failed to set format_index and frame_index.");
+ return UVC_ERROR;
+ }
+
+ result = uvc_set_commit_video_control(handle, &handle->video_control);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@ uvc_probe_video_control: Failed to set commit video controls.");
+ return UVC_ERROR;
+ }
+
+ return result;
+}
+
+/**
+ * H264 エクステンションユニットのネゴシエーションを行います.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param format_index フォーマットインデックス
+ * @param frame_index フレームインデックス
+ * @param fps フレームレート
+ * @return UVC_SUCCESS の場合はネゴシエーションに成功、それ以外はネゴシエーションに失敗
+ */
+static uvc_result uvc_extension_h264_negotiation(struct uvc_device_handle *handle, uint8_t format_index, uint8_t frame_index, uint32_t fps) {
+ if (!uvc_has_h264_extension(&handle->descriptor, handle->active_config)) {
+ LOGE("@@@ uvc_extension_h264_negotiation: Not support h264 extension unit.");
+ return UVC_PARAMETER_INVALID;
+ }
+
+ uvc_result result;
+ struct uvc_vs_format_descriptor *format = uvc_find_format_descriptor(&handle->descriptor, handle->active_config, format_index);
+ struct uvc_vs_frame_descriptor *frame = uvc_find_frame_descriptor(&handle->descriptor, handle->active_config, format_index, frame_index);
+ if (format == NULL || frame == NULL) {
+ LOGE("@@@ uvc_extension_h264_negotiation: Not found format or frame descriptor.");
+ return UVC_PARAMETER_INVALID;
+ }
+
+ handle->bInterfaceNumber = format->streaming_interface->interface->bInterfaceNumber;
+ handle->frame_type = frame->bDescriptorSubType;
+ handle->video_control.bFormatIndex = format_index;
+ handle->video_control.bFrameIndex = frame_index;
+
+ size_t s = uvc_len_probe_h264_extension(handle);
+ if (s != 46) {
+ LOGE("@@@ uvc_extension_h264_negotiation: Failed to get length of h264 configure.");
+ return UVC_ERROR;
+ }
+
+ result = uvc_get_max_probe_h264_extension(handle, &handle->h264_extension);
+ if (result != UVC_SUCCESS) {
+ LOGE("uvc_extension_h264_negotiation: Failed to get a h264 configure.");
+ return UVC_ERROR;
+ }
+
+ if ((handle->h264_extension.bStreamMuxOption & UVC_H264_MUX_OPTION_H264) == 0) {
+ LOGE("uvc_extension_h264_negotiation: bStreamMuxOption is not support h264.");
+ return UVC_ERROR;
+ }
+
+ // TODO: H264 Extension Unit に設定するパラメータの調整
+
+ handle->h264_extension.dwFrameInterval = (uint32_t) (10000000 / fps);
+// handle->h264_extension.dwBitRate = 1000000;
+ handle->h264_extension.bmHints = 0x0;
+// handle->h264_extension.wConfigurationIndex = 0;
+ handle->h264_extension.wWidth = frame->wWidth;
+ handle->h264_extension.wHeight = frame->wHeight;
+ handle->h264_extension.wSliceUnits = 0;
+ handle->h264_extension.wSliceMode = UVC_H264_SLICEMODE_IGNORED;
+ handle->h264_extension.wProfile = UVC_H264_PROFILE_MAIN;
+ handle->h264_extension.wIFramePeriod = 2000;
+// handle->h264_extension.wEstimatedVideoDelay = 30;
+// handle->h264_extension.wEstimatedMaxConfigDelay = 250;
+// handle->h264_extension.bUsageType = UVC_H264_USAGETYPE_REALTIME;
+ handle->h264_extension.bRateControlMode = UVC_H264_RATECONTROL_VBR;
+// handle->h264_extension.bTemporalScaleMode = 0x0;
+// handle->h264_extension.bSpatialScaleMode = 0x0;
+// handle->h264_extension.bSNRScaleMode = 0x0;
+ handle->h264_extension.bStreamMuxOption = UVC_H264_MUX_OPTION_H264 | UVC_H264_MUX_OPTION_ENABLE;
+// handle->h264_extension.bStreamFormat = UVC_H264_STREAMFORMAT_ANNEXB;
+// handle->h264_extension.bEntropyCABAC = UVC_H264_ENTROPY_CAVLC;
+// handle->h264_extension.bTimestamp = UVC_H264_TIMESTAMP_SEI_ENABLE;
+// handle->h264_extension.bNumOfReorderFrames = 0;
+// handle->h264_extension.bPreviewFlipped = UVC_H264_PREFLIPPED_HORIZONTAL;
+// handle->h264_extension.bView = 0x0;
+// handle->h264_extension.bStreamID = 0x0;
+// handle->h264_extension.bSpatialLayerRatio = 0x0;
+// handle->h264_extension.wLeakyBucketSize = 2000;
+
+ result = uvc_set_probe_h264_extension(handle, &handle->h264_extension);
+ if (result != UVC_SUCCESS) {
+ LOGE("uvc_extension_h264_negotiation: Failed to set a h264 configure.");
+ return UVC_ERROR;
+ }
+
+ result = uvc_get_probe_h264_extension(handle, &handle->h264_extension);
+ if (result != UVC_SUCCESS) {
+ LOGE("uvc_extension_h264_negotiation: Failed to get a h264 configure.");
+ return UVC_ERROR;
+ }
+
+ if (handle->h264_extension.wWidth == 0 && handle->h264_extension.wHeight == 0) {
+ LOGE("uvc_extension_h264_negotiation: Failed to get a h264 configure. wWidth=0, wHeight=0");
+ return UVC_ERROR;
+ }
+
+ result = uvc_commit_probe_h264_extension(handle, &handle->h264_extension);
+ if (result != UVC_SUCCESS) {
+ LOGE("uvc_extension_h264_negotiation: Failed to commit a h264 configure.");
+ return UVC_ERROR;
+ }
+
+ return UVC_SUCCESS;
+}
+
+
+/**
+ * UVCの静止画開始のネゴシエーションを行います.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param format_index フォーマットインデックス
+ * @param frame_index フレームインデックス
+ * @param compressionIndex 圧縮インデックス
+ * @return UVC_SUCCESSの場合はネゴシエーションに成功、それ以外はネゴシエーションに失敗
+ */
+static uvc_result uvc_still_negotiation(struct uvc_device_handle *handle, uint8_t format_index, uint8_t frame_index, uint8_t compressionIndex) {
+ uvc_result result;
+ struct uvc_still_control *ctrl = &handle->still_control;
+ struct uvc_vs_format_descriptor *format = uvc_find_format_descriptor(&handle->descriptor, handle->active_config, format_index);
+ struct uvc_vs_frame_descriptor *frame = uvc_find_frame_descriptor(&handle->descriptor, handle->active_config, format_index, frame_index);
+ if (format == NULL || frame == NULL) {
+ LOGE("@@@ uvc_probe_video_control: Not found format or frame descriptor.");
+ return UVC_ERROR;
+ }
+
+ uint8_t interfaceNum = format->streaming_interface->interface->bInterfaceNumber;
+ handle->still_control.bInterfaceNumber = interfaceNum;
+ handle->frame_type = frame->bDescriptorSubType;
+
+ int r = uvc_get_max_probe_still_control(handle, &handle->still_control);
+ if (r != UVC_SUCCESS) {
+ r = uvc_get_min_probe_still_control(handle, &handle->still_control);
+ if (r != UVC_SUCCESS) {
+ r = uvc_get_probe_still_control(handle, &handle->still_control);
+ if (r != UVC_SUCCESS) {
+ LOGE("@@@ uvc_probe_still_control: Failed to get probe still controls.");
+ return UVC_ERROR;
+ }
+ }
+ }
+
+ ctrl->bFormatIndex = format_index;
+ ctrl->bFrameIndex = frame_index;
+ ctrl->bCompressionIndex = compressionIndex;
+ ctrl->bInterfaceNumber = interfaceNum;
+
+ result = uvc_set_probe_still_control(handle, ctrl);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@ uvc_probe_still_control: Failed to set probe still controls.");
+ return UVC_ERROR;
+ }
+
+ result = uvc_get_probe_still_control(handle, ctrl);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@ uvc_probe_still_control: Failed to get probe still controls.");
+ return UVC_ERROR;
+ }
+
+ if (!(ctrl->bFormatIndex == format_index && ctrl->bFrameIndex == frame_index)) {
+ LOGE("@@ uvc_probe_still_control: Failed to set format_index and frame_index.");
+ return UVC_ERROR;
+ }
+
+ result = uvc_set_commit_still_control(handle, &handle->still_control);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@ uvc_probe_still_control: Failed to set commit still controls.");
+ return UVC_ERROR;
+ }
+
+ return result;
+}
+
+/**
+ * フレームの最大サイズを取得します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param is_still 静止画の場合にはUVC_TRUE、それ以外はUVC_FALSE
+ * @return 最大サイズ、取得に失敗した場合は0
+ */
+static uint32_t uvc_get_max_frame_size(struct uvc_device_handle *handle, uint8_t is_still) {
+ uint8_t bFormatIndex;
+ uint8_t bFrameIndex;
+
+ if (is_still) {
+ struct uvc_still_control *ctrl = &handle->still_control;
+ uint32_t dwMaxVideoFrameSize = ctrl->dwMaxVideoFrameSize;
+ if (dwMaxVideoFrameSize > 0) {
+ return dwMaxVideoFrameSize;
+ }
+
+ bFormatIndex = ctrl->bFormatIndex;
+ bFrameIndex = ctrl->bFrameIndex;
+ } else {
+ struct uvc_video_control *ctrl = &handle->video_control;
+ uint32_t dwMaxVideoFrameSize = ctrl->dwMaxVideoFrameSize;
+ if (dwMaxVideoFrameSize > 0) {
+ return dwMaxVideoFrameSize;
+ }
+
+ bFormatIndex = ctrl->bFormatIndex;
+ bFrameIndex = ctrl->bFrameIndex;
+ }
+
+ struct uvc_vs_frame_descriptor *frame = uvc_find_frame_descriptor(&handle->descriptor, handle->active_config, bFormatIndex, bFrameIndex);
+ if (frame) {
+ switch (frame->bDescriptorSubType) {
+ case VS_FRAME_UNCOMPRESSED: {
+ struct uvc_vs_frame_uncompressed_descriptor *m = (struct uvc_vs_frame_uncompressed_descriptor *) frame;
+ return m->dwMaxVideoFrameBufferSize;
+ }
+ case VS_FRAME_MJPEG: {
+ struct uvc_vs_frame_mjpeg_descriptor *m = (struct uvc_vs_frame_mjpeg_descriptor *) frame;
+ return m->dwMaxVideoFrameBufferSize;
+ }
+ case VS_FRAME_H264: {
+ struct uvc_vs_frame_h264_descriptor *m = (struct uvc_vs_frame_h264_descriptor *) frame;
+ return m->dwMaxBitRate / 8;
+ }
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * アイソクロナス転送用の構造体を作成します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param is_still 静止画の場合にはUVC_TRUE、それ以外はUVC_FALSE
+ * @return 構造体の作成に成功した場合はUVC_SUCCESS
+ */
+static uvc_result uvc_isochronous_transfer(struct uvc_device_handle *handle, uint8_t is_still) {
+ uint32_t config_bytes_per_packet;
+ if (is_still) {
+ config_bytes_per_packet = handle->still_control.dwMaxPayloadTransferSize;
+ } else {
+ config_bytes_per_packet = handle->video_control.dwMaxPayloadTransferSize;
+ }
+
+ uint32_t endpoint_bytes_per_packet = 0;
+ uint32_t packets_per_transfer = 0;
+ uint32_t total_transfer_size = 0;
+ uint32_t dwMaxVideoFrameSize = uvc_get_max_frame_size(handle, is_still);
+ if (dwMaxVideoFrameSize == 0) {
+ LOGE("uvc_isochronous_transfer: dwMaxVideoFrame is zero.");
+ return UVC_ERROR;
+ }
+
+ struct uvc_video_streaming_altsetting *altsetting = uvc_get_active_streaming_altsetting(handle);
+ while (altsetting) {
+ struct uvc_endpoint_descriptor *endpoint = NULL;
+
+ if (is_still) {
+ endpoint = altsetting->still_endpoint;
+ } else {
+ endpoint = altsetting->video_endpoint;
+ }
+
+ if (endpoint->bmAttributes & 0x01) {
+ endpoint_bytes_per_packet = endpoint->wMaxPacketSize;
+ endpoint_bytes_per_packet = (endpoint_bytes_per_packet & 0x07ff) *
+ (((endpoint_bytes_per_packet >> 11) & 3) + 1);
+
+ // 転送速度が足りない場合がありますが、その場合には最後に見つかった altsetting を使用します。
+ if (altsetting->next == NULL || endpoint_bytes_per_packet >= config_bytes_per_packet) {
+ packets_per_transfer = (dwMaxVideoFrameSize + endpoint_bytes_per_packet - 1) / endpoint_bytes_per_packet;
+
+ // 使用する uvc_transfer の個数を制限します。
+ // 大きいサイズにすると Nexus5X で ENOMEM (メモリ不足) が発生したので、小さくしています。
+ if (packets_per_transfer > 32) {
+ packets_per_transfer = 32;
+ }
+
+ total_transfer_size = packets_per_transfer * endpoint_bytes_per_packet;
+ break;
+ }
+ }
+ altsetting = altsetting->next;
+ }
+
+ if (altsetting == NULL) {
+ LOGE("@@@ uvc_isochronous_transfer: altsetting not found.");
+ return UVC_ERROR;
+ }
+
+ if (total_transfer_size == 0 || packets_per_transfer == 0) {
+ LOGE("@@@ uvc_isochronous_transfer: Failed to calculate a buffer length.");
+ return UVC_ERROR;
+ }
+
+ if (uvc_set_interface_alt_setting(handle, altsetting) != UVC_SUCCESS) {
+ LOGE("@@@ uvc_isochronous_transfer: Failed to set altsetting.");
+ return UVC_ERROR;
+ }
+
+ for (int i = 0; i < TRANSFER_SIZE; i++) {
+ struct uvc_endpoint_descriptor *endpoint;
+ if (is_still) {
+ endpoint = altsetting->still_endpoint;
+ } else {
+ endpoint = altsetting->video_endpoint;
+ }
+
+ handle->transfers[i] = uvc_create_transfer(handle, endpoint->bEndpointAddress, UVC_TRANSFER_TYPE_ISO,
+ total_transfer_size, (uint8_t) packets_per_transfer, endpoint_bytes_per_packet);
+ if (handle->transfers[i] == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+ }
+
+ return UVC_SUCCESS;
+}
+
+/**
+ * バルク転送用の構造体を作成します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @return 構造体の作成に成功した場合はUVC_SUCCESS
+ */
+static uvc_result uvc_bulk_transfer(struct uvc_device_handle *handle) {
+ uint32_t length = handle->video_control.dwMaxPayloadTransferSize;
+ uint8_t endpoint = uvc_get_active_streaming_interface(handle)->header->bEndpointAddress;
+
+ for (int i = 0; i < TRANSFER_SIZE; i++) {
+ handle->transfers[i] = uvc_create_transfer(handle, endpoint, UVC_TRANSFER_TYPE_BULK, length, 0, 0);
+ if (handle->transfers[i] == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+ }
+ return UVC_SUCCESS;
+}
+
+/**
+ * UVC からのデータ転送を行う構造体とフレームバッファの構造体のメモリ解放を行います.
+ *
+ * @param handle UVCを操作するハンドラへのポインタ
+ */
+static void uvc_dispose_transfer_and_frame(struct uvc_device_handle *handle) {
+ for (int i = 0; i < TRANSFER_SIZE; i++) {
+ if (handle->transfers[i]) {
+ if (handle->transfers[i]->urb) {
+ uvc_cancel_iso_transfer(handle, handle->transfers[i]);
+ }
+ SAFE_FREE(handle->transfers[i]);
+ }
+ }
+
+ for (int i = 0; i < UVC_FRAME_SIZE; i++) {
+ SAFE_FREE(handle->frame[i]);
+ }
+
+ uvc_attach_kernel_driver_and_release(handle, uvc_get_video_streaming_interface_number(handle));
+}
+
+/**
+ * UVC デバイスが isochronous が使用できるか確認します.
+ *
+ * @param handle UVCを操作するハンドラへのポインタ
+ * @return isochronousの場合は UVC_TRUE 、それ以外の場合は UVC_FALSE
+ */
+static int uvc_is_isochronous(struct uvc_device_handle *handle) {
+ struct uvc_video_streaming_altsetting *altsetting = uvc_get_active_streaming_altsetting(handle);
+ while (altsetting) {
+ // isochronousのフラグがONになっている endpoint かチェック
+ if (altsetting->video_endpoint->bmAttributes & 0x01) {
+ return UVC_TRUE;
+ }
+ altsetting = altsetting->next;
+ }
+ return UVC_FALSE;
+}
+
+
+static uvc_result uvc_create_fps_step(uint32_t **fps, uint32_t *length, uint32_t min, uint32_t max, uint32_t step) {
+ uint32_t cnt = (max - min) / step;
+ uint32_t *temp = calloc(cnt, sizeof(uint32_t));
+ if (temp == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+
+ for (int i = 0; i < cnt; i++) {
+ temp[i] = min + step * i;
+ }
+
+ *fps = temp;
+ *length = cnt;
+
+ return UVC_SUCCESS;
+}
+
+
+static uvc_result uvc_create_fps_fixed(uint32_t **fps, uint32_t *length, uint32_t interval_size, uint32_t *intervals) {
+ uint32_t *temp = calloc(interval_size, sizeof(uint32_t));
+ if (temp == NULL) {
+ return UVC_OUT_OF_MEMORY;
+ }
+
+ for (int i = 0; i < interval_size; i++) {
+ temp[i] = (uint32_t) (10000000 / intervals[i]);
+ }
+
+ *fps = temp;
+ *length = interval_size;
+
+ return UVC_SUCCESS;
+}
+
+
+////////////////////// public /////////////////////////
+
+
+/**
+ * UVCデバイスのハンドルを作成します.
+ *
+ * @param fd UVCデバイスへのファイルディスクリプタ
+ * @return UVCデバイスのハンドルへのポインタ
+ */
+struct uvc_device_handle *uvc_open_device(int fd) {
+ struct uvc_device_handle *handle = (struct uvc_device_handle *) calloc(1, sizeof(struct uvc_device_handle));
+ if (handle) {
+ handle->fd = fd;
+
+ if (!lseek(fd, 0, SEEK_SET)) {
+ uint8_t desc[4096];
+ ssize_t length = read(fd, desc, sizeof(desc));
+ if (uvc_parse_descriptor(&handle->descriptor, desc, (uint32_t) length) != UVC_SUCCESS) {
+ uvc_dispose_descriptor(&handle->descriptor);
+ SAFE_FREE(handle);
+ return NULL;
+ }
+ } else {
+ SAFE_FREE(handle);
+ return NULL;
+ }
+
+ uvc_get_configuration(handle, &handle->active_config);
+ uvc_get_capabilities(handle, &handle->caps);
+ uvc_detach_kernel_driver_and_claim(handle, uvc_get_video_control_interface_number(handle));
+
+ LOGD("uvc version: %02X", uvc_get_uvc_version(&handle->descriptor, handle->active_config));
+ LOGD("uvc_get_configuration: %02X", handle->active_config);
+ LOGD("uvc_get_capabilities: %02X", handle->caps);
+ LOGD("uvc_has_h264_extension: %02X", uvc_has_h264_extension(&handle->descriptor, handle->active_config));
+ }
+ return handle;
+}
+
+/**
+ * UVCデバイスのプレビューを開始します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @param formatIndex フォーマットインデックス
+ * @param frameIndex フレームインデックス
+ * @return カメラの開始に成功した場合はUVC_SUCCESSを返却、開始に失敗した場合にはUVC_ERRORなどを返却します。
+ */
+uvc_result uvc_start_video(struct uvc_device_handle *handle, uint8_t formatIndex, uint8_t frameIndex, uint32_t fps, int32_t use_h264) {
+ uvc_result result;
+
+ if (handle->running == UVC_VIDEO_RUNNING) {
+ LOGW("@@@ uvc_start_video: uvv camera is already running.");
+ return UVC_ALREADY_RUNNING;
+ }
+
+ uvc_detach_kernel_driver_and_claim(handle, uvc_get_video_streaming_interface_number(handle));
+
+ if (use_h264) {
+ result = uvc_extension_h264_negotiation(handle, formatIndex, frameIndex, fps);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@@ uvc_start_video: Failed to negotiation a extension unit.");
+ return result;
+ }
+ }
+
+ result = uvc_stream_negotiation(handle, formatIndex, frameIndex, fps);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@@ uvc_start_video: Failed to negotiation a video control.");
+ return result;
+ }
+
+ if (uvc_is_isochronous(handle)) {
+ result = uvc_isochronous_transfer(handle, UVC_FALSE);
+ } else {
+ result = uvc_bulk_transfer(handle);
+ }
+
+ if (result != UVC_SUCCESS) {
+ uvc_dispose_transfer_and_frame(handle);
+ LOGE("@@ uvc_start_video: Failed to create a transfer.");
+ return result;
+ }
+
+ uint32_t dwMaxVideoFrameSize = uvc_get_max_frame_size(handle, UVC_FALSE);
+ if (dwMaxVideoFrameSize == 0) {
+ uvc_dispose_transfer_and_frame(handle);
+ LOGE("@@@ uvc_start_video: Not found a dwMaxVideoFrameSize.");
+ return UVC_ERROR;
+ }
+
+ for (int i = 0; i < UVC_FRAME_SIZE; i++) {
+ handle->frame[i] = uvc_create_frame(handle->frame_type, dwMaxVideoFrameSize);
+ if (handle->frame[i] == NULL) {
+ uvc_dispose_transfer_and_frame(handle);
+ LOGE("@@@ uvc_start_video: Failed to create a uvc_frame.");
+ return UVC_OUT_OF_MEMORY;
+ }
+ }
+
+ for (int i = 0; i < TRANSFER_SIZE; i++) {
+ switch (handle->transfers[i]->type) {
+ case UVC_TRANSFER_TYPE_ISO: {
+ result = uvc_submit_iso_transfer(handle, handle->transfers[i]);
+ if (result != UVC_SUCCESS) {
+ uvc_dispose_transfer_and_frame(handle);
+ return result;
+ }
+ } break;
+
+ case UVC_TRANSFER_TYPE_BULK: {
+ result = uvc_submit_bulk_transfer(handle, handle->transfers[i]);
+ if (result != UVC_SUCCESS) {
+ uvc_dispose_transfer_and_frame(handle);
+ return result;
+ }
+ } break;
+
+ default:
+ LOGE("@@@ uvc_start_video: Unknown transfer type.");
+ return UVC_ERROR;
+ }
+ }
+
+ handle->running = UVC_VIDEO_RUNNING;
+
+ return UVC_SUCCESS;
+}
+
+/**
+ * UVCデバイスのプレビューを停止します.
+ *
+ * @param handle UVCデバイスのハンドル
+ * @return
+ */
+uvc_result uvc_stop_video(struct uvc_device_handle *handle) {
+ // ここではフラグを STOP に変更だけしておく
+ // uvc_handle_event の中で後始末を行う。
+ handle->running = UVC_VIDEO_STOP;
+ return UVC_SUCCESS;
+}
+
+/**
+ * UVCデバイスのハンドルを削除します.
+ *
+ * @param handle UVCデバイスのハンドルへのポインタ
+ */
+uvc_result uvc_close_device(struct uvc_device_handle *handle) {
+ uvc_attach_kernel_driver_and_release(handle, uvc_get_video_control_interface_number(handle));
+ uvc_dispose_transfer_and_frame(handle);
+ uvc_dispose_descriptor(&handle->descriptor);
+ SAFE_FREE(handle);
+ return UVC_SUCCESS;
+}
+
+// uvc_handle_event の中でカウントするエラーの上限
+#define UVC_HANDLE_EVENT_ERROR_COUNT 10
+
+/**
+ * UVCデバイスからのイベント処置を行います.
+ *
+ * uvc_stop_camera が呼び出されるまで、この関数は終了しません。
+ * 必ず別のスレッドを作成して呼び出すことが必要です。
+ *
+ * @param handle UVCデバイスのハンドルへのポインタ
+ */
+uvc_result uvc_handle_event(struct uvc_device_handle *handle) {
+ // アプリが強制終了したときにスレッドが生き残ることがある
+ // この while を抜けることができなくなる可能性があるので、
+ // error_count で処理を抜けるようにしておく。
+ uint32_t error_count = 0;
+ while (handle->running == UVC_VIDEO_RUNNING && error_count < UVC_HANDLE_EVENT_ERROR_COUNT) {
+ if (uvc_poll_event(handle) != UVC_SUCCESS) {
+ error_count++;
+ } else {
+ error_count = 0;
+ }
+ }
+
+ if (error_count >= UVC_HANDLE_EVENT_ERROR_COUNT) {
+ LOGE("@@@@ uvc_handle_event: UVC camera is stopped because an error occurred.");
+ }
+
+ // エラー終了の場合を考慮して、状態をSTOPにしておく
+ handle->running = UVC_VIDEO_STOP;
+
+ // frame と transfer を解放
+ uvc_dispose_transfer_and_frame(handle);
+
+ // エラーカウントが閾値を超えていた場合にはエラーを返却する
+ return error_count < UVC_HANDLE_EVENT_ERROR_COUNT ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+uvc_result uvc_get_fps_list(struct uvc_vs_frame_descriptor *frame, uint32_t **fps, uint32_t *length, uint32_t *default_fps) {
+ switch (frame->bDescriptorSubType) {
+ case VS_FRAME_MJPEG: {
+ struct uvc_vs_frame_mjpeg_descriptor *frame_mjpeg = (struct uvc_vs_frame_mjpeg_descriptor *) frame;
+ *default_fps = 10000000 / frame_mjpeg->dwDefaultFrameInterval;
+ if (frame_mjpeg->bFrameIntervalType == 0) {
+ return uvc_create_fps_step(fps, length,
+ frame_mjpeg->dwMinFrameInterval,
+ frame_mjpeg->dwMaxFrameInterval,
+ frame_mjpeg->dwFrameIntervalStep);
+ } else {
+ return uvc_create_fps_fixed(fps, length,
+ frame_mjpeg->bFrameIntervalType,
+ frame_mjpeg->dwFrameInterval);
+ }
+ }
+
+ case VS_FRAME_UNCOMPRESSED: {
+ struct uvc_vs_frame_uncompressed_descriptor *frame_uncompressed = (struct uvc_vs_frame_uncompressed_descriptor *) frame;
+ *default_fps = 10000000 / frame_uncompressed->dwDefaultFrameInterval;
+ if (frame_uncompressed->bFrameIntervalType == 0) {
+ return uvc_create_fps_step(fps, length,
+ frame_uncompressed->dwMinFrameInterval,
+ frame_uncompressed->dwMaxFrameInterval,
+ frame_uncompressed->dwFrameIntervalStep);
+ } else {
+ return uvc_create_fps_fixed(fps, length,
+ frame_uncompressed->bFrameIntervalType,
+ frame_uncompressed->dwFrameInterval);
+ }
+ }
+
+ case VS_FRAME_H264 : {
+ struct uvc_vs_frame_h264_descriptor *frame_h264 = (struct uvc_vs_frame_h264_descriptor *) frame;
+ *default_fps = 10000000 / frame_h264->dwDefaultFrameInterval;
+ return uvc_create_fps_fixed(fps, length,
+ frame_h264->bNumFrameIntervals,
+ frame_h264->dwFrameInterval);
+ }
+
+ default:
+ LOGE("@@ uvc_get_fps: unknown frame type.");
+ return UVC_ERROR;
+ }
+}
+
+
+struct uvc_video_streaming_interface *uvc_get_active_streaming_interface(struct uvc_device_handle *handle) {
+ return uvc_get_video_streaming_interface(&handle->descriptor, handle->active_config);
+}
+
+
+struct uvc_video_control_interface *uvc_get_active_control_interface(struct uvc_device_handle *handle) {
+ return uvc_get_video_control_interface(&handle->descriptor, handle->active_config);
+}
+
+
+struct uvc_video_streaming_altsetting *uvc_get_active_streaming_altsetting(struct uvc_device_handle *handle) {
+ return uvc_get_video_streaming_altsetting(&handle->descriptor, handle->active_config);
+}
+
+
+struct uvc_vc_extension_unit_descriptor *uvc_get_active_extension_descriptor(struct uvc_device_handle *handle) {
+ return uvc_find_extension_descriptor(&handle->descriptor, handle->active_config);
+}
+
+
+uvc_result uvc_disconnect_interface(struct uvc_device_handle *handle, unsigned int interface) {
+ struct usbdevfs_disconnect_claim dc;
+ dc.interface = interface;
+ strcpy(dc.driver, UVC_DRIVER_NAME);
+ dc.flags = USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER;
+
+ int r = xioctl(handle->fd, USBDEVFS_DISCONNECT_CLAIM, &dc);
+ if (r == 0) {
+ struct usbdevfs_ioctl command;
+ command.ifno = interface;
+ command.ioctl_code = USBDEVFS_DISCONNECT;
+ command.data = NULL;
+ r = xioctl(handle->fd, USBDEVFS_IOCTL, &command) == 0 ? UVC_SUCCESS : UVC_ERROR;
+ if (r == 0) {
+ return UVC_SUCCESS;
+ }
+ }
+ return UVC_ERROR;
+}
+
+
+uvc_result uvc_set_configuration(struct uvc_device_handle *handle, unsigned int config_id) {
+ int r = xioctl(handle->fd, USBDEVFS_SETCONFIGURATION, &config_id);
+ if (r == 0) {
+ handle->active_config = (uint8_t) config_id;
+ return UVC_SUCCESS;
+ }
+ return UVC_ERROR;
+}
+
+
+uint8_t uvc_get_still_capture_method(struct uvc_device_handle *handle) {
+ struct uvc_video_streaming_interface *streaming_interface = uvc_get_active_streaming_interface(handle);
+ if (streaming_interface == NULL || streaming_interface->header == NULL) {
+ return 0;
+ }
+ return streaming_interface->header->bStillCaptureMethod;
+}
+
+
+uvc_result uvc_capture_still_image(struct uvc_device_handle *handle, uint8_t formatIndex, uint8_t frameIndex, uint8_t compressionIndex) {
+ uvc_result result;
+
+ if (handle->running == UVC_VIDEO_RUNNING) {
+ LOGW("@@@ uvc_capture_still_image: uvv camera is already running.");
+ return UVC_ALREADY_RUNNING;
+ }
+
+ uint8_t method = uvc_get_still_capture_method(handle);
+ if (method != METHOD_2 && method != METHOD_3) {
+ LOGW("@@@ uvc_capture_still_image: Not supported a still capture.");
+ return UVC_ERROR;
+ }
+
+ uvc_detach_kernel_driver_and_claim(handle, uvc_get_video_streaming_interface_number(handle));
+
+ result = uvc_still_negotiation(handle, formatIndex, frameIndex, compressionIndex);
+ if (result != UVC_SUCCESS) {
+ LOGE("@@@ uvc_capture_still_image: Failed to negotiation a still control.");
+ return result;
+ }
+
+ if (uvc_is_isochronous(handle)) {
+ result = uvc_isochronous_transfer(handle, UVC_TRUE);
+ } else {
+ result = uvc_bulk_transfer(handle);
+ }
+
+ if (result != UVC_SUCCESS) {
+ uvc_dispose_transfer_and_frame(handle);
+ return result;
+ }
+
+ uint32_t dwMaxVideoFrameSize = uvc_get_max_frame_size(handle, UVC_TRUE);
+ if (dwMaxVideoFrameSize == 0) {
+ LOGE("@@@ uvc_capture_still_image: Not found a dwMaxVideoFrameSize.");
+ return UVC_ERROR;
+ }
+
+ for (int i = 0; i < UVC_FRAME_SIZE; i++) {
+ handle->frame[i] = uvc_create_frame(handle->frame_type, dwMaxVideoFrameSize);
+ if (handle->frame[i] == NULL) {
+ uvc_dispose_transfer_and_frame(handle);
+ return UVC_OUT_OF_MEMORY;
+ }
+ }
+
+ for (int i = 0; i < TRANSFER_SIZE; i++) {
+ switch (handle->transfers[i]->type) {
+ case UVC_TRANSFER_TYPE_ISO: {
+ result = uvc_submit_iso_transfer(handle, handle->transfers[i]);
+ if (result != UVC_SUCCESS) {
+ uvc_dispose_transfer_and_frame(handle);
+ return result;
+ }
+ } break;
+
+ case UVC_TRANSFER_TYPE_BULK: {
+ result = uvc_submit_bulk_transfer(handle, handle->transfers[i]);
+ if (result != UVC_SUCCESS) {
+ uvc_dispose_transfer_and_frame(handle);
+ return result;
+ }
+ } break;
+
+ default:
+ LOGE("@@@ uvc_capture_still_image: Unknown transfer type.");
+ return UVC_ERROR;
+ }
+ }
+
+ handle->running = UVC_VIDEO_RUNNING;
+
+ return UVC_SUCCESS;
+}
+
+
+uvc_result uvc_set_camera_terminal_control(struct uvc_device_handle *handle, int control, void* value, int length) {
+ struct uvc_video_control_interface *control_interface = uvc_get_active_control_interface(handle);
+ if (control_interface == NULL) {
+ return UVC_ERROR;
+ }
+
+ struct uvc_vc_input_terminal_descriptor *input = control_interface->input_terminal;
+ if (input == NULL) {
+ return UVC_ERROR;
+ }
+
+ uint16_t wValue = (uint16_t) (control << 8);
+ uint16_t wIndex = (input->bTerminalID << 8) | control_interface->interface->bInterfaceNumber;
+ uint16_t wLength = (uint16_t) length;
+ return uvc_control_transfer(handle, REQ_TYPE_SET, SET_CUR, wValue, wIndex, wLength, value) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+uvc_result uvc_get_camera_terminal_control(struct uvc_device_handle *handle, int control, int request, void *value, int length) {
+ struct uvc_video_control_interface *control_interface = uvc_get_active_control_interface(handle);
+ if (control_interface == NULL) {
+ return UVC_ERROR;
+ }
+
+ struct uvc_vc_input_terminal_descriptor *input = control_interface->input_terminal;
+ if (input == NULL) {
+ return UVC_ERROR;
+ }
+
+ uint16_t wValue = (uint16_t) (control << 8);
+ uint16_t wIndex = (input->bTerminalID << 8) | control_interface->interface->bInterfaceNumber;
+ uint16_t wLength = (uint16_t) length;
+ return uvc_control_transfer(handle, REQ_TYPE_GET, (uint8_t) request, wValue, wIndex, wLength, value) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+uvc_result uvc_set_processing_unit_control(struct uvc_device_handle *handle, int control, void* value, int length) {
+ struct uvc_video_control_interface *control_interface = uvc_get_active_control_interface(handle);
+ if (control_interface == NULL) {
+ return UVC_ERROR;
+ }
+
+ struct uvc_vc_processing_unit_descriptor *processing = control_interface->processing;
+ if (processing == NULL) {
+ return UVC_ERROR;
+ }
+
+ uint16_t wValue = (uint16_t) (control << 8);
+ uint16_t wIndex = (processing->bUnitID << 8) | control_interface->interface->bInterfaceNumber;
+ uint16_t wLength = (uint16_t) length;
+ return uvc_control_transfer(handle, REQ_TYPE_SET, SET_CUR, wValue, wIndex, wLength, value) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+uvc_result uvc_get_processing_unit_control(struct uvc_device_handle *handle, int control, int request, void *value, int length) {
+ struct uvc_video_control_interface *control_interface = uvc_get_active_control_interface(handle);
+ if (control_interface == NULL) {
+ return UVC_ERROR;
+ }
+
+ struct uvc_vc_processing_unit_descriptor *processing = control_interface->processing;
+ if (processing == NULL) {
+ return UVC_ERROR;
+ }
+
+ uint16_t wValue = (uint16_t) (control << 8);
+ uint16_t wIndex = (processing->bUnitID << 8) | control_interface->interface->bInterfaceNumber;
+ uint16_t wLength = (uint16_t) length;
+ return uvc_control_transfer(handle, REQ_TYPE_GET, (uint8_t) request, wValue, wIndex, wLength, value) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+uvc_result uvc_set_encoding_unit_control(struct uvc_device_handle *handle, int control, void* value, int length) {
+ struct uvc_video_control_interface *control_interface = uvc_get_active_control_interface(handle);
+ if (control_interface == NULL) {
+ return UVC_ERROR;
+ }
+
+ struct uvc_vc_encoding_unit_descriptor *encoding = control_interface->encoding;
+ if (encoding == NULL) {
+ return UVC_ERROR;
+ }
+
+ uint16_t wValue = (uint16_t) (control << 8);
+ uint16_t wIndex = (encoding->bUnitID << 8) | control_interface->interface->bInterfaceNumber;
+ uint16_t wLength = (uint16_t) length;
+ return uvc_control_transfer(handle, REQ_TYPE_SET, SET_CUR, wValue, wIndex, wLength, value) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
+
+
+uvc_result uvc_get_encoding_unit_control(struct uvc_device_handle *handle, int control, int request, void *value, int length) {
+ struct uvc_video_control_interface *control_interface = uvc_get_active_control_interface(handle);
+ if (control_interface == NULL) {
+ return UVC_ERROR;
+ }
+
+ struct uvc_vc_encoding_unit_descriptor *encoding = control_interface->encoding;
+ if (encoding == NULL) {
+ return UVC_ERROR;
+ }
+
+ uint16_t wValue = (uint16_t) (control << 8);
+ uint16_t wIndex = (encoding->bUnitID << 8) | control_interface->interface->bInterfaceNumber;
+ uint16_t wLength = (uint16_t) length;
+ return uvc_control_transfer(handle, REQ_TYPE_GET, (uint8_t) request, wValue, wIndex, wLength, value) > 0 ? UVC_SUCCESS : UVC_ERROR;
+}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc.h
new file mode 100644
index 0000000000..2d010da764
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/jni/uvc.h
@@ -0,0 +1,643 @@
+/*
+ uvc.h
+ Copyright (c) 2018 NTT DOCOMO,INC.
+ Released under the MIT license
+ http://opensource.org/licenses/mit-license.php
+ */
+#ifndef UVC_H
+#define UVC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include
+#include "common.h"
+#include "uvc-descriptor.h"
+#include "uvc-h264-config.h"
+
+/** VideoStreaming interface control selector (A.9.7) */
+enum {
+ VS_CONTROL_UNDEFINED = 0x00,
+ VS_PROBE_CONTROL = 0x01,
+ VS_COMMIT_CONTROL = 0x02,
+ VS_STILL_PROBE_CONTROL = 0x03,
+ VS_STILL_COMMIT_CONTROL = 0x04,
+ VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05,
+ VS_STREAM_ERROR_CODE_CONTROL = 0x06,
+ VS_GENERATE_KEY_FRAME_CONTROL = 0x07,
+ VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08,
+ VS_SYNCH_DELAY_CONTROL = 0x09
+};
+
+
+/**
+ * Video Class-Specific Request Codes.
+ */
+enum uvc_request_code {
+ GET_STATUS = 0x00,
+ CLEAR_FEATURE = 0x01,
+ SET_FEATURE = 0x03,
+ SET_ADDRESS = 0x05,
+ GET_DESCRIPTOR = 0x06,
+ SET_DESCRIPTOR = 0x07,
+ GET_CONFIGURATION = 0x08,
+ SET_CONFIGURATION = 0x09,
+ GET_INTERFACE = 0x0a,
+ SET_INTERFACE = 0x0b,
+ SYNCH_FRAME = 0x0c,
+
+ // UVC
+ RC_UNDEFINED = 0x00,
+ SET_CUR = 0x01,
+ SET_CUR_ALL = 0x11,
+ GET_CUR = 0x81,
+ GET_MIN = 0x82,
+ GET_MAX = 0x83,
+ GET_RES = 0x84,
+ GET_LEN = 0x85,
+ GET_INFO = 0x86,
+ GET_DEF = 0x87,
+ GET_CUR_ALL = 0x91,
+ GET_MIN_ALL = 0x92,
+ GET_MAX_ALL = 0x93,
+ GET_RES_ALL = 0x94,
+ GET_DEF_ALL = 0x97
+};
+
+
+enum {
+ TYPE_CT = 0,
+ TYPE_PU = 1,
+ TYPE_EU = 2
+};
+
+enum {
+ CT_CONTROL_UNDEFINED = 0x00,
+ CT_SCANNING_MODE_CONTROL = 0x01,
+ CT_AE_MODE_CONTROL = 0x02,
+ CT_AE_PRIORITY_CONTROL = 0x03,
+ CT_EXPOSURE_TIME_ABSOLUTE_CONTROL = 0x04,
+ CT_EXPOSURE_TIME_RELATIVE_CONTROL = 0x05,
+ CT_FOCUS_ABSOLUTE_CONTROL= 0x06,
+ CT_FOCUS_RELATIVE_CONTROL = 0x07,
+ CT_FOCUS_AUTO_CONTROL = 0x08,
+ CT_IRIS_ABSOLUTE_CONTROL = 0x09,
+ CT_IRIS_RELATIVE_CONTROL = 0x0A,
+ CT_ZOOM_ABSOLUTE_CONTROL = 0x0B,
+ CT_ZOOM_RELATIVE_CONTROL = 0x0C,
+ CT_PANTILT_ABSOLUTE_CONTROL = 0x0D,
+ CT_PANTILT_RELATIVE_CONTROL = 0x0E,
+ CT_ROLL_ABSOLUTE_CONTROL = 0x0F,
+ CT_ROLL_RELATIVE_CONTROL = 0x10,
+ CT_PRIVACY_CONTROL = 0x11,
+ CT_FOCUS_SIMPLE_CONTROL = 0x12,
+ CT_WINDOW_CONTROL = 0x13,
+ CT_REGION_OF_INTEREST_CONTROL = 0x14
+};
+
+
+enum {
+ PU_CONTROL_UNDEFINED = 0x00,
+ PU_BACKLIGHT_COMPENSATION_CONTROL = 0x01,
+ PU_BRIGHTNESS_CONTROL = 0x02,
+ PU_CONTRAST_CONTROL = 0x03,
+ PU_GAIN_CONTROL = 0x04,
+ PU_POWER_LINE_FREQUENCY_CONTROL = 0x05,
+ PU_HUE_CONTROL = 0x06,
+ PU_SATURATION_CONTROL = 0x07,
+ PU_SHARPNESS_CONTROL = 0x08,
+ PU_GAMMA_CONTROL = 0x09,
+ PU_WHITE_BALANCE_TEMPERATURE_CONTROL = 0x0A,
+ PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL = 0x0B,
+ PU_WHITE_BALANCE_COMPONENT_CONTROL = 0x0C,
+ PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL = 0x0D,
+ PU_DIGITAL_MULTIPLIER_CONTROL = 0x0E,
+ PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL = 0x0F,
+ PU_HUE_AUTO_CONTROL = 0x10,
+ PU_ANALOG_VIDEO_STANDARD_CONTROL = 0x11,
+ PU_ANALOG_LOCK_STATUS_CONTROL = 0x12,
+ PU_CONTRAST_AUTO_CONTROL = 0x13
+};
+
+
+enum {
+ EU_CONTROL_UNDEFINED = 0x00,
+ EU_SELECT_LAYER_CONTROL = 0x01,
+ EU_PROFILE_TOOLSET_CONTROL = 0x02,
+ EU_VIDEO_RESOLUTION_CONTROL = 0x03,
+ EU_MIN_FRAME_INTERVAL_CONTROL = 0x04,
+ EU_SLICE_MODE_CONTROL = 0x05,
+ EU_RATE_CONTROL_MODE_CONTROL = 0x06,
+ EU_AVERAGE_BITRATE_CONTROL = 0x07,
+ EU_CPB_SIZE_CONTROL = 0x08,
+ EU_PEAK_BIT_RATE_CONTROL = 0x09,
+ EU_QUANTIZATION_PARAMS_CONTROL = 0x0A,
+ EU_SYNC_REF_FRAME_CONTROL = 0x0B,
+ EU_LTR_BUFFER_CONTROL = 0x0C,
+ EU_LTR_PICTURE_CONTROL = 0x0D,
+ EU_LTR_VALIDATION_CONTROL = 0x0E,
+ EU_LEVEL_IDC_LIMIT_CONTROL = 0x0F,
+ EU_SEI_PAYLOADTYPE_CONTROL = 0x10,
+ EU_QP_RANGE_CONTROL = 0x11,
+ EU_PRIORITY_CONTROL = 0x12,
+ EU_START_OR_STOP_LAYER_CONTROL = 0x13,
+ EU_ERROR_RESILIENCY_CONTROL = 0x14
+};
+
+
+/** Payload header flags (2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+
+struct uvc_device_handle;
+struct uvc_frame;
+
+
+/**
+ * フレームバッファを通知するコールバック関数の定義.
+ */
+typedef void (*UVC_FRAME_CALLBACK)(void *user, struct uvc_frame *frame);
+
+
+/**
+ * UVC デバイスの動作状態を定義.
+ */
+typedef enum _uvc_status {
+ /**
+ * UVC デバイスの動画撮影が停止中.
+ */
+ UVC_VIDEO_STOP = 0,
+
+ /**
+ * UVC デバイスの動画撮影が動作中.
+ */
+ UVC_VIDEO_RUNNING = 1
+} uvc_status;
+
+
+/**
+ * Video Probe and Commit Controls.
+ */
+struct uvc_video_control {
+ uint16_t bmHint;
+ uint8_t bFormatIndex;
+ uint8_t bFrameIndex;
+ uint32_t dwFrameInterval;
+ uint16_t wKeyFrameRate;
+ uint16_t wPFrameRate;
+ uint16_t wCompQuality;
+ uint16_t wCompWindowSize;
+ uint16_t wDelay;
+ uint32_t dwMaxVideoFrameSize;
+ uint32_t dwMaxPayloadTransferSize;
+
+ // UVC 1.1
+ uint32_t dwClockFrequency;
+ uint8_t bmFramingInfo;
+ uint8_t bPreferedVersion;
+ uint8_t bMinVersion;
+ uint8_t bMaxVersion;
+
+ // UVC 1.5
+ uint8_t bUsage;
+ uint8_t bBitDepthLuma;
+ uint8_t bmSettings;
+ uint8_t bMaxNumberOfRefFramesPlus1;
+ uint16_t bmRateControlModes;
+ uint64_t bmLayoutPerStream;
+};
+
+/**
+ * Still Probe and Commit Controls.
+ */
+struct uvc_still_control {
+ uint8_t bFormatIndex;
+ uint8_t bFrameIndex;
+ uint8_t bCompressionIndex;
+ uint32_t dwMaxVideoFrameSize;
+ uint32_t dwMaxPayloadTransferSize;
+
+ // interfaceNumberを保持
+ uint8_t bInterfaceNumber;
+};
+
+
+/**
+ * フレームバッファのサイズを定義.
+ */
+#define UVC_FRAME_SIZE 2
+
+/**
+ * UVC デバイスから送られてくるフレームデータ.
+ */
+struct uvc_frame {
+ /**
+ * フレームタイプ.
+ */
+ uint8_t type;
+
+ /**
+ * Presentation Time Stamp.
+ */
+ uint32_t pts;
+
+ /**
+ * System Time Clock.
+ */
+ uint32_t stc;
+
+ /**
+ * 1KHz SOF token counter.
+ */
+ uint16_t sof;
+
+ /**
+ * UVC デバイスから取得したデータサイズ.
+ */
+ uint32_t got_bytes;
+
+ /**
+ * フレームのバッファサイズ.
+ */
+ uint32_t length;
+
+ /**
+ * フレームバッファ.
+ *
+ * 配列が0で定義してあるが、length のサイズ分だけメモリが確保されています。
+ *
+ */
+ uint8_t buf[0];
+};
+
+/**
+ * リクエストの個数.
+ */
+#define TRANSFER_SIZE 10
+
+
+enum {
+ UVC_TRANSFER_TYPE_ISO = 1,
+ UVC_TRANSFER_TYPE_BULK = 2
+};
+
+
+/**
+ * UVC デバイスへのリクエストとレスポンスを格納する構造体.
+ */
+struct uvc_transfer {
+ /**
+ * UVC デバイスのハンドル.
+ */
+ struct uvc_device_handle *handle;
+
+ /**
+ * linux の USB 要求ブロック.
+ */
+ struct usbdevfs_urb *urb;
+
+ /**
+ * 転送タイプ.
+ */
+ uint8_t type;
+
+ /**
+ * 転送を行うエンドポイント.
+ */
+ uint8_t endpoint;
+
+ /**
+ * 転送用バッファのサイズ.
+ */
+ uint32_t length;
+
+ /**
+ * アイソクロナス転送用のパケット数.
+ */
+ uint8_t num_iso_packet;
+
+ /**
+ * アイソクロナス転送用のパケットのサイズ.
+ */
+ uint32_t size_iso_packet;
+
+ /**
+ * 転送用バッファ.
+ */
+ uint8_t buf[0];
+};
+
+
+/**
+ * UVC デバイスを管理する構造体.
+ */
+struct uvc_device_handle {
+ /**
+ * UVC デバイスのディスクリプタ.
+ */
+ struct uvc_descriptor descriptor;
+
+ /**
+ * UVC デバイスのビデオをコントロールするための構造体.
+ *
+ * 「4.3.1.1 Video Probe and Commit Controls」で定義されている。
+ */
+ struct uvc_video_control video_control;
+
+ /**
+ * UVC デバイスの静止画をコントロールするための構造値.
+ *
+ * 「4.3.1.2 Video Still Probe Control and Still Commit Control」で定義されている。
+ */
+ struct uvc_still_control still_control;
+
+ /**
+ * H.264 用 Extension Unit.
+ */
+ struct uvc_h264_extension_unit h264_extension;
+
+ /**
+ * UVC デバイスへのファイルディスクリプタ.
+ */
+ int fd;
+
+ /**
+ * UVC デバイスの動作状態.
+ */
+ uvc_status running;
+
+ /**
+ * UVC デバイスのコンフィグ.
+ */
+ uint8_t active_config;
+
+ /**
+ * UVC デバイスの機能.
+ */
+ uint32_t caps;
+
+ /**
+ * bInterfaceNumber を保持.
+ */
+ uint8_t bInterfaceNumber;
+
+ /**
+ * 選択されたフレームタイプ.
+ */
+ uint8_t frame_type;
+
+ /**
+ * H264 多重化フラグ.
+ *
+ * H264 を MJPEG や YUV に多重化する場合は UVC_TRUE を設定します。
+ */
+ int use_ext_h264;
+
+ /**
+ * 現在処理を行なっているフレームID.
+ *
+ * UVC では、0, 1 が交互に送られてくる。
+ *
+ */
+ int frame_id;
+
+ /**
+ * UVC デバイスから送られてくるフレームデータを格納する変数.
+ *
+ * UVC では、フレームは0,1なので、2個用意しておく。
+ *
+ */
+ struct uvc_frame *frame[UVC_FRAME_SIZE];
+
+ /**
+ * UVC デバイスへ送るリクエストを格納する変数.
+ */
+ struct uvc_transfer *transfers[TRANSFER_SIZE];
+
+ /**
+ * フレームを通知する関数.
+ */
+ UVC_FRAME_CALLBACK callback;
+
+ /**
+ * フレームに通知するときのユーザが設定するポインタ.
+ */
+ void *user;
+};
+
+
+/**
+ * uvc_device_handle を作成して、デバイスから DescriptorParser を読み込む。
+ *
+ * @param fd UVCデバイスのファイルディスクリプタ
+ * @return uvc_device_handle のポインタ
+ */
+struct uvc_device_handle *uvc_open_device(int fd);
+
+/**
+ * 指定された UVC デバイスの動画撮影を開始します.
+ *
+ * @param handle UVCデバイス
+ * @param formatIndex フォーマットインデックス
+ * @param frameIndex フレームインデックス
+ * @param fps フレームレート
+ * @param use_h264 H264 Extension 使用フラグ
+ * @return
+ */
+uvc_result uvc_start_video(struct uvc_device_handle *handle, uint8_t formatIndex, uint8_t frameIndex, uint32_t fps, int32_t use_h264);
+
+/**
+ * 指定された UVC デバイスの動画撮影を停止します.
+ *
+ * @param handle UVCデバイス
+ * @return
+ */
+uvc_result uvc_stop_video(struct uvc_device_handle *handle);
+
+/**
+ * UVC デバイスをクローズします.
+ *
+ * 一旦、クローズすると動かなくなりますので、ご注意ください。
+ *
+ * @param handle UVCデバイス
+ * @return
+ */
+uvc_result uvc_close_device(struct uvc_device_handle *handle);
+
+/**
+ * UVC デバイスのイベント処理を行う関数.
+ *
+ * iso や bulk などの転送処理を行うのでメインスレッドではなく、別のスレッドを作成して呼び出してください。
+ *
+ * @param handle UVCデバイス
+ * @return
+ */
+uvc_result uvc_handle_event(struct uvc_device_handle *handle);
+
+/**
+ * UVCデバイスが持つ fps のリストを取得します.
+ * @param frame フレーム
+ * @param fps FPSを格納するポインタ
+ * @param length FPSのリスト数を格納する変数
+ * @param default_fps デフォルトのFPSを格納する変数
+ * @return UVC_SUCCESSの場合はFPSリストの取得に成功、それ以外はリストの取得に失敗
+ */
+uvc_result uvc_get_fps_list(struct uvc_vs_frame_descriptor *frame, uint32_t **fps, uint32_t *length, uint32_t *default_fps);
+
+/**
+ * アクティブに設定されている uvc_video_streaming_interface の構造体を取得します.
+ *
+ * @param handle UVCデバイス
+ * @return uvc_video_streaming_interfaceのポインタ、取得できない場合には NULL を返却します。
+ */
+struct uvc_video_streaming_interface *uvc_get_active_streaming_interface(struct uvc_device_handle *handle);
+
+/**
+ * アクティブに設定されている uvc_video_control_interface の構造体を取得します.
+ *
+ * @param handle UVCデバイス
+ * @return uvc_video_control_interface、取得できない場合には NULL を返却します。
+ */
+struct uvc_video_control_interface *uvc_get_active_control_interface(struct uvc_device_handle *handle);
+
+/**
+ * アクティブに設定されている uvc_video_streaming_altsetting の構造体を取得します.
+ *
+ * @param handle UVCデバイス
+ * @return uvc_video_streaming_altsetting、取得できない場合には NULL を返却します。
+ */
+struct uvc_video_streaming_altsetting *uvc_get_active_streaming_altsetting(struct uvc_device_handle *handle);
+
+/**
+ * アクティブに設定されている uvc_vc_extension_unit_descriptor の構造体を取得します.
+ *
+ * @param handle UVCデバイス
+ * @return uvc_vc_extension_unit_descriptor、取得できない場合には NULL を返却します。
+ */
+struct uvc_vc_extension_unit_descriptor *uvc_get_active_extension_descriptor(struct uvc_device_handle *handle);
+
+/**
+ * アクティブにするコンフィギュレーションを設定します.
+ *
+ * @param handle UVCデバイス
+ * @param config_id アクティブにするコンフィギュレーションID
+ * @return UVC_SUCCESSの場合は設定に成功、それ以外は設定に失敗
+ */
+uvc_result uvc_set_configuration(struct uvc_device_handle *handle, unsigned int config_id);
+
+/**
+ * 指定されたインターフェースを解除します.
+ *
+ * Android OS側でclaimされたインターフェースを解除します。
+ *
+ * @param handle UVCデバイス
+ * @param interface 解除するインターフェース
+ * @return UVC_SUCCESSの場合は解除に成功、それ以外は解除に失敗
+ */
+uvc_result uvc_disconnect_interface(struct uvc_device_handle *handle, unsigned int interface);
+
+/**
+ * 静止画のモードを取得します.
+ *
+ * @param handle UVCデバイス
+ * @return METHOD_0の場合は未サポート
+ * METHOD_1の場合はハードキーのみ対応
+ * METHOD_2の場合はプレビューと排他的撮影可能
+ * METHOD_3の場合にはプレビューと同時撮影可能
+ */
+uint8_t uvc_get_still_capture_method(struct uvc_device_handle *handle);
+
+/**
+ * 静止画の撮影を行います.
+ *
+ * @param handle UVCデバイス
+ * @param formatIndex フォーマットインデックス
+ * @param frameIndex フレームインデックス
+ * @param compressionIndex 圧縮インデックス
+ * @return UVC_SUCCESSの場合は撮影に成功、それ以外は撮影に失敗
+ */
+uvc_result uvc_capture_still_image(struct uvc_device_handle *handle, uint8_t formatIndex, uint8_t frameIndex, uint8_t compressionIndex);
+
+/**
+ * カメラターミナルコントロールの設定を行います.
+ *
+ * @param handle UVCデバイス
+ * @param control コントロールID
+ * @param value 設定する値
+ * @param length 設定する値のサイズ
+ * @return UVC_SUCCESSの場合は撮影に成功、それ以外は撮影に失敗
+ */
+uvc_result uvc_set_camera_terminal_control(struct uvc_device_handle *handle, int control, void* value, int length);
+
+/**
+ * カメラターミナルコントロールの値を取得します.
+ *
+ * @param handle UVCデバイス
+ * @param control コントロールID
+ * @param request リクエストタイプ
+ * @param value 設定する値
+ * @param length 設定する値のサイズ
+ * @return UVC_SUCCESSの場合は撮影に成功、それ以外は撮影に失敗
+ */
+uvc_result uvc_get_camera_terminal_control(struct uvc_device_handle *handle, int control, int request, void *value, int length);
+
+/**
+ * プロセシングユニットコントロールの設定を行います.
+ *
+ * @param handle UVCデバイス
+ * @param control コントロールID
+ * @param value 設定する値
+ * @param length 設定する値のサイズ
+ * @return UVC_SUCCESSの場合は撮影に成功、それ以外は撮影に失敗
+ */
+uvc_result uvc_set_processing_unit_control(struct uvc_device_handle *handle, int control, void* value, int length);
+
+/**
+ * プロセシングユニットコントロールの値を取得します.
+ *
+ * @param handle UVCデバイス
+ * @param control コントロールID
+ * @param request リクエストタイプ
+ * @param value 設定する値
+ * @param length 設定する値のサイズ
+ * @return UVC_SUCCESSの場合は撮影に成功、それ以外は撮影に失敗
+ */
+uvc_result uvc_get_processing_unit_control(struct uvc_device_handle *handle, int control, int request, void *value, int length);
+
+/**
+ * エンコーディングユニットコントロールの設定を行います.
+ *
+ * @param handle UVCデバイス
+ * @param control コントロールID
+ * @param value 設定する値
+ * @param length 設定する値のサイズ
+ * @return UVC_SUCCESSの場合は撮影に成功、それ以外は撮影に失敗
+ */
+uvc_result uvc_set_encoding_unit_control(struct uvc_device_handle *handle, int control, void* value, int length);
+
+/**
+ * エンコーディングユニットコントロールの値を取得します.
+ *
+ * @param handle UVCデバイス
+ * @param control コントロールID
+ * @param request リクエストタイプ
+ * @param value 設定する値
+ * @param length 設定する値のサイズ
+ * @return UVC_SUCCESSの場合は撮影に成功、それ以外は撮影に失敗
+ */
+uvc_result uvc_get_encoding_unit_control(struct uvc_device_handle *handle, int control, int request, void *value, int length);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif //UVC_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/res/values/strings.xml b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..55344e5192
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/test/java/org/deviceconnect/android/libuvc/ExampleUnitTest.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/test/java/org/deviceconnect/android/libuvc/ExampleUnitTest.java
new file mode 100644
index 0000000000..84dc937843
--- /dev/null
+++ b/dConnectDevicePlugin/dConnectDeviceUVC/libuvc/src/test/java/org/deviceconnect/android/libuvc/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package org.deviceconnect.android.libuvc;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/build.gradle b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/build.gradle
deleted file mode 100644
index 672cbb7022..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/build.gradle
+++ /dev/null
@@ -1,40 +0,0 @@
-apply plugin: 'com.android.library'
-
-android {
- compileSdkVersion 29
- ndkVersion "21.0.6113669"
-
- defaultConfig {
- minSdkVersion 21
- targetSdkVersion 29
- ndk {
- // NDKビルドするABIを指定する
- abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'
- }
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
- }
- }
-
- externalNativeBuild {
- ndkBuild {
- path 'src/main/jni/Android.mk'
- }
- }
-}
-
-dependencies {
- implementation fileTree(dir: new File(buildDir, 'libs'), include: '*.jar')
-}
-
-dependencies {
- implementation fileTree(dir: new File(buildDir, 'libs'), include: '*.jar')
- implementation "androidx.legacy:legacy-support-v4:${supportLibVersion}"
- implementation("com.serenegiant:common:${commonLibVersion}") {
- exclude module: 'support-v4'
- }
-}
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/AndroidManifest.xml b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/AndroidManifest.xml
deleted file mode 100644
index 977092cee7..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/CameraDialog.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/CameraDialog.java
deleted file mode 100755
index 3dbead69a1..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/CameraDialog.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * 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.util.ArrayList;
-import java.util.List;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.hardware.usb.UsbDevice;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.Button;
-import android.widget.CheckedTextView;
-import android.widget.Spinner;
-
-import com.serenegiant.usb.DeviceFilter;
-import com.serenegiant.usb.USBMonitor;
-
-import com.serenegiant.uvccamera.R;
-
-public class CameraDialog extends DialogFragment {
- private static final String TAG = CameraDialog.class.getSimpleName();
-
- 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 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 ClassCastException e) {
- } catch (final NullPointerException e) {
- }
- if (mUSBMonitor == null) {
- throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- 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 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);
- 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;
- }
-
-
- @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 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);
- }
-
- public void updateDevices() {
-// mUSBMonitor.dumpDevices();
- final List 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 final LayoutInflater mInflater;
- private final List mList;
-
- public DeviceListAdapter(final Context context, final Listlist) {
- mInflater = LayoutInflater.from(context);
- mList = list != null ? list : new ArrayList();
- }
-
- @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 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;
- }
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/DeviceFilter.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/DeviceFilter.java
deleted file mode 100755
index dff4f09614..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/DeviceFilter.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * 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.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.Context;
-import android.content.res.Resources.NotFoundException;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
-import android.text.TextUtils;
-import android.util.Log;
-
-public final class DeviceFilter {
-
- private static final String TAG = "DeviceFilter";
-
- // USB Vendor ID (or -1 for unspecified)
- public final int mVendorId;
- // USB Product ID (or -1 for unspecified)
- public final int mProductId;
- // USB device or interface class (or -1 for unspecified)
- public final int mClass;
- // USB device subclass (or -1 for unspecified)
- public final int mSubclass;
- // USB device protocol (or -1 for unspecified)
- public final int mProtocol;
- // USB device manufacturer name string (or null for unspecified)
- public final String mManufacturerName;
- // USB device product name string (or null for unspecified)
- public final String mProductName;
- // USB device serial number string (or null for unspecified)
- public final String mSerialNumber;
- // set true if specific device(s) should exclude
- public final boolean isExclude;
-
- public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
- final int protocol, final String manufacturer, final String product, final String serialNum) {
- this(vid, pid, clasz, subclass, protocol, manufacturer, product, serialNum, false);
- }
-
- public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
- final int protocol, final String manufacturer, final String product, final String serialNum, final boolean isExclude) {
- mVendorId = vid;
- mProductId = pid;
- mClass = clasz;
- mSubclass = subclass;
- mProtocol = protocol;
- mManufacturerName = manufacturer;
- mProductName = product;
- mSerialNumber = serialNum;
- this.isExclude = isExclude;
-/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
- mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
- }
-
- public DeviceFilter(final UsbDevice device) {
- this(device, false);
- }
-
- public DeviceFilter(final UsbDevice device, final boolean isExclude) {
- mVendorId = device.getVendorId();
- mProductId = device.getProductId();
- mClass = device.getDeviceClass();
- mSubclass = device.getDeviceSubclass();
- mProtocol = device.getDeviceProtocol();
- mManufacturerName = null; // device.getManufacturerName();
- mProductName = null; // device.getProductName();
- mSerialNumber = null; // device.getSerialNumber();
- this.isExclude = isExclude;
-/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
- mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
- }
-
- /**
- * 指定したxmlリソースからDeviceFilterリストを生成する
- * @param context
- * @param deviceFilterXmlId
- * @return
- */
- public static List getDeviceFilters(final Context context, final int deviceFilterXmlId) {
- final XmlPullParser parser = context.getResources().getXml(deviceFilterXmlId);
- final List deviceFilters = new ArrayList();
- try {
- int eventType = parser.getEventType();
- while (eventType != XmlPullParser.END_DOCUMENT) {
- if (eventType == XmlPullParser.START_TAG) {
- final DeviceFilter deviceFilter = readEntryOne(context, parser);
- if (deviceFilter != null) {
- deviceFilters.add(deviceFilter);
- }
- }
- eventType = parser.next();
- }
- } catch (final XmlPullParserException e) {
- Log.d(TAG, "XmlPullParserException", e);
- } catch (final IOException e) {
- Log.d(TAG, "IOException", e);
- }
-
- return Collections.unmodifiableList(deviceFilters);
- }
-
- /**
- * read as integer values with default value from xml(w/o exception throws)
- * resource integer id is also resolved into integer
- * @param parser
- * @param namespace
- * @param name
- * @param defaultValue
- * @return
- */
- private static final int getAttributeInteger(final Context context, final XmlPullParser parser, final String namespace, final String name, final int defaultValue) {
- int result = defaultValue;
- try {
- String v = parser.getAttributeValue(namespace, name);
- if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
- final String r = v.substring(1);
- final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
- if (resId > 0) {
- result = context.getResources().getInteger(resId);
- }
- } else {
- int radix = 10;
- if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
- (v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
- // allow hex values starting with 0x or 0X
- radix = 16;
- v = v.substring(2);
- }
- result = Integer.parseInt(v, radix);
- }
- } catch (final NotFoundException e) {
- result = defaultValue;
- } catch (final NumberFormatException e) {
- result = defaultValue;
- } catch (final NullPointerException e) {
- result = defaultValue;
- }
- return result;
- }
-
- /**
- * read as boolean values with default value from xml(w/o exception throws)
- * resource boolean id is also resolved into boolean
- * if the value is zero, return false, if the value is non-zero integer, return true
- * @param context
- * @param parser
- * @param namespace
- * @param name
- * @param defaultValue
- * @return
- */
- private static final boolean getAttributeBoolean(final Context context, final XmlPullParser parser, final String namespace, final String name, final boolean defaultValue) {
- boolean result = defaultValue;
- try {
- String v = parser.getAttributeValue(namespace, name);
- if ("TRUE".equalsIgnoreCase(v)) {
- result = true;
- } else if ("FALSE".equalsIgnoreCase(v)) {
- result = false;
- } else if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
- final String r = v.substring(1);
- final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
- if (resId > 0) {
- result = context.getResources().getBoolean(resId);
- }
- } else {
- int radix = 10;
- if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
- (v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
- // allow hex values starting with 0x or 0X
- radix = 16;
- v = v.substring(2);
- }
- final int val = Integer.parseInt(v, radix);
- result = val != 0;
- }
- } catch (final NotFoundException e) {
- result = defaultValue;
- } catch (final NumberFormatException e) {
- result = defaultValue;
- } catch (final NullPointerException e) {
- result = defaultValue;
- }
- return result;
- }
-
- /**
- * read as String attribute with default value from xml(w/o exception throws)
- * resource string id is also resolved into string
- * @param parser
- * @param namespace
- * @param name
- * @param defaultValue
- * @return
- */
- private static final String getAttributeString(final Context context, final XmlPullParser parser, final String namespace, final String name, final String defaultValue) {
- String result = defaultValue;
- try {
- result = parser.getAttributeValue(namespace, name);
- if (result == null)
- result = defaultValue;
- if (!TextUtils.isEmpty(result) && result.startsWith("@")) {
- final String r = result.substring(1);
- final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
- if (resId > 0)
- result = context.getResources().getString(resId);
- }
- } catch (final NotFoundException e) {
- result = defaultValue;
- } catch (final NumberFormatException e) {
- result = defaultValue;
- } catch (final NullPointerException e) {
- result = defaultValue;
- }
- return result;
- }
-
- public static DeviceFilter readEntryOne(final Context context, final XmlPullParser parser)
- throws XmlPullParserException, IOException {
- int vendorId = -1;
- int productId = -1;
- int deviceClass = -1;
- int deviceSubclass = -1;
- int deviceProtocol = -1;
- boolean exclude = false;
- String manufacturerName = null;
- String productName = null;
- String serialNumber = null;
- boolean hasValue = false;
-
- String tag;
- int eventType = parser.getEventType();
- while (eventType != XmlPullParser.END_DOCUMENT) {
- tag = parser.getName();
- if (!TextUtils.isEmpty(tag) && (tag.equalsIgnoreCase("usb-device"))) {
- if (eventType == XmlPullParser.START_TAG) {
- hasValue = true;
- vendorId = getAttributeInteger(context, parser, null, "vendor-id", -1);
- if (vendorId == -1) {
- vendorId = getAttributeInteger(context, parser, null, "vendorId", -1);
- if (vendorId == -1)
- vendorId = getAttributeInteger(context, parser, null, "venderId", -1);
- }
- productId = getAttributeInteger(context, parser, null, "product-id", -1);
- if (productId == -1)
- productId = getAttributeInteger(context, parser, null, "productId", -1);
- deviceClass = getAttributeInteger(context, parser, null, "class", -1);
- deviceSubclass = getAttributeInteger(context, parser, null, "subclass", -1);
- deviceProtocol = getAttributeInteger(context, parser, null, "protocol", -1);
- manufacturerName = getAttributeString(context, parser, null, "manufacturer-name", null);
- if (TextUtils.isEmpty(manufacturerName))
- manufacturerName = getAttributeString(context, parser, null, "manufacture", null);
- productName = getAttributeString(context, parser, null, "product-name", null);
- if (TextUtils.isEmpty(productName))
- productName = getAttributeString(context, parser, null, "product", null);
- serialNumber = getAttributeString(context, parser, null, "serial-number", null);
- if (TextUtils.isEmpty(serialNumber))
- serialNumber = getAttributeString(context, parser, null, "serial", null);
- exclude = getAttributeBoolean(context, parser, null, "exclude", false);
- } else if (eventType == XmlPullParser.END_TAG) {
- if (hasValue) {
- return new DeviceFilter(vendorId, productId, deviceClass,
- deviceSubclass, deviceProtocol, manufacturerName, productName,
- serialNumber, exclude);
- }
- }
- }
- eventType = parser.next();
- }
- return null;
- }
-
-/* public void write(XmlSerializer serializer) throws IOException {
- serializer.startTag(null, "usb-device");
- if (mVendorId != -1) {
- serializer
- .attribute(null, "vendor-id", Integer.toString(mVendorId));
- }
- if (mProductId != -1) {
- serializer.attribute(null, "product-id",
- Integer.toString(mProductId));
- }
- if (mClass != -1) {
- serializer.attribute(null, "class", Integer.toString(mClass));
- }
- if (mSubclass != -1) {
- serializer.attribute(null, "subclass", Integer.toString(mSubclass));
- }
- if (mProtocol != -1) {
- serializer.attribute(null, "protocol", Integer.toString(mProtocol));
- }
- if (mManufacturerName != null) {
- serializer.attribute(null, "manufacturer-name", mManufacturerName);
- }
- if (mProductName != null) {
- serializer.attribute(null, "product-name", mProductName);
- }
- if (mSerialNumber != null) {
- serializer.attribute(null, "serial-number", mSerialNumber);
- }
- serializer.attribute(null, "serial-number", Boolean.toString(isExclude));
- serializer.endTag(null, "usb-device");
- } */
-
- /**
- * 指定したクラス・サブクラス・プロトコルがこのDeviceFilterとマッチするかどうかを返す
- * mExcludeフラグは別途#isExcludeか自前でチェックすること
- * @param clasz
- * @param subclass
- * @param protocol
- * @return
- */
- private boolean matches(final int clasz, final int subclass, final int protocol) {
- return ((mClass == -1 || clasz == mClass)
- && (mSubclass == -1 || subclass == mSubclass) && (mProtocol == -1 || protocol == mProtocol));
- }
-
- /**
- * 指定したUsbDeviceがこのDeviceFilterにマッチするかどうかを返す
- * mExcludeフラグは別途#isExcludeか自前でチェックすること
- * @param device
- * @return
- */
- public boolean matches(final UsbDevice device) {
- if (mVendorId != -1 && device.getVendorId() != mVendorId) {
- return false;
- }
- if (mProductId != -1 && device.getProductId() != mProductId) {
- return false;
- }
-/* if (mManufacturerName != null && device.getManufacturerName() == null)
- return false;
- if (mProductName != null && device.getProductName() == null)
- return false;
- if (mSerialNumber != null && device.getSerialNumber() == null)
- return false;
- if (mManufacturerName != null && device.getManufacturerName() != null
- && !mManufacturerName.equals(device.getManufacturerName()))
- return false;
- if (mProductName != null && device.getProductName() != null
- && !mProductName.equals(device.getProductName()))
- return false;
- if (mSerialNumber != null && device.getSerialNumber() != null
- && !mSerialNumber.equals(device.getSerialNumber()))
- return false; */
-
- // check device class/subclass/protocol
- if (matches(device.getDeviceClass(), device.getDeviceSubclass(), device.getDeviceProtocol())) {
- return true;
- }
-
- // if device doesn't match, check the interfaces
- final int count = device.getInterfaceCount();
- for (int i = 0; i < count; i++) {
- final UsbInterface intf = device.getInterface(i);
- if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), intf.getInterfaceProtocol())) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * このDeviceFilterに一致してかつmExcludeがtrueならtrueを返す
- * @param device
- * @return
- */
- public boolean isExclude(final UsbDevice device) {
- return isExclude && matches(device);
- }
-
- /**
- * これって要らんかも, equalsでできる気が
- * @param f
- * @return
- */
- public boolean matches(final DeviceFilter f) {
- if (isExclude != f.isExclude) {
- return false;
- }
- if (mVendorId != -1 && f.mVendorId != mVendorId) {
- return false;
- }
- if (mProductId != -1 && f.mProductId != mProductId) {
- return false;
- }
- if (f.mManufacturerName != null && mManufacturerName == null) {
- return false;
- }
- if (f.mProductName != null && mProductName == null) {
- return false;
- }
- if (f.mSerialNumber != null && mSerialNumber == null) {
- return false;
- }
- if (mManufacturerName != null && f.mManufacturerName != null
- && !mManufacturerName.equals(f.mManufacturerName)) {
- return false;
- }
- if (mProductName != null && f.mProductName != null
- && !mProductName.equals(f.mProductName)) {
- return false;
- }
- if (mSerialNumber != null && f.mSerialNumber != null
- && !mSerialNumber.equals(f.mSerialNumber)) {
- return false;
- }
-
- // check device class/subclass/protocol
- return matches(f.mClass, f.mSubclass, f.mProtocol);
- }
-
- @Override
- public boolean equals(final Object obj) {
- // can't compare if we have wildcard strings
- if (mVendorId == -1 || mProductId == -1 || mClass == -1
- || mSubclass == -1 || mProtocol == -1) {
- return false;
- }
- if (obj instanceof DeviceFilter) {
- final DeviceFilter filter = (DeviceFilter) obj;
-
- if (filter.mVendorId != mVendorId
- || filter.mProductId != mProductId
- || filter.mClass != mClass || filter.mSubclass != mSubclass
- || filter.mProtocol != mProtocol) {
- return false;
- }
- if ((filter.mManufacturerName != null && mManufacturerName == null)
- || (filter.mManufacturerName == null && mManufacturerName != null)
- || (filter.mProductName != null && mProductName == null)
- || (filter.mProductName == null && mProductName != null)
- || (filter.mSerialNumber != null && mSerialNumber == null)
- || (filter.mSerialNumber == null && mSerialNumber != null)) {
- return false;
- }
- if ((filter.mManufacturerName != null && mManufacturerName != null && !mManufacturerName
- .equals(filter.mManufacturerName))
- || (filter.mProductName != null && mProductName != null && !mProductName
- .equals(filter.mProductName))
- || (filter.mSerialNumber != null && mSerialNumber != null && !mSerialNumber
- .equals(filter.mSerialNumber))) {
- return false;
- }
- return (filter.isExclude != isExclude);
- }
- if (obj instanceof UsbDevice) {
- final UsbDevice device = (UsbDevice) obj;
- if (isExclude
- || (device.getVendorId() != mVendorId)
- || (device.getProductId() != mProductId)
- || (device.getDeviceClass() != mClass)
- || (device.getDeviceSubclass() != mSubclass)
- || (device.getDeviceProtocol() != mProtocol) ) {
- return false;
- }
-/* if ((mManufacturerName != null && device.getManufacturerName() == null)
- || (mManufacturerName == null && device
- .getManufacturerName() != null)
- || (mProductName != null && device.getProductName() == null)
- || (mProductName == null && device.getProductName() != null)
- || (mSerialNumber != null && device.getSerialNumber() == null)
- || (mSerialNumber == null && device.getSerialNumber() != null)) {
- return (false);
- } */
-/* if ((device.getManufacturerName() != null && !mManufacturerName
- .equals(device.getManufacturerName()))
- || (device.getProductName() != null && !mProductName
- .equals(device.getProductName()))
- || (device.getSerialNumber() != null && !mSerialNumber
- .equals(device.getSerialNumber()))) {
- return (false);
- } */
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return (((mVendorId << 16) | mProductId) ^ ((mClass << 16)
- | (mSubclass << 8) | mProtocol));
- }
-
- @Override
- public String toString() {
- return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId="
- + mProductId + ",mClass=" + mClass + ",mSubclass=" + mSubclass
- + ",mProtocol=" + mProtocol
- + ",mManufacturerName=" + mManufacturerName
- + ",mProductName=" + mProductName
- + ",mSerialNumber=" + mSerialNumber
- + ",isExclude=" + isExclude
- + "]";
- }
-
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IButtonCallback.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IButtonCallback.java
deleted file mode 100755
index 4025085c46..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IButtonCallback.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.serenegiant.usb;
-
-public interface IButtonCallback {
- void onButton(int button, int state);
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IFrameCallback.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IFrameCallback.java
deleted file mode 100755
index ee16e878f4..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IFrameCallback.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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;
-/**
- * Callback interface for UVCCamera class
- * If you need frame data as ByteBuffer, you can use this callback interface with UVCCamera#setFrameCallback
- */
-public interface IFrameCallback {
- /**
- * This method is called from native library via JNI on the same thread as UVCCamera#startCapture.
- * You can use both UVCCamera#startCapture and #setFrameCallback
- * but it is better to use either for better performance.
- * You can also pass pixel format type to UVCCamera#setFrameCallback for this method.
- * Some frames may drops if this method takes a time.
- * When you use some color format like NV21, this library never execute color space conversion,
- * just execute pixel format conversion. If you want to get same result as on screen, please try to
- * consider to get images via texture(SurfaceTexture) and read pixel buffer from it using OpenGL|ES2/3
- * instead of using IFrameCallback(this way is much efficient in most case than using IFrameCallback).
- * @param frame this is direct ByteBuffer from JNI layer and you should handle it's byte order and limitation.
- */
- public void onFrame(ByteBuffer frame);
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IPreviewFrameCallback.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IPreviewFrameCallback.java
deleted file mode 100644
index c7a177a741..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IPreviewFrameCallback.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.serenegiant.usb;
-
-
-public interface IPreviewFrameCallback {
-
- public void onFrame(byte[] frame);
-
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IStatusCallback.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IStatusCallback.java
deleted file mode 100755
index ad743204ff..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/IStatusCallback.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.serenegiant.usb;
-
-import java.nio.ByteBuffer;
-
-public interface IStatusCallback {
- void onStatus(int statusClass, int event, int selector, int statusAttribute, ByteBuffer data);
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/Size.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/Size.java
deleted file mode 100755
index a268f23528..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/Size.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * 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.util.Locale;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class Size implements Parcelable {
- //
- /**
- * native側のuvc_raw_format_tの値, こっちは主にlibuvc用
- * 9999 is still image
- */
- public int type;
- /**
- * native側のraw_frame_tの値, androusb用,
- * libuvcは対応していない
- */
- public int frame_type;
- public int index;
- public int width;
- public int height;
- public int frameIntervalType;
- public int frameIntervalIndex;
- public int[] intervals;
- // ここ以下はframeIntervalTypeとintervalsから#updateFrameRateで計算する
- public float[] fps;
- private String frameRates;
-
- /**
- * コンストラクタ
- * @param _type native側のraw_format_tの値, ただし9999は静止画
- * @param _frame_type native側のraw_frame_tの値
- * @param _index
- * @param _width
- * @param _height
- */
- public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height) {
- type = _type;
- frame_type = _frame_type;
- index = _index;
- width = _width;
- height = _height;
- frameIntervalType = -1;
- frameIntervalIndex = 0;
- intervals = null;
- updateFrameRate();
- }
-
- /**
- * コンストラクタ
- * @param _type native側のraw_format_tの値, ただし9999は静止画
- * @param _frame_type native側のraw_frame_tの値
- * @param _index
- * @param _width
- * @param _height
- * @param _min_intervals
- * @param _max_intervals
- */
- public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int _min_intervals, final int _max_intervals, final int _step) {
- type = _type;
- frame_type = _frame_type;
- index = _index;
- width = _width;
- height = _height;
- frameIntervalType = 0;
- frameIntervalIndex = 0;
- intervals = new int[3];
- intervals[0] = _min_intervals;
- intervals[1] = _max_intervals;
- intervals[2] = _step;
- updateFrameRate();
- }
-
- /**
- * コンストラクタ
- * @param _type native側のraw_format_tの値, ただし9999は静止画
- * @param _frame_type native側のraw_frame_tの値
- * @param _index
- * @param _width
- * @param _height
- * @param _intervals
- */
- public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int[] _intervals) {
- type = _type;
- frame_type = _frame_type;
- index = _index;
- width = _width;
- height = _height;
- final int n = _intervals != null ? _intervals.length : -1;
- if (n > 0) {
- frameIntervalType = n;
- intervals = new int[n];
- System.arraycopy(_intervals, 0, intervals, 0, n);
- } else {
- frameIntervalType = -1;
- intervals = null;
- }
- frameIntervalIndex = 0;
- updateFrameRate();
- }
-
- /**
- * コピーコンストラクタ
- * @param other
- */
- public Size(final Size other) {
- type = other.type;
- frame_type = other.frame_type;
- index = other.index;
- width = other.width;
- height = other.height;
- frameIntervalType = other.frameIntervalType;
- frameIntervalIndex = other.frameIntervalIndex;
- final int n = other.intervals != null ? other.intervals.length : -1;
- if (n > 0) {
- intervals = new int[n];
- System.arraycopy(other.intervals, 0, intervals, 0, n);
- } else {
- intervals = null;
- }
- updateFrameRate();
- }
-
- private Size(final Parcel source) {
- // 読み取り順はwriteToParcelでの書き込み順と同じでないとダメ
- type = source.readInt();
- frame_type = source.readInt();
- index = source.readInt();
- width = source.readInt();
- height = source.readInt();
- frameIntervalType = source.readInt();
- frameIntervalIndex = source.readInt();
- if (frameIntervalType >= 0) {
- if (frameIntervalType > 0) {
- intervals = new int[frameIntervalType];
- } else {
- intervals = new int[3];
- }
- source.readIntArray(intervals);
- } else {
- intervals = null;
- }
- updateFrameRate();
- }
-
- public Size set(final Size other) {
- if (other != null) {
- type = other.type;
- frame_type = other.frame_type;
- index = other.index;
- width = other.width;
- height = other.height;
- frameIntervalType = other.frameIntervalType;
- frameIntervalIndex = other.frameIntervalIndex;
- final int n = other.intervals != null ? other.intervals.length : -1;
- if (n > 0) {
- intervals = new int[n];
- System.arraycopy(other.intervals, 0, intervals, 0, n);
- } else {
- intervals = null;
- }
- updateFrameRate();
- }
- return this;
- }
-
- public float getCurrentFrameRate() throws IllegalStateException {
- final int n = fps != null ? fps.length : 0;
- if ((frameIntervalIndex >= 0) && (frameIntervalIndex < n)) {
- return fps[frameIntervalIndex];
- }
- throw new IllegalStateException("unknown frame rate or not ready");
- }
-
- public void setCurrentFrameRate(final float frameRate) {
- // 一番近いのを選ぶ
- int index = -1;
- final int n = fps != null ? fps.length : 0;
- for (int i = 0; i < n; i++) {
- if (fps[i] <= frameRate) {
- index = i;
- break;
- }
- }
- frameIntervalIndex = index;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(final Parcel dest, final int flags) {
- dest.writeInt(type);
- dest.writeInt(frame_type);
- dest.writeInt(index);
- dest.writeInt(width);
- dest.writeInt(height);
- dest.writeInt(frameIntervalType);
- dest.writeInt(frameIntervalIndex);
- if (intervals != null) {
- dest.writeIntArray(intervals);
- }
- }
-
- public void updateFrameRate() {
- final int n = frameIntervalType;
- if (n > 0) {
- fps = new float[n];
- for (int i = 0; i < n; i++) {
- final float _fps = fps[i] = 10000000.0f / intervals[i];
- }
- } else if (n == 0) {
- try {
- final int min = Math.min(intervals[0], intervals[1]);
- final int max = Math.max(intervals[0], intervals[1]);
- final int step = intervals[2];
- if (step > 0) {
- int m = 0;
- for (int i = min; i <= max; i+= step) { m++; }
- fps = new float[m];
- m = 0;
- for (int i = min; i <= max; i+= step) {
- final float _fps = fps[m++] = 10000000.0f / i;
- }
- } else {
- final float max_fps = 10000000.0f / min;
- int m = 0;
- for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) { m++; }
- fps = new float[m];
- m = 0;
- for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) {
- this.fps[m++] = fps;
- }
- }
- } catch (final Exception e) {
- // ignore, なんでかminとmaxが0になってるんちゃうかな
- fps = null;
- }
- }
- final int m = fps != null ? fps.length : 0;
- final StringBuilder sb = new StringBuilder();
- sb.append("[");
- for (int i = 0; i < m; i++) {
- sb.append(String.format(Locale.US, "%4.1f", fps[i]));
- if (i < m-1) {
- sb.append(",");
- }
- }
- sb.append("]");
- frameRates = sb.toString();
- if (frameIntervalIndex > m) {
- frameIntervalIndex = 0;
- }
- }
-
- @Override
- public String toString() {
- float frame_rate = 0.0f;
- try {
- frame_rate = getCurrentFrameRate();
- } catch (final Exception e) {
- }
- return String.format(Locale.US, "Size(%dx%d@%4.1f,type:%d,frame:%d,index:%d,%s)", width, height, frame_rate, type, frame_type, index, frameRates);
- }
-
- public static final Creator CREATOR = new Parcelable.Creator() {
- @Override
- public Size createFromParcel(final Parcel source) {
- return new Size(source);
- }
- @Override
- public Size[] newArray(final int size) {
- return new Size[size];
- }
- };
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/USBMonitor.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/USBMonitor.java
deleted file mode 100755
index 936029feb4..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/USBMonitor.java
+++ /dev/null
@@ -1,1361 +0,0 @@
-/*
- * 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.io.UnsupportedEncodingException;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import android.Manifest;
-import android.annotation.SuppressLint;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbDeviceConnection;
-import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-
-import com.serenegiant.utils.BuildCheck;
-import com.serenegiant.utils.HandlerThreadHandler;
-
-
-public final class USBMonitor {
-
- private static final boolean DEBUG = false; // TODO set false on production
- private static final String TAG = "USBMonitor";
-
- private static final String ACTION_USB_PERMISSION_BASE = "com.serenegiant.USB_PERMISSION.";
- private final String ACTION_USB_PERMISSION = ACTION_USB_PERMISSION_BASE + hashCode();
-
- public static final String ACTION_USB_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
-
- /**
- * openしているUsbControlBlock
- */
- private final ConcurrentHashMap mCtrlBlocks = new ConcurrentHashMap();
- private final SparseArray> mHasPermissions = new SparseArray>();
-
- private final WeakReference mWeakContext;
- private final UsbManager mUsbManager;
- private final OnDeviceConnectListener mOnDeviceConnectListener;
- private PendingIntent mPermissionIntent = null;
- private List mDeviceFilters = new ArrayList();
-
- /**
- * コールバックをワーカースレッドで呼び出すためのハンドラー
- */
- private final Handler mAsyncHandler;
- private volatile boolean destroyed;
- /**
- * USB機器の状態変更時のコールバックリスナー
- */
- public interface OnDeviceConnectListener {
- /**
- * called when device attached
- * @param device
- */
- public void onAttach(UsbDevice device);
- /**
- * called when device dettach(after onDisconnect)
- * @param device
- */
- public void onDettach(UsbDevice device);
- /**
- * called after device opend
- * @param device
- * @param ctrlBlock
- * @param createNew
- */
- public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew);
- /**
- * called when USB device removed or its power off (this callback is called after device closing)
- * @param device
- * @param ctrlBlock
- */
- public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock);
- /**
- * called when canceled or could not get permission from user
- * @param device
- */
- public void onCancel(UsbDevice device);
- }
-
- public USBMonitor(final Context context, final OnDeviceConnectListener listener) {
- if (DEBUG) Log.v(TAG, "USBMonitor:Constructor");
- if (listener == null)
- throw new IllegalArgumentException("OnDeviceConnectListener should not null.");
- mWeakContext = new WeakReference(context);
- mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
- mOnDeviceConnectListener = listener;
- mAsyncHandler = HandlerThreadHandler.createHandler(TAG);
- destroyed = false;
- if (DEBUG) Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
- }
-
- /**
- * Release all related resources,
- * never reuse again
- */
- public void destroy() {
- if (DEBUG) Log.i(TAG, "destroy:");
- unregister();
- if (!destroyed) {
- destroyed = true;
- // モニターしているUSB機器を全てcloseする
- final Set keys = mCtrlBlocks.keySet();
- if (keys != null) {
- UsbControlBlock ctrlBlock;
- try {
- for (final UsbDevice key: keys) {
- ctrlBlock = mCtrlBlocks.remove(key);
- if (ctrlBlock != null) {
- ctrlBlock.close();
- }
- }
- } catch (final Exception e) {
- Log.e(TAG, "destroy:", e);
- }
- }
- mCtrlBlocks.clear();
- try {
- mAsyncHandler.getLooper().quit();
- } catch (final Exception e) {
- Log.e(TAG, "destroy:", e);
- }
- }
- }
-
- /**
- * register BroadcastReceiver to monitor USB events
- * @throws IllegalStateException
- */
- public synchronized void register() throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- if (mPermissionIntent == null) {
- 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);
- final IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
- // ACTION_USB_DEVICE_ATTACHED never comes on some devices so it should not be added here
- filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
- context.registerReceiver(mUsbReceiver, filter);
- }
- // start connection check
- mDeviceCounts = 0;
- mAsyncHandler.postDelayed(mDeviceCheckRunnable, 1000);
- }
- }
-
- /**
- * unregister BroadcastReceiver
- * @throws IllegalStateException
- */
- public synchronized void unregister() throws IllegalStateException {
- // 接続チェック用Runnableを削除
- mDeviceCounts = 0;
- if (!destroyed) {
- mAsyncHandler.removeCallbacks(mDeviceCheckRunnable);
- }
- if (mPermissionIntent != null) {
-// if (DEBUG) Log.i(TAG, "unregister:");
- final Context context = mWeakContext.get();
- try {
- if (context != null) {
- context.unregisterReceiver(mUsbReceiver);
- }
- } catch (final Exception e) {
- Log.w(TAG, e);
- }
- mPermissionIntent = null;
- }
- }
-
- public synchronized boolean isRegistered() {
- return !destroyed && (mPermissionIntent != null);
- }
-
- /**
- * set device filter
- * @param filter
- * @throws IllegalStateException
- */
- public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- mDeviceFilters.clear();
- mDeviceFilters.add(filter);
- }
-
- /**
- * デバイスフィルターを追加
- * @param filter
- * @throws IllegalStateException
- */
- public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- mDeviceFilters.add(filter);
- }
-
- /**
- * デバイスフィルターを削除
- * @param filter
- * @throws IllegalStateException
- */
- public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- mDeviceFilters.remove(filter);
- }
-
- /**
- * set device filters
- * @param filters
- * @throws IllegalStateException
- */
- public void setDeviceFilter(final List filters) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- mDeviceFilters.clear();
- mDeviceFilters.addAll(filters);
- }
-
- /**
- * add device filters
- * @param filters
- * @throws IllegalStateException
- */
- public void addDeviceFilter(final List filters) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- mDeviceFilters.addAll(filters);
- }
-
- /**
- * remove device filters
- * @param filters
- */
- public void removeDeviceFilter(final List filters) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- mDeviceFilters.removeAll(filters);
- }
-
- /**
- * return the number of connected USB devices that matched device filter
- * @return
- * @throws IllegalStateException
- */
- public int getDeviceCount() throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- return getDeviceList().size();
- }
-
- /**
- * return device list, return empty list if no device matched
- * @return
- * @throws IllegalStateException
- */
- public List getDeviceList() throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- return getDeviceList(mDeviceFilters);
- }
-
- /**
- * return device list, return empty list if no device matched
- * @param filters
- * @return
- * @throws IllegalStateException
- */
- public List getDeviceList(final List filters) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- final HashMap deviceList = mUsbManager.getDeviceList();
- final List result = new ArrayList();
- if (deviceList != null) {
- if ((filters == null) || filters.isEmpty()) {
- result.addAll(deviceList.values());
- } else {
- for (final UsbDevice device: deviceList.values() ) {
- for (final DeviceFilter filter: filters) {
- if ((filter != null) && filter.matches(device)) {
- // when filter matches
- if (!filter.isExclude) {
- result.add(device);
- }
- break;
- }
- }
- }
- }
- }
- return result;
- }
-
- /**
- * return device list, return empty list if no device matched
- * @param filter
- * @return
- * @throws IllegalStateException
- */
- public List getDeviceList(final DeviceFilter filter) throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- final HashMap deviceList = mUsbManager.getDeviceList();
- final List result = new ArrayList();
- if (deviceList != null) {
- for (final UsbDevice device: deviceList.values() ) {
- if ((filter == null) || (filter.matches(device) && !filter.isExclude)) {
- result.add(device);
- }
- }
- }
- return result;
- }
-
- /**
- * get USB device list, without filter
- * @return
- * @throws IllegalStateException
- */
- public Iterator getDevices() throws IllegalStateException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- Iterator iterator = null;
- final HashMap list = mUsbManager.getDeviceList();
- if (list != null)
- iterator = list.values().iterator();
- return iterator;
- }
-
- /**
- * output device list to LogCat
- */
- public final void dumpDevices() {
- final HashMap list = mUsbManager.getDeviceList();
- if (list != null) {
- final Set keys = list.keySet();
- if (keys != null && keys.size() > 0) {
- final StringBuilder sb = new StringBuilder();
- for (final String key: keys) {
- final UsbDevice device = list.get(key);
- final int num_interface = device != null ? device.getInterfaceCount() : 0;
- sb.setLength(0);
- for (int i = 0; i < num_interface; i++) {
- sb.append(String.format(Locale.US, "interface%d:%s", i, device.getInterface(i).toString()));
- }
- Log.i(TAG, "key=" + key + ":" + device + ":" + sb.toString());
- }
- } else {
- Log.i(TAG, "no device");
- }
- } else {
- Log.i(TAG, "no device");
- }
- }
-
- /**
- * return whether the specific Usb device has permission
- * @param device
- * @return true: 指定したUsbDeviceにパーミッションがある
- * @throws IllegalStateException
- */
- public final boolean hasPermission(final UsbDevice device) throws IllegalStateException, SecurityException {
- if (destroyed) throw new IllegalStateException("already destroyed");
- return updatePermission(device, device != null && mUsbManager.hasPermission(device));
- }
-
- /**
- * 内部で保持しているパーミッション状態を更新
- * @param device
- * @param hasPermission
- * @return hasPermission
- */
- private boolean updatePermission(final UsbDevice device, final boolean hasPermission) {
- final int deviceKey = getDeviceKey(device, true);
- synchronized (mHasPermissions) {
- if (hasPermission) {
- if (mHasPermissions.get(deviceKey) == null) {
- mHasPermissions.put(deviceKey, new WeakReference(device));
- }
- } else {
- mHasPermissions.remove(deviceKey);
- }
- }
- return hasPermission;
- }
-
- /**
- * request permission to access to USB device
- * @param device
- * @return true if fail to request permission
- */
- public synchronized boolean requestPermission(final UsbDevice device) {
-// if (DEBUG) Log.v(TAG, "requestPermission:device=" + device);
- boolean result = false;
- if (isRegistered()) {
- if (device != null) {
- if (mUsbManager.hasPermission(device)) {
- // call onConnect if app already has permission
- processConnect(device);
- } else {
- try {
- // パーミッションがなければ要求する
- mUsbManager.requestPermission(device, mPermissionIntent);
- } catch (final Exception e) {
- // Android5.1.xのGALAXY系でandroid.permission.sec.MDM_APP_MGMTという意味不明の例外生成するみたい
- Log.w(TAG, e);
- processCancel(device);
- result = true;
- }
- }
- } else {
- processCancel(device);
- result = true;
- }
- } else {
- processCancel(device);
- result = true;
- }
- return result;
- }
-
- /**
- * 指定したUsbDeviceをopenする
- * @param device
- * @return
- * @throws SecurityException パーミッションがなければSecurityExceptionを投げる
- */
- public UsbControlBlock openDevice(final UsbDevice device) throws SecurityException {
- if (hasPermission(device)) {
- UsbControlBlock result = mCtrlBlocks.get(device);
- if (result == null) {
- result = new UsbControlBlock(USBMonitor.this, device); // この中でopenDeviceする
- mCtrlBlocks.put(device, result);
- }
- return result;
- } else {
- throw new SecurityException("has no permission");
- }
- }
-
- /**
- * BroadcastReceiver for USB permission
- */
- private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(final Context context, final Intent intent) {
- if (destroyed) return;
- final String action = intent.getAction();
- if (ACTION_USB_PERMISSION.equals(action)) {
- // when received the result of requesting USB permission
- try {
- synchronized (USBMonitor.this) {
- final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
- if (device != null) {
- // get permission, call onConnect
- processConnect(device);
- }
- } else {
- // failed to get permission
- processCancel(device);
- }
- }
- } catch (SecurityException e ) {
- // ignore
- }
- } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
- final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- updatePermission(device, hasPermission(device));
- processAttach(device);
- } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
- // when device removed
- final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- if (device != null) {
- UsbControlBlock ctrlBlock = mCtrlBlocks.remove(device);
- if (ctrlBlock != null) {
- // cleanup
- ctrlBlock.close();
- }
- mDeviceCounts = 0;
- processDettach(device);
- }
- }
- }
- };
-
- /** number of connected & detected devices */
- private volatile int mDeviceCounts = 0;
- /**
- * periodically check connected devices and if it changed, call onAttach
- */
- private final Runnable mDeviceCheckRunnable = new Runnable() {
- @Override
- public void run() {
- if (destroyed) return;
- final List devices = getDeviceList();
- final int n = devices.size();
- final int hasPermissionCounts;
- final int m;
- synchronized (mHasPermissions) {
- hasPermissionCounts = mHasPermissions.size();
- mHasPermissions.clear();
- try {
- for (final UsbDevice device : devices) {
- hasPermission(device);
- }
- } catch (SecurityException e) {
- // ignore
- }
- m = mHasPermissions.size();
- }
- if ((n > mDeviceCounts) || (m > hasPermissionCounts)) {
- mDeviceCounts = n;
- if (mOnDeviceConnectListener != null) {
- for (int i = 0; i < n; i++) {
- final UsbDevice device = devices.get(i);
- mAsyncHandler.post(new Runnable() {
- @Override
- public void run() {
- mOnDeviceConnectListener.onAttach(device);
- }
- });
- }
- }
- }
- mAsyncHandler.postDelayed(this, 2000); // confirm every 2 seconds
- }
- };
-
- /**
- * open specific USB device
- * @param device
- */
- private final void processConnect(final UsbDevice device) {
- if (destroyed) return;
-
- updatePermission(device, true);
- mAsyncHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) Log.v(TAG, "processConnect:device=" + device);
- UsbControlBlock ctrlBlock;
- final boolean createNew;
- ctrlBlock = mCtrlBlocks.get(device);
- if (ctrlBlock == null) {
- ctrlBlock = new UsbControlBlock(USBMonitor.this, device);
- mCtrlBlocks.put(device, ctrlBlock);
- createNew = true;
- } else {
- createNew = false;
- }
- if (mOnDeviceConnectListener != null) {
- mOnDeviceConnectListener.onConnect(device, ctrlBlock, createNew);
- }
- }
- });
- }
-
- private final void processCancel(final UsbDevice device) {
- if (destroyed) return;
- if (DEBUG) Log.v(TAG, "processCancel:");
- updatePermission(device, false);
- if (mOnDeviceConnectListener != null) {
- mAsyncHandler.post(new Runnable() {
- @Override
- public void run() {
- mOnDeviceConnectListener.onCancel(device);
- }
- });
- }
- }
-
- private final void processAttach(final UsbDevice device) {
- if (destroyed) return;
- if (DEBUG) Log.v(TAG, "processAttach:");
- if (mOnDeviceConnectListener != null) {
- mAsyncHandler.post(new Runnable() {
- @Override
- public void run() {
- mOnDeviceConnectListener.onAttach(device);
- }
- });
- }
- }
-
- private final void processDettach(final UsbDevice device) {
- if (destroyed) return;
- if (DEBUG) Log.v(TAG, "processDettach:");
- if (mOnDeviceConnectListener != null) {
- mAsyncHandler.post(new Runnable() {
- @Override
- public void run() {
- mOnDeviceConnectListener.onDettach(device);
- }
- });
- }
- }
-
- /**
- * USB機器毎の設定保存用にデバイスキー名を生成する。
- * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
- * 同種の製品だと同じキー名になるので注意
- * @param device nullなら空文字列を返す
- * @return
- */
- public static final String getDeviceKeyName(final UsbDevice device) {
- return getDeviceKeyName(device, null, false);
- }
-
- /**
- * USB機器毎の設定保存用にデバイスキー名を生成する。
- * useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
- * @param device
- * @param useNewAPI
- * @return
- */
- public static final String getDeviceKeyName(final UsbDevice device, final boolean useNewAPI) {
- return getDeviceKeyName(device, null, useNewAPI);
- }
- /**
- * USB機器毎の設定保存用にデバイスキー名を生成する。この機器名をHashMapのキーにする
- * UsbDeviceがopenしている時のみ有効
- * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
- * serialがnullや空文字でなければserialを含めたデバイスキー名を生成する
- * useNewAPI=trueでAPIレベルを満たしていればマニュファクチャ名, バージョン, コンフィギュレーションカウントも使う
- * @param device nullなら空文字列を返す
- * @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
- * @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
- * @return
- */
- @SuppressLint("NewApi")
- public static final String getDeviceKeyName(final UsbDevice device, final String serial, final boolean useNewAPI)
- throws SecurityException {
- if (device == null) return "";
- final StringBuilder sb = new StringBuilder();
- sb.append(device.getVendorId()); sb.append("#"); // API >= 12
- sb.append(device.getProductId()); sb.append("#"); // API >= 12
- sb.append(device.getDeviceClass()); sb.append("#"); // API >= 12
- sb.append(device.getDeviceSubclass()); sb.append("#"); // API >= 12
- sb.append(device.getDeviceProtocol()); // API >= 12
- if (!TextUtils.isEmpty(serial)) {
- sb.append("#"); sb.append(serial);
- }
- if (useNewAPI && BuildCheck.isAndroid5()) {
- sb.append("#");
- if (TextUtils.isEmpty(serial)) {
- sb.append(device.getSerialNumber()); sb.append("#"); // API >= 21
- }
- sb.append(device.getManufacturerName()); sb.append("#"); // API >= 21
- sb.append(device.getConfigurationCount()); sb.append("#"); // API >= 21
- if (BuildCheck.isMarshmallow()) {
- sb.append(device.getVersion()); sb.append("#"); // API >= 23
- }
- }
-// if (DEBUG) Log.v(TAG, "getDeviceKeyName:" + sb.toString());
- return sb.toString();
- }
-
- /**
- * デバイスキーを整数として取得
- * getDeviceKeyNameで得られる文字列のhasCodeを取得
- * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
- * 同種の製品だと同じデバイスキーになるので注意
- * @param device nullなら0を返す
- * @return
- */
- public static final int getDeviceKey(final UsbDevice device) {
- return device != null ? getDeviceKeyName(device, null, false).hashCode() : 0;
- }
-
- /**
- * デバイスキーを整数として取得
- * getDeviceKeyNameで得られる文字列のhasCodeを取得
- * useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
- * @param device
- * @param useNewAPI
- * @return
- */
- public static final int getDeviceKey(final UsbDevice device, final boolean useNewAPI) {
- return device != null ? getDeviceKeyName(device, null, useNewAPI).hashCode() : 0;
- }
-
- /**
- * デバイスキーを整数として取得
- * getDeviceKeyNameで得られる文字列のhasCodeを取得
- * serialがnullでuseNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
- * @param device nullなら0を返す
- * @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
- * @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
- * @return
- */
- public static final int getDeviceKey(final UsbDevice device, final String serial, final boolean useNewAPI) {
- return device != null ? getDeviceKeyName(device, serial, useNewAPI).hashCode() : 0;
- }
-
- public static class UsbDeviceInfo {
- public String usb_version;
- public String manufacturer;
- public String product;
- public String version;
- public String serial;
-
- private void clear() {
- usb_version = manufacturer = product = version = serial = null;
- }
-
- @Override
- public String toString() {
- return String.format("UsbDevice:usb_version=%s,manufacturer=%s,product=%s,version=%s,serial=%s",
- usb_version != null ? usb_version : "",
- manufacturer != null ? manufacturer : "",
- product != null ? product : "",
- version != null ? version : "",
- serial != null ? serial : "");
- }
- }
-
- private static final int USB_DIR_OUT = 0;
- private static final int USB_DIR_IN = 0x80;
- private static final int USB_TYPE_MASK = (0x03 << 5);
- private static final int USB_TYPE_STANDARD = (0x00 << 5);
- private static final int USB_TYPE_CLASS = (0x01 << 5);
- private static final int USB_TYPE_VENDOR = (0x02 << 5);
- private static final int USB_TYPE_RESERVED = (0x03 << 5);
- private static final int USB_RECIP_MASK = 0x1f;
- private static final int USB_RECIP_DEVICE = 0x00;
- private static final int USB_RECIP_INTERFACE = 0x01;
- private static final int USB_RECIP_ENDPOINT = 0x02;
- private static final int USB_RECIP_OTHER = 0x03;
- private static final int USB_RECIP_PORT = 0x04;
- private static final int USB_RECIP_RPIPE = 0x05;
- private static final int USB_REQ_GET_STATUS = 0x00;
- private static final int USB_REQ_CLEAR_FEATURE = 0x01;
- private static final int USB_REQ_SET_FEATURE = 0x03;
- private static final int USB_REQ_SET_ADDRESS = 0x05;
- private static final int USB_REQ_GET_DESCRIPTOR = 0x06;
- private static final int USB_REQ_SET_DESCRIPTOR = 0x07;
- private static final int USB_REQ_GET_CONFIGURATION = 0x08;
- private static final int USB_REQ_SET_CONFIGURATION = 0x09;
- private static final int USB_REQ_GET_INTERFACE = 0x0A;
- private static final int USB_REQ_SET_INTERFACE = 0x0B;
- private static final int USB_REQ_SYNCH_FRAME = 0x0C;
- private static final int USB_REQ_SET_SEL = 0x30;
- private static final int USB_REQ_SET_ISOCH_DELAY = 0x31;
- private static final int USB_REQ_SET_ENCRYPTION = 0x0D;
- private static final int USB_REQ_GET_ENCRYPTION = 0x0E;
- private static final int USB_REQ_RPIPE_ABORT = 0x0E;
- private static final int USB_REQ_SET_HANDSHAKE = 0x0F;
- private static final int USB_REQ_RPIPE_RESET = 0x0F;
- private static final int USB_REQ_GET_HANDSHAKE = 0x10;
- private static final int USB_REQ_SET_CONNECTION = 0x11;
- private static final int USB_REQ_SET_SECURITY_DATA = 0x12;
- private static final int USB_REQ_GET_SECURITY_DATA = 0x13;
- private static final int USB_REQ_SET_WUSB_DATA = 0x14;
- private static final int USB_REQ_LOOPBACK_DATA_WRITE = 0x15;
- private static final int USB_REQ_LOOPBACK_DATA_READ = 0x16;
- private static final int USB_REQ_SET_INTERFACE_DS = 0x17;
-
- private static final int USB_REQ_STANDARD_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE); // 0x10
- private static final int USB_REQ_STANDARD_DEVICE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE); // 0x90
- private static final int USB_REQ_STANDARD_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE); // 0x11
- private static final int USB_REQ_STANDARD_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE); // 0x91
- private static final int USB_REQ_STANDARD_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT); // 0x12
- private static final int USB_REQ_STANDARD_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT); // 0x92
-
- private static final int USB_REQ_CS_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0x20
- private static final int USB_REQ_CS_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0xa0
- private static final int USB_REQ_CS_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0x21
- private static final int USB_REQ_CS_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0xa1
- private static final int USB_REQ_CS_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0x22
- private static final int USB_REQ_CS_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0xa2
-
- private static final int USB_REQ_VENDER_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0x40
- private static final int USB_REQ_VENDER_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0xc0
- private static final int USB_REQ_VENDER_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0x41
- private static final int USB_REQ_VENDER_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0xc1
- private static final int USB_REQ_VENDER_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0x42
- private static final int USB_REQ_VENDER_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0xc2
-
- private static final int USB_DT_DEVICE = 0x01;
- private static final int USB_DT_CONFIG = 0x02;
- private static final int USB_DT_STRING = 0x03;
- private static final int USB_DT_INTERFACE = 0x04;
- private static final int USB_DT_ENDPOINT = 0x05;
- private static final int USB_DT_DEVICE_QUALIFIER = 0x06;
- private static final int USB_DT_OTHER_SPEED_CONFIG = 0x07;
- private static final int USB_DT_INTERFACE_POWER = 0x08;
- private static final int USB_DT_OTG = 0x09;
- private static final int USB_DT_DEBUG = 0x0a;
- private static final int USB_DT_INTERFACE_ASSOCIATION = 0x0b;
- private static final int USB_DT_SECURITY = 0x0c;
- private static final int USB_DT_KEY = 0x0d;
- private static final int USB_DT_ENCRYPTION_TYPE = 0x0e;
- private static final int USB_DT_BOS = 0x0f;
- private static final int USB_DT_DEVICE_CAPABILITY = 0x10;
- private static final int USB_DT_WIRELESS_ENDPOINT_COMP = 0x11;
- private static final int USB_DT_WIRE_ADAPTER = 0x21;
- private static final int USB_DT_RPIPE = 0x22;
- private static final int USB_DT_CS_RADIO_CONTROL = 0x23;
- private static final int USB_DT_PIPE_USAGE = 0x24;
- private static final int USB_DT_SS_ENDPOINT_COMP = 0x30;
- private static final int USB_DT_CS_DEVICE = (USB_TYPE_CLASS | USB_DT_DEVICE);
- private static final int USB_DT_CS_CONFIG = (USB_TYPE_CLASS | USB_DT_CONFIG);
- private static final int USB_DT_CS_STRING = (USB_TYPE_CLASS | USB_DT_STRING);
- private static final int USB_DT_CS_INTERFACE = (USB_TYPE_CLASS | USB_DT_INTERFACE);
- private static final int USB_DT_CS_ENDPOINT = (USB_TYPE_CLASS | USB_DT_ENDPOINT);
- private static final int USB_DT_DEVICE_SIZE = 18;
-
- /**
- * 指定したIDのStringディスクリプタから文字列を取得する。取得できなければnull
- * @param connection
- * @param id
- * @param languageCount
- * @param languages
- * @return
- */
- private static String getString(final UsbDeviceConnection connection, final int id, final int languageCount, final byte[] languages) {
- final byte[] work = new byte[256];
- String result = null;
- for (int i = 1; i <= languageCount; i++) {
- int ret = connection.controlTransfer(
- USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
- USB_REQ_GET_DESCRIPTOR,
- (USB_DT_STRING << 8) | id, languages[i], work, 256, 0);
- if ((ret > 2) && (work[0] == ret) && (work[1] == USB_DT_STRING)) {
- // skip first two bytes(bLength & bDescriptorType), and copy the rest to the string
- try {
- result = new String(work, 2, ret - 2, "UTF-16LE");
- if (!"Љ".equals(result)) { // 変なゴミが返ってくる時がある
- break;
- } else {
- result = null;
- }
- } catch (final UnsupportedEncodingException e) {
- // ignore
- }
- }
- }
- return result;
- }
-
- /**
- * ベンダー名・製品名・バージョン・シリアルを取得する
- * @param device
- * @return
- */
- public UsbDeviceInfo getDeviceInfo(final UsbDevice device) {
- return updateDeviceInfo(mUsbManager, device, null);
- }
-
- /**
- * ベンダー名・製品名・バージョン・シリアルを取得する
- * #updateDeviceInfo(final UsbManager, final UsbDevice, final UsbDeviceInfo)のヘルパーメソッド
- * @param context
- * @param device
- * @return
- */
- public static UsbDeviceInfo getDeviceInfo(final Context context, final UsbDevice device) {
- return updateDeviceInfo((UsbManager)context.getSystemService(Context.USB_SERVICE), device, new UsbDeviceInfo());
- }
-
- /**
- * ベンダー名・製品名・バージョン・シリアルを取得する
- * @param manager
- * @param device
- * @param _info
- * @return
- */
- public static UsbDeviceInfo updateDeviceInfo(final UsbManager manager, final UsbDevice device, final UsbDeviceInfo _info) {
- final UsbDeviceInfo info = _info != null ? _info : new UsbDeviceInfo();
- info.clear();
-
- if (device != null) {
- if (BuildCheck.isLollipop()) {
- info.manufacturer = device.getManufacturerName();
- info.product = device.getProductName();
- info.serial = device.getSerialNumber();
- }
- if (BuildCheck.isMarshmallow()) {
- info.usb_version = device.getVersion();
- }
- if ((manager != null) && manager.hasPermission(device)) {
- final UsbDeviceConnection connection = manager.openDevice(device);
- final byte[] desc = connection.getRawDescriptors();
-
- if (TextUtils.isEmpty(info.usb_version)) {
- info.usb_version = String.format("%x.%02x", ((int)desc[3] & 0xff), ((int)desc[2] & 0xff));
- }
- if (TextUtils.isEmpty(info.version)) {
- info.version = String.format("%x.%02x", ((int)desc[13] & 0xff), ((int)desc[12] & 0xff));
- }
- if (TextUtils.isEmpty(info.serial)) {
- info.serial = connection.getSerial();
- }
-
- final byte[] languages = new byte[256];
- int languageCount = 0;
- // controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
- try {
- int result = connection.controlTransfer(
- USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
- USB_REQ_GET_DESCRIPTOR,
- (USB_DT_STRING << 8) | 0, 0, languages, 256, 0);
- if (result > 0) {
- languageCount = (result - 2) / 2;
- }
- if (languageCount > 0) {
- if (TextUtils.isEmpty(info.manufacturer)) {
- info.manufacturer = getString(connection, desc[14], languageCount, languages);
- }
- if (TextUtils.isEmpty(info.product)) {
- info.product = getString(connection, desc[15], languageCount, languages);
- }
- if (TextUtils.isEmpty(info.serial)) {
- info.serial = getString(connection, desc[16], languageCount, languages);
- }
- }
- } finally {
- connection.close();
- }
- }
- if (TextUtils.isEmpty(info.manufacturer)) {
- info.manufacturer = USBVendorId.vendorName(device.getVendorId());
- }
- if (TextUtils.isEmpty(info.manufacturer)) {
- info.manufacturer = String.format("%04x", device.getVendorId());
- }
- if (TextUtils.isEmpty(info.product)) {
- info.product = String.format("%04x", device.getProductId());
- }
- }
- return info;
- }
-
- /**
- * control class
- * never reuse the instance when it closed
- */
- public static final class UsbControlBlock implements Cloneable {
- private final WeakReference mWeakMonitor;
- private final WeakReference mWeakDevice;
- protected UsbDeviceConnection mConnection;
- protected final UsbDeviceInfo mInfo;
- private final int mBusNum;
- private final int mDevNum;
- private final SparseArray> mInterfaces = new SparseArray>();
-
- /**
- * this class needs permission to access USB device before constructing
- * @param monitor
- * @param device
- */
- private UsbControlBlock(final USBMonitor monitor, final UsbDevice device) {
- if (DEBUG) Log.i(TAG, "UsbControlBlock:constructor");
- mWeakMonitor = new WeakReference(monitor);
- mWeakDevice = new WeakReference(device);
- mConnection = monitor.mUsbManager.openDevice(device);
- mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
- final String name = device.getDeviceName();
- final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null;
- int busnum = 0;
- int devnum = 0;
- if (v != null) {
- busnum = Integer.parseInt(v[v.length-2]);
- devnum = Integer.parseInt(v[v.length-1]);
- }
- mBusNum = busnum;
- mDevNum = devnum;
-// if (DEBUG) {
- if (mConnection != null) {
- final int desc = mConnection.getFileDescriptor();
- final byte[] rawDesc = mConnection.getRawDescriptors();
- Log.i(TAG, String.format(Locale.US, "name=%s,desc=%d,busnum=%d,devnum=%d,rawDesc=", name, desc, busnum, devnum) + rawDesc);
- } else {
- Log.e(TAG, "could not connect to device " + name);
- }
-// }
- }
-
- /**
- * copy constructor
- * @param src
- * @throws IllegalStateException
- */
- private UsbControlBlock(final UsbControlBlock src) throws IllegalStateException {
- final USBMonitor monitor = src.getUSBMonitor();
- final UsbDevice device = src.getDevice();
- if (device == null) {
- throw new IllegalStateException("device may already be removed");
- }
- mConnection = monitor.mUsbManager.openDevice(device);
- if (mConnection == null) {
- throw new IllegalStateException("device may already be removed or have no permission");
- }
- mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
- mWeakMonitor = new WeakReference(monitor);
- mWeakDevice = new WeakReference(device);
- mBusNum = src.mBusNum;
- mDevNum = src.mDevNum;
- // FIXME USBMonitor.mCtrlBlocksに追加する(今はHashMapなので追加すると置き換わってしまうのでだめ, ListかHashMapにListをぶら下げる?)
- }
-
- /**
- * duplicate by clone
- * need permission
- * USBMonitor never handle cloned UsbControlBlock, you should release it after using it.
- * @return
- * @throws CloneNotSupportedException
- */
- @Override
- public UsbControlBlock clone() throws CloneNotSupportedException {
- final UsbControlBlock ctrlblock;
- try {
- ctrlblock = new UsbControlBlock(this);
- } catch (final IllegalStateException e) {
- throw new CloneNotSupportedException(e.getMessage());
- }
- return ctrlblock;
- }
-
- public USBMonitor getUSBMonitor() {
- return mWeakMonitor.get();
- }
-
- public final UsbDevice getDevice() {
- return mWeakDevice.get();
- }
-
- /**
- * get device name
- * @return
- */
- public String getDeviceName() {
- final UsbDevice device = mWeakDevice.get();
- return device != null ? device.getDeviceName() : "";
- }
-
- /**
- * get device id
- * @return
- */
- public int getDeviceId() {
- final UsbDevice device = mWeakDevice.get();
- return device != null ? device.getDeviceId() : 0;
- }
-
- /**
- * get device key string
- * @return same value if the devices has same vendor id, product id, device class, device subclass and device protocol
- */
- public String getDeviceKeyName() {
- return USBMonitor.getDeviceKeyName(mWeakDevice.get());
- }
-
- /**
- * get device key string
- * @param useNewAPI if true, try to use serial number
- * @return
- * @throws IllegalStateException
- */
- public String getDeviceKeyName(final boolean useNewAPI) throws IllegalStateException {
- if (useNewAPI) checkConnection();
- return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, useNewAPI);
- }
-
- /**
- * get device key
- * @return
- * @throws IllegalStateException
- */
- public int getDeviceKey() throws IllegalStateException {
- checkConnection();
- return USBMonitor.getDeviceKey(mWeakDevice.get());
- }
-
- /**
- * get device key
- * @param useNewAPI if true, try to use serial number
- * @return
- * @throws IllegalStateException
- */
- public int getDeviceKey(final boolean useNewAPI) throws IllegalStateException {
- if (useNewAPI) checkConnection();
- return USBMonitor.getDeviceKey(mWeakDevice.get(), mInfo.serial, useNewAPI);
- }
-
- /**
- * get device key string
- * if device has serial number, use it
- * @return
- */
- public String getDeviceKeyNameWithSerial() {
- return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, false);
- }
-
- /**
- * get device key
- * if device has serial number, use it
- * @return
- */
- public int getDeviceKeyWithSerial() {
- return getDeviceKeyNameWithSerial().hashCode();
- }
-
- /**
- * get UsbDeviceConnection
- * @return
- */
- public synchronized UsbDeviceConnection getConnection() {
- return mConnection;
- }
-
- /**
- * get file descriptor to access USB device
- * @return
- * @throws IllegalStateException
- */
- public synchronized int getFileDescriptor() throws IllegalStateException {
- checkConnection();
- return mConnection.getFileDescriptor();
- }
-
- /**
- * get raw descriptor for the USB device
- * @return
- * @throws IllegalStateException
- */
- public synchronized byte[] getRawDescriptors() throws IllegalStateException {
- checkConnection();
- return mConnection.getRawDescriptors();
- }
-
- /**
- * get vendor id
- * @return
- */
- public int getVenderId() {
- final UsbDevice device = mWeakDevice.get();
- return device != null ? device.getVendorId() : 0;
- }
-
- /**
- * get product id
- * @return
- */
- public int getProductId() {
- final UsbDevice device = mWeakDevice.get();
- return device != null ? device.getProductId() : 0;
- }
-
- /**
- * get version string of USB
- * @return
- */
- public String getUsbVersion() {
- return mInfo.usb_version;
- }
-
- /**
- * get manufacture
- * @return
- */
- public String getManufacture() {
- return mInfo.manufacturer;
- }
-
- /**
- * get product name
- * @return
- */
- public String getProductName() {
- return mInfo.product;
- }
-
- /**
- * get version
- * @return
- */
- public String getVersion() {
- return mInfo.version;
- }
-
- /**
- * get serial number
- * @return
- */
- public String getSerial() {
- return mInfo.serial;
- }
-
- public int getBusNum() {
- return mBusNum;
- }
-
- public int getDevNum() {
- return mDevNum;
- }
-
- /**
- * get interface
- * @param interface_id
- * @throws IllegalStateException
- */
- public synchronized UsbInterface getInterface(final int interface_id) throws IllegalStateException {
- return getInterface(interface_id, 0);
- }
-
- /**
- * get interface
- * @param interface_id
- * @param altsetting
- * @return
- * @throws IllegalStateException
- */
- public synchronized UsbInterface getInterface(final int interface_id, final int altsetting) throws IllegalStateException {
- checkConnection();
- SparseArray intfs = mInterfaces.get(interface_id);
- if (intfs == null) {
- intfs = new SparseArray();
- mInterfaces.put(interface_id, intfs);
- }
- UsbInterface intf = intfs.get(altsetting);
- if (intf == null) {
- final UsbDevice device = mWeakDevice.get();
- final int n = device.getInterfaceCount();
- for (int i = 0; i < n; i++) {
- final UsbInterface temp = device.getInterface(i);
- if ((temp.getId() == interface_id) && (temp.getAlternateSetting() == altsetting)) {
- intf = temp;
- break;
- }
- }
- if (intf != null) {
- intfs.append(altsetting, intf);
- }
- }
- return intf;
- }
-
- /**
- * open specific interface
- * @param intf
- */
- public synchronized void claimInterface(final UsbInterface intf) {
- claimInterface(intf, true);
- }
-
- public synchronized void claimInterface(final UsbInterface intf, final boolean force) {
- checkConnection();
- mConnection.claimInterface(intf, force);
- }
-
- /**
- * close interface
- * @param intf
- * @throws IllegalStateException
- */
- public synchronized void releaseInterface(final UsbInterface intf) throws IllegalStateException {
- checkConnection();
- final SparseArray intfs = mInterfaces.get(intf.getId());
- if (intfs != null) {
- final int index = intfs.indexOfValue(intf);
- intfs.removeAt(index);
- if (intfs.size() == 0) {
- mInterfaces.remove(intf.getId());
- }
- }
- mConnection.releaseInterface(intf);
- }
-
- /**
- * Close device
- * This also close interfaces if they are opened in Java side
- */
- public synchronized void close() {
- if (DEBUG) Log.i(TAG, "UsbControlBlock#close:");
-
- if (mConnection != null) {
- final int n = mInterfaces.size();
- for (int i = 0; i < n; i++) {
- final SparseArray intfs = mInterfaces.valueAt(i);
- if (intfs != null) {
- final int m = intfs.size();
- for (int j = 0; j < m; j++) {
- final UsbInterface intf = intfs.valueAt(j);
- mConnection.releaseInterface(intf);
- }
- intfs.clear();
- }
- }
- mInterfaces.clear();
- mConnection.close();
- mConnection = null;
- final USBMonitor monitor = mWeakMonitor.get();
- if (monitor != null) {
- if (monitor.mOnDeviceConnectListener != null) {
- monitor.mOnDeviceConnectListener.onDisconnect(mWeakDevice.get(), UsbControlBlock.this);
- }
- monitor.mCtrlBlocks.remove(getDevice());
- }
- }
- }
-
- @Override
- public boolean equals(final Object o) {
- if (o == null) return false;
- if (o instanceof UsbControlBlock) {
- final UsbDevice device = ((UsbControlBlock) o).getDevice();
- return device == null ? mWeakDevice.get() == null
- : device.equals(mWeakDevice.get());
- } else if (o instanceof UsbDevice) {
- return o.equals(mWeakDevice.get());
- }
- return super.equals(o);
- }
-
-// @Override
-// protected void finalize() throws Throwable {
-/// close();
-// super.finalize();
-// }
-
- private synchronized void checkConnection() throws IllegalStateException {
- if (mConnection == null) {
- throw new IllegalStateException("already closed");
- }
- }
- }
-
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/USBVendorId.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/USBVendorId.java
deleted file mode 100755
index d354b66ca7..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/USBVendorId.java
+++ /dev/null
@@ -1,859 +0,0 @@
-/*
- * 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;
-
-public class USBVendorId {
- private static final SparseArray IDS = new SparseArray();
-
- public static String vendorName(final int vendor_id) {
- return IDS.get(vendor_id);
- }
-
- static {
- IDS.put(10006, "YUEN DA ELECTRONIC PRODUCTS FACTORY");
- IDS.put(10013, "Gionee Communication Equipment Co., Ltd. ShenZhen");
- IDS.put(10022, "Universal Electronics Inc. (dba: TVIEW)");
- IDS.put(1003, "Atmel Corporation");
- IDS.put(1006, "Mitsumi");
- IDS.put(1008, "HP Inc.");
- IDS.put(10112, "M31 Technology Corp.");
- IDS.put(10113, "Liteconn Co., Ltd.");
- IDS.put(10121, "Suzhou WEIJU Electronics Technology Co., Ltd.");
- IDS.put(10144, "Mondokey Limited");
- IDS.put(10149, "Advantest Corporation");
- IDS.put(10150, "iRobot Corporation");
- IDS.put(1020, "Elitegroup Computer Systems");
- IDS.put(1021, "Xilinx Inc.");
- IDS.put(10226, "Sibridge Tech.");
- IDS.put(1026, "ALi Corporation");
- IDS.put(1027, "Future Technology Devices International Limited");
- IDS.put(10275, "Dongguan Jiumutong Industry Co., Ltd.");
- IDS.put(10289, "Power Integrations");
- IDS.put(10291, "Oculus VR, Inc.");
- IDS.put(10300, "HIGH TEK HARNESS ENTERPRISE CO., LTD.");
- IDS.put(10316, "Full in Hope Co., Ltd.");
- IDS.put(1032, "Quanta Computer Inc.");
- IDS.put(10329, "Viconn Technology (HK) Co., Ltd.");
- IDS.put(1033, "NEC Corporation");
- IDS.put(1035, "Weltrend Semiconductor");
- IDS.put(1037, "VIA Technologies, Inc.");
- IDS.put(10374, "Seeed Technology Co., Ltd.");
- IDS.put(10375, "Specwerkz");
- IDS.put(1038, "MCCI Corporation");
- IDS.put(10398, "Esselte Leitz GmbH & Co. KG");
- IDS.put(10406, "E-SEEK Inc.");
- IDS.put(1041, "BUFFALO INC.");
- IDS.put(10423, "Pleora Technologies Inc.");
- IDS.put(10431, "Vitetech Int'l Co., Ltd.");
- IDS.put(1044, "Giga-Byte Technology Co., Ltd.");
- IDS.put(10446, "Changzhou Shi Wujin Miqi East Electronic Co., Ltd.");
- IDS.put(10457, "Shenzhen Ourconn Technology Co., Ltd.");
- IDS.put(10458, "G.SKILL Int'l Enterprice Co., Ltd.");
- IDS.put(1046, "Nuvoton Technology Corp.");
- IDS.put(10466, "Surplus Electronic Technology Co., Ltd.");
- IDS.put(10470, "BIAMP SYSTEMS");
- IDS.put(10509, "IBCONN Technologies (Shenzhen) Co., Ltd.");
- IDS.put(10510, "Fugoo Inc.");
- IDS.put(10519, "Pan Xin Precision Electronics Co., Ltd.");
- IDS.put(10530, "Dongguan Digi-in Digital Technology Co., Ltd.");
- IDS.put(1054, "Creative Labs");
- IDS.put(10540, "GENUSION, Inc.");
- IDS.put(10544, "Ineda Systems Inc.");
- IDS.put(10545, "Jolla Ltd.");
- IDS.put(10546, "Peraso Technologies, Inc.");
- IDS.put(10549, "Nanjing Magewell Electronics Co., Ltd.");
- IDS.put(10560, "Shenzhen Yiwanda Electronics Co., Ltd.");
- IDS.put(1057, "Nokia Corporation");
- IDS.put(10575, "Dollar Connection Ltd.");
- IDS.put(10595, "BIO-key International, Inc.");
- IDS.put(1060, "Microchip-SMSC");
- IDS.put(10603, "Xacti Corporation");
- IDS.put(10615, "Shenzhen Zowee Technology Co., Ltd.");
- IDS.put(10643, "ADPlaus Technology Limited");
- IDS.put(10646, "Unwired Technology");
- IDS.put(1065, "Cirrus Logic Inc.");
- IDS.put(10657, "Union Electric Plug & Connector Corp.");
- IDS.put(10674, "Canova Tech");
- IDS.put(10685, "Silicon Works");
- IDS.put(10695, "HANRICO ANFU ELECTRONICS CO., LTD.");
- IDS.put(10700, "Kodak Alaris");
- IDS.put(10702, "JGR Optics Inc.");
- IDS.put(10703, "Richtek Technology Corporation");
- IDS.put(10705, "Binatone Electronics Int. Ltd.");
- IDS.put(1071, "Molex Inc.");
- IDS.put(10715, "Shenzhen iBoard Technology Co., Ltd.");
- IDS.put(10719, "SMIT(HK) Limited");
- IDS.put(1072, "Fujitsu Component Limited");
- IDS.put(10725, "Dongguan Kechenda Electronic Technology Co., Ltd.");
- IDS.put(10726, "Fengshun Peiying Electro-Acoustic Co., Ltd.");
- IDS.put(10744, "MD ELEKTRONIK GmbH");
- IDS.put(10749, "Bad Elf, LLC");
- IDS.put(10770, "Vreo Limited");
- IDS.put(10772, "Kanex");
- IDS.put(10781, "Oxford Nanopore Technologies");
- IDS.put(10782, "Obsidian Technology");
- IDS.put(10783, "Lucent Trans Electronics Co., Ltd.");
- IDS.put(10784, "GUOGUANG GROUP CO., LTD.");
- IDS.put(10788, "CNPLUS");
- IDS.put(10789, "Fourstar Group");
- IDS.put(10790, "Tragant International Co., Ltd.");
- IDS.put(10791, "DongGuan LianGang Optoelectronic Technology Co., Ltd.");
- IDS.put(10797, "Atrust Computer Corp.");
- IDS.put(10798, "VIA Alliance Semiconductor Co., Ltd.");
- IDS.put(10799, "BSUN Electronics Co., Ltd.");
- IDS.put(1080, "Advanced Micro Devices");
- IDS.put(10807, "RTD Embedded Technologies, Inc.");
- IDS.put(10816, "Shenzhen Choseal Industrial Co., Ltd.");
- IDS.put(10817, "Canyon Semiconductor");
- IDS.put(10818, "Spectra7 Microsystems Corp.");
- IDS.put(10821, "Meizu Technology Co., Ltd.");
- IDS.put(10822, "Hubei Yingtong Telecommunication Cable Inc.");
- IDS.put(10829, "Wilder Technologies");
- IDS.put(10837, "Diodes Inc.");
- IDS.put(10846, "DuPont");
- IDS.put(1085, "Lexmark International Inc.");
- IDS.put(10852, "Zhejiang Songcheng Electronics Co., Ltd.");
- IDS.put(10859, "VSN Mobil");
- IDS.put(10875, "Bellwether Electronic Corp.");
- IDS.put(10878, "VAIO Corporation");
- IDS.put(10879, "Perixx Computer GmbH");
- IDS.put(10885, "HANK ELECTRONICS CO., LTD");
- IDS.put(10892, "Sonnet Technologies, Inc.");
- IDS.put(10893, "Keysight Technologies Inc.");
- IDS.put(10895, "Manutronics Vietnam Joint Stock Company");
- IDS.put(10900, "G2 Touch Co., Ltd.");
- IDS.put(10902, "Micromax Informatics Ltd");
- IDS.put(10910, "SEIKO SOLUTIONS Inc.");
- IDS.put(10912, "Casco Products Corp.");
- IDS.put(10922, "Virtium Technology, Inc.");
- IDS.put(10923, "Field and Company LLC, dba Leef USA");
- IDS.put(10928, "GM Global Technology Operations LLC");
- IDS.put(10931, "Key Asic Inc.");
- IDS.put(10943, "Revolabs, Inc.");
- IDS.put(10945, "Lattice Semiconductor Corp");
- IDS.put(10947, "Foshan Nanhai Saga Audio Equipment Co., Ltd.");
- IDS.put(10957, "Silergy Corp.");
- IDS.put(10963, "Shenzhen Hali-Power Industrial Co., Ltd.");
- IDS.put(10971, "I-PEX (Dai-ichi Seiko)");
- IDS.put(10973, "SEE-PLUS INDUSTRIAL LTD.");
- IDS.put(10990, "Adapt-IP Company");
- IDS.put(10997, "Libratone A/S");
- IDS.put(10999, "Shenzhen Hazens Automotive Electronics (SZ) Co., Ltd.");
- IDS.put(11000, "Jiangsu Toppower Automotive Electronics Co., Ltd.");
- IDS.put(11001, "Drapho Electronics Technology Co., Ltd.");
- IDS.put(1102, "Alps Electric Co., Ltd.");
- IDS.put(11022, "Le Shi Zhi Xin Electronic Technology (Tian Jin) Limited");
- IDS.put(11024, "Cardiac Insight, Inc.");
- IDS.put(11028, "EverPro Technologies Company, Ltd.");
- IDS.put(11029, "Rosenberger Hochfrequenztechnik");
- IDS.put(11035, "Dongguan City Sanji Electronics Co., Ltd.");
- IDS.put(11037, "Lintes Technology Co., Ltd.");
- IDS.put(11039, "KinnexA, Inc.");
- IDS.put(11042, "Metra Electronics Corp.");
- IDS.put(11044, "KeepKey, LLC");
- IDS.put(11047, "FluxData Incorporated");
- IDS.put(1105, "Texas Instruments");
- IDS.put(11061, "Assem Technology Co., Ltd.");
- IDS.put(11062, "Dongguan City Jianghan Electronics Co., Ltd.");
- IDS.put(11063, "Huizhou Desay SV Automotive Co., Ltd.");
- IDS.put(11064, "Ningbo Rixing Electronics Co., Ltd.");
- IDS.put(11069, "GuangDong YuanFeng Automotive Electroics Co., Ltd.");
- IDS.put(11080, "Sounding Audio Industrial Limited");
- IDS.put(11082, "Yueqing Huaxin Electronic Co., Ltd.");
- IDS.put(11098, "Universal Audio, Inc.");
- IDS.put(11111, "Lifesize, Inc.");
- IDS.put(11123, "Pioneer DJ Corporation");
- IDS.put(11124, "Embedded Intelligence, Inc.");
- IDS.put(11125, "New Matter");
- IDS.put(11126, "Shanghai Wingtech Electronic Technology Co., Ltd.");
- IDS.put(11127, "Epiphan Systems Inc.");
- IDS.put(11130, "Spin Master Far East Ltd.");
- IDS.put(11131, "Gigaset Digital Technology (Shenzhen) Co., Ltd.");
- IDS.put(11132, "Noveltek Semiconductor Corp.");
- IDS.put(11139, "Silicon Line GmbH");
- IDS.put(11140, "Ever Win International Corp.");
- IDS.put(11144, "Socionext Inc.");
- IDS.put(11145, "Ugreen Group Limited");
- IDS.put(11146, "Shanghai Pateo Electronic Equipment Mfg. Co., Ltd.");
- IDS.put(1115, "Renesas Electronics Corp.");
- IDS.put(11154, "i-BLADES, Inc.");
- IDS.put(11155, "Altia Systems Inc.");
- IDS.put(11156, "ShenZhen Baoyuanda Electronics Co., Ltd.");
- IDS.put(11157, "iST - Integrated Service Technology Inc.");
- IDS.put(11158, "HYUNDAI MOBIS Co., Ltd.");
- IDS.put(11161, "360fly, Inc.");
- IDS.put(11162, "HUIZHOU CHENG SHUO HARDWARE PLASTIC CO., LTD.");
- IDS.put(11163, "Zhongshan Aute Electronics Technology Co., Ltd.");
- IDS.put(11164, "Guangdong King Link Industrial Co., Ltd.");
- IDS.put(11167, "Scietera Technologies, Inc.");
- IDS.put(11168, "InVue Security Products");
- IDS.put(11169, "I-Sheng Electric Wire & Cable Co., Ltd.");
- IDS.put(11170, "China Daheng Group Inc Beijing Image Vision Tech Branch");
- IDS.put(11171, "Shenzhen FeiTianXia Technology Ltd.");
- IDS.put(11172, "Shenzhen HengJia New Energy Auto Part Co., Ltd.");
- IDS.put(11175, "77 Elektronika Kft.");
- IDS.put(11176, "YUDU EASON ELECTRONIC CO., LTD.");
- IDS.put(1118, "Microsoft Corporation");
- IDS.put(11181, "XIN JI (SHENZHEN) COMPUTER PARTS CO., LTD.");
- IDS.put(11189, "Silk ID Systems");
- IDS.put(11190, "3D Imaging & Simulations Corp. (3DISC)");
- IDS.put(11191, "Dongguan ChengXiang Industrial Co., Ltd.");
- IDS.put(11192, "OCC (Zhuhai) Electronic Co., Ltd.");
- IDS.put(11194, "Sinseader Electronic Co., Ltd.");
- IDS.put(11195, "DONGGUAN YELLOWKNIFE Industrial Co., Ltd.");
- IDS.put(11197, "RF Creations Ltd.");
- IDS.put(11198, "Chengyi Semiconductors (Shanghai) Co., Ltd.");
- IDS.put(11199, "Shenzhen Shinning Electronic Co., Ltd.");
- IDS.put(11200, "Shenzhen WFD Electronics Co., Ltd.");
- IDS.put(11201, "Dongguan Sino Syncs Industrial Co., Ltd.");
- IDS.put(11202, "JNTC Co., Ltd.");
- IDS.put(11208, "DONGGUAN POLIXIN ELECTRIC CO., LTD.");
- IDS.put(11209, "Tama Electric (Suzhou) Co., Ltd.");
- IDS.put(1121, "Primax Electronics");
- IDS.put(11210, "Exvision, Inc.");
- IDS.put(11216, "mophie, LLC");
- IDS.put(11219, "Dongguan ULT-unite electronic technology co., LTD");
- IDS.put(11220, "JL Audio, Inc.");
- IDS.put(11221, "Cable Matters Inc.");
- IDS.put(11222, "CoroWare, Inc.");
- IDS.put(11229, "Charm Sciences Inc.");
- IDS.put(1123, "EATON");
- IDS.put(11230, "Pickering Interfaces Limited");
- IDS.put(11231, "Hangzhou Hikvision Digital Technology Co., Ltd.");
- IDS.put(11232, "FULLINK ELECTRONICS TECHNOLOGY (SZ) LTD");
- IDS.put(11233, "AutoChips Inc.");
- IDS.put(11234, "Electric Connector Technology Co., Ltd.");
- IDS.put(11237, "LELTEK");
- IDS.put(11238, "Dongguan KaiWin Electronics Co., Ltd.");
- IDS.put(11239, "BEFS Co., Ltd.");
- IDS.put(11240, "Archisite, Inc.");
- IDS.put(11241, "Magneti Marelli S.p.A Electr BL");
- IDS.put(11246, "Ventev Mobile");
- IDS.put(11247, "Quanta Storage Inc.");
- IDS.put(11248, "Tech-Top Technology Limited");
- IDS.put(11253, "Shenzhen YOOBAO Technology Co., Ltd.");
- IDS.put(11254, "Shenzhen Sinotek Technology Co., Ltd.");
- IDS.put(11255, "KEYW");
- IDS.put(11256, "Visual Land Inc.");
- IDS.put(11264, "MEEM SL Ltd");
- IDS.put(11265, "Dongguan Arin Electronics Technology Co., Ltd.");
- IDS.put(11266, "DongGuan City JianNuo Electronics Co., Ltd.");
- IDS.put(11268, "Shenzhen XOX Electronics Co., Ltd.");
- IDS.put(11269, "Protop International Inc.");
- IDS.put(11270, "Microsemi Semiconductor (US) Inc.");
- IDS.put(11271, "Webcloak LLC");
- IDS.put(11272, "INVECAS INC.");
- IDS.put(11274, "ATANS Technology Inc.");
- IDS.put(11275, "Triple Win Precision Technology Co., Ltd.");
- IDS.put(11276, "IC Realtech");
- IDS.put(11277, "Embrava Pty Ltd");
- IDS.put(1128, "Wieson Technologies Co., Ltd.");
- IDS.put(11280, "Sinotronics Co., Ltd.");
- IDS.put(11281, "ALLBEST ELECTRONICS TECHNOLOGY CO., LTD.");
- IDS.put(11282, "Shenzhen Xin Kai Feng Electronics Factory");
- IDS.put(11283, "MOST WELL Technology Corp.");
- IDS.put(11284, "Buffalo Memory Co., Ltd.");
- IDS.put(11285, "Xentris Wireless");
- IDS.put(11286, "Priferential Accessories Ltd");
- IDS.put(11289, "Sunlike Technology Co., Ltd.");
- IDS.put(11290, "Young Fast Optoelectronics Co., Ltd.");
- IDS.put(11291, "ISAW Camera Inc");
- IDS.put(11298, "Qanba USA, LLC");
- IDS.put(11299, "Super Micro Computer Inc.");
- IDS.put(11302, "Micromax International Corporation");
- IDS.put(11304, "Granite River Labs Japan Ltd.");
- IDS.put(11305, "Coagent Enterprise Limited");
- IDS.put(11306, "LEIA Inc.");
- IDS.put(11309, "Shenzhen Ebull Technology Limited");
- IDS.put(1131, "American Megatrends");
- IDS.put(11310, "Hualun Technology Co., Ltd.");
- IDS.put(11311, "Sensel, Inc.");
- IDS.put(11319, "Shenzhen Adition Audio Science & Technology Co., Ltd.");
- IDS.put(11320, "Goldenconn Electronics Technology (Suzhou) Co., Ltd.");
- IDS.put(11321, "JIB Electronics Technology Co., Ltd.");
- IDS.put(11322, "Changzhou Shinco Automotive Electronics Co., Ltd.");
- IDS.put(11323, "Shenzhen Hangsheng Electronics Corp., Ltd.");
- IDS.put(11324, "Beartooth Radio, Inc.");
- IDS.put(11325, "Audience, A Knowles Company");
- IDS.put(11327, "Nextbit Systems, Inc.");
- IDS.put(11328, "Leadtrend");
- IDS.put(11329, "Adaptertek Technology Co., Ltd.");
- IDS.put(1133, "Logitech Inc.");
- IDS.put(11330, "Feature Integration Technology Inc.");
- IDS.put(11331, "Avegant Corporation");
- IDS.put(11335, "Chunghsin International Electronics Co., Ltd.");
- IDS.put(11336, "Delphi Electrical Centers (Shanghai) Co., Ltd.");
- IDS.put(11341, "VVETEK DOO");
- IDS.put(11347, "Huizhou Foryou General Electronics Co., Ltd.");
- IDS.put(11348, "LifeWatch Technologies Ltd.");
- IDS.put(11349, "Magicleap");
- IDS.put(11355, "Dongguan City Shenglan Electronics Co., LTD.");
- IDS.put(11356, "Neusoft Corporation");
- IDS.put(11357, "SIP Simya Electronics Technology Co., Ltd.");
- IDS.put(11358, "GNSD Automotive Co., Ltd.");
- IDS.put(11359, "YOODS Co., Ltd.");
- IDS.put(11360, "Sirin Mobile Technologies AG");
- IDS.put(11361, "Jadmam Corporation dba: Boytone");
- IDS.put(11373, "Gibson Innovations");
- IDS.put(11374, "Shen Zhen Xian Shuo Technology Co. LTD");
- IDS.put(11375, "PST Eletronica LTDA");
- IDS.put(11376, "PERI, Inc.");
- IDS.put(11377, "Bozhou BoTong Information Technology Co., Ltd.");
- IDS.put(11383, "Profindustry GmbH");
- IDS.put(11384, "BRAGI GmbH");
- IDS.put(11385, "WAWGD, Inc. (DBA: Foresight Sports)");
- IDS.put(11390, "Dongguan Allpass Electronic Co., Ltd.");
- IDS.put(11391, "SHENZHEN D-VITEC INDUSTRIAL CO., LTD.");
- IDS.put(11392, "motomobile AG");
- IDS.put(11393, "Indie Semiconductor");
- IDS.put(11397, "Audientes");
- IDS.put(11403, "Huizhou Dehong Technology Co., Ltd.");
- IDS.put(11404, "PowerCenter Technology Limited");
- IDS.put(11405, "Mizco International, Inc.");
- IDS.put(11408, "I. AM. PLUS, LLC");
- IDS.put(11409, "Corigine, Inc.");
- IDS.put(11410, "Ningbo Yinzhou Shengke Electronics Co., Ltd.");
- IDS.put(11417, "Prusa Research s.r.o.");
- IDS.put(11423, "e-Smart Systems Pvt. Ltd.");
- IDS.put(11424, "Leagtech Jiangxi Electronic Co., Ltd.");
- IDS.put(11425, "Dongguan Yujia Electronics Technology Co., Ltd.");
- IDS.put(11426, "GuangZhou MingPing Electronics Technology");
- IDS.put(11427, "DJI Technology Co., Ltd.");
- IDS.put(11428, "Shenzhen Alex Technology Co., Ltd.");
- IDS.put(11433, "JITS TECHNOLOGY CO., LIMITED");
- IDS.put(11434, "LIVV Brand llc");
- IDS.put(11444, "Ava Enterprises, Inc. dba: Boss Audio Systems");
- IDS.put(11448, "Shenzhen Sydixon Electronic Technology Co., Ltd.");
- IDS.put(11449, "On-Bright Electronics (Shanghai) Co., Ltd.");
- IDS.put(11450, "Dongguan Puxu Industrial Co., Ltd.");
- IDS.put(11451, "Shenzhen Soling Indusrtial Co., Ltd.");
- IDS.put(11453, "EGGCYTE, INC.");
- IDS.put(11455, "Donggguan Yuhua Electronic Co., Ltd.");
- IDS.put(11456, "Hangzhou Zero Zero Technology Co., Ltd.");
- IDS.put(11462, "Prodigy Technovations Pvt Ltd");
- IDS.put(11463, "EmergiTech, Inc");
- IDS.put(11464, "Hewlett Packard Enterprise");
- IDS.put(11465, "Monolithic Power Systems Inc.");
- IDS.put(11467, "USB Memory Direct");
- IDS.put(11468, "Silicon Mitus Inc.");
- IDS.put(11472, "Technics Global Electronics & JCE Co., Ltd.");
- IDS.put(11478, "Immersive Media");
- IDS.put(11479, "Cosemi Technologies Inc.");
- IDS.put(11481, "Cambrionix Ltd");
- IDS.put(11482, "CXUN Co. Ltd.");
- IDS.put(11483, "China Tsp Inc");
- IDS.put(11490, "Yanfeng Visteon (Chongqing) Automotive Electronics Co");
- IDS.put(11491, "Alcorlink Corp.");
- IDS.put(11492, "ISBC Ltd.");
- IDS.put(11493, "InX8 Inc dba: AKiTiO");
- IDS.put(11494, "SDAN Tecchnology Co., Ltd.");
- IDS.put(11495, "Lemobile Information Technology (Beijing) Co., Ltd.");
- IDS.put(11496, "GongGuan HWX Electronic Technology Co., Ltd.");
- IDS.put(11497, "Suzhu Jingshi Electronic Technology Co., Ltd.");
- IDS.put(11498, "Zhong Shan City Richsound Electronic Industrial Ltd.");
- IDS.put(11499, "Dongguang Kangbang Electronics Co., Ltd.");
- IDS.put(1151, "Plantronics, Inc.");
- IDS.put(1154, "Kyocera Corporation");
- IDS.put(1155, "STMicroelectronics");
- IDS.put(1161, "Foxconn / Hon Hai");
- IDS.put(1165, "ITE Tech Inc.");
- IDS.put(1177, "Yamaha Corporation");
- IDS.put(1188, "Hitachi, Ltd.");
- IDS.put(1191, "Visioneer");
- IDS.put(1193, "Canon Inc.");
- IDS.put(1200, "Nikon Corporation");
- IDS.put(1201, "Pan International");
- IDS.put(1204, "Cypress Semiconductor");
- IDS.put(1205, "ROHM Co., Ltd.");
- IDS.put(1207, "Compal Electronics, Inc.");
- IDS.put(1208, "Seiko Epson Corp.");
- IDS.put(1211, "I-O Data Device, Inc.");
- IDS.put(1221, "Fujitsu Ltd.");
- IDS.put(1227, "FUJIFILM Corporation");
- IDS.put(1238, "Mentor Graphics");
- IDS.put(1240, "Microchip Technology Inc.");
- IDS.put(1241, "Holtek Semiconductor, Inc.");
- IDS.put(1242, "Panasonic Corporation");
- IDS.put(1245, "Sharp Corporation");
- IDS.put(1250, "Exar Corporation");
- IDS.put(1254, "Identiv, Inc.");
- IDS.put(1256, "Samsung Electronics Co., Ltd.");
- IDS.put(1260, "Tokyo Electron Device Limited");
- IDS.put(1266, "Chicony Electronics Co., Ltd.");
- IDS.put(1271, "Newnex Technology Corp.");
- IDS.put(1273, "Brother Industries, Ltd.");
- IDS.put(1276, "SUNPLUS TECHNOLOGY CO., LTD.");
- IDS.put(1278, "PFU Limited");
- IDS.put(1281, "Fujikura/DDK");
- IDS.put(1282, "Acer, Inc.");
- IDS.put(1287, "Hosiden Corporation");
- IDS.put(1293, "Belkin International, Inc.");
- IDS.put(1300, "FCI Electronics");
- IDS.put(1302, "Longwell Electronics/Longwell Company");
- IDS.put(1305, "Star Micronics Co., LTD");
- IDS.put(1309, "American Power Conversion");
- IDS.put(1314, "ACON, Advanced-Connectek, Inc.");
- IDS.put(1343, "Synopsys, Inc.");
- IDS.put(1356, "Sony Corporation");
- IDS.put(1360, "Fuji Xerox Co., Ltd.");
- IDS.put(1367, "ATEN International Co. Ltd.");
- IDS.put(1369, "Cadence Design Systems, Inc.");
- IDS.put(1386, "WACOM Co., Ltd.");
- IDS.put(1389, "EIZO Corporation");
- IDS.put(1390, "Elecom Co., Ltd.");
- IDS.put(1394, "Conexant Systems, Inc.");
- IDS.put(1398, "BAFO/Quality Computer Accessories");
- IDS.put(1403, "Y-E Data, Inc.");
- IDS.put(1404, "AVM GmbH");
- IDS.put(1410, "Roland Corporation");
- IDS.put(1412, "RATOC Systems, Inc.");
- IDS.put(1419, "Infineon Technologies");
- IDS.put(1423, "Alcor Micro, Corp.");
- IDS.put(1424, "OMRON Corporation");
- IDS.put(1447, "Bose Corporation");
- IDS.put(1449, "OmniVision Technologies, Inc.");
- IDS.put(1452, "Apple");
- IDS.put(1453, "Y.C. Cable U.S.A., Inc");
- IDS.put(14627, "National Instruments");
- IDS.put(1470, "Tyco Electronics Corp., a TE Connectivity Ltd. company");
- IDS.put(1473, "MegaChips Corporation");
- IDS.put(1478, "Qualcomm, Inc");
- IDS.put(1480, "Foxlink/Cheng Uei Precision Industry Co., Ltd.");
- IDS.put(1482, "Ricoh Company Ltd.");
- IDS.put(1498, "Microtek International Inc.");
- IDS.put(1504, "Symbol Technologies");
- IDS.put(1507, "Genesys Logic, Inc.");
- IDS.put(1509, "Fuji Electric Co., Ltd.");
- IDS.put(1525, "Unixtar Technology Inc.");
- IDS.put(1529, "Datalogic ADC");
- IDS.put(1535, "LeCroy Corporation");
- IDS.put(1539, "Novatek Microelectronics Corp.");
- IDS.put(1545, "SMK Manufacturing Inc.");
- IDS.put(1551, "Joinsoon Electronics Mfg. Co., Ltd.");
- IDS.put(1555, "TransAct Technologies Incorporated");
- IDS.put(1561, "Seiko Instruments Inc.");
- IDS.put(1582, "JPC/MAIN SUPER Inc.");
- IDS.put(1583, "Sin Sheng Terminal & Machine Inc.");
- IDS.put(1593, "Chrontel, Inc.");
- IDS.put(1611, "Analog Devices, Inc. Development Tools");
- IDS.put(1612, "Ji-Haw Industrial Co., Ltd");
- IDS.put(1614, "Suyin Corporation");
- IDS.put(1621, "Space Shuttle Hi-Tech Co.,Ltd.");
- IDS.put(1622, "Glory Mark Electronic Ltd.");
- IDS.put(1623, "Tekcon Electronics Corp.");
- IDS.put(1624, "Sigma Designs, Inc.");
- IDS.put(1631, "Good Way Technology Co., Ltd. & GWC technology Inc");
- IDS.put(1632, "TSAY-E (BVI) International Inc.");
- IDS.put(1633, "Hamamatsu Photonics K.K.");
- IDS.put(1642, "Total Technologies, Ltd.");
- IDS.put(1659, "Prolific Technology, Inc.");
- IDS.put(16700, "Dell Inc.");
- IDS.put(1680, "Golden Bridge Electech Inc.");
- IDS.put(1689, "Tektronix, Inc.");
- IDS.put(1690, "Askey Computer Corporation");
- IDS.put(1709, "Greatland Electronics Taiwan Ltd.");
- IDS.put(1710, "Eurofins Digital Testing Belgium");
- IDS.put(1720, "Pixela Corporation");
- IDS.put(1724, "Oki Data Corporation");
- IDS.put(1727, "Leoco Corporation");
- IDS.put(1732, "Bizlink Technology, Inc.");
- IDS.put(1736, "SIIG, Inc.");
- IDS.put(1747, "Mitsubishi Electric Corporation");
- IDS.put(1758, "Heisei Technology Co., Ltd.");
- IDS.put(1802, "Oki Electric Industry Co., Ltd.");
- IDS.put(1805, "Comoss Electronic Co., Ltd.");
- IDS.put(1809, "Magic Control Technology Corp.");
- IDS.put(1816, "Imation Corp.");
- IDS.put(1838, "Sunix Co., Ltd.");
- IDS.put(1846, "Lorom Industrial Co., Ltd.");
- IDS.put(1848, "Mad Catz, Inc.");
- IDS.put(1899, "HID Global GmbH");
- IDS.put(1901, "Denso Corporation");
- IDS.put(1913, "Fairchild Semiconductor");
- IDS.put(1921, "SanDisk Corporation");
- IDS.put(1937, "Copartner Technology Corporation");
- IDS.put(1954, "National Technical Systems");
- IDS.put(1971, "Plustek, Inc.");
- IDS.put(1972, "OLYMPUS CORPORATION");
- IDS.put(1975, "TIME Interconnect Ltd.");
- IDS.put(1994, "AVerMedia Technologies, Inc.");
- IDS.put(1999, "Casio Computer Co., Ltd.");
- IDS.put(2015, "David Electronics Company, Ltd.");
- IDS.put(2039, "Century Corporation");
- IDS.put(2058, "Evermuch Technology Co., Ltd.");
- IDS.put(2101, "Action Star Enterprise Co., Ltd.");
- IDS.put(2112, "Argosy Research Inc.");
- IDS.put(2122, "Wipro Limited");
- IDS.put(2159, "MEC IMEX INC/HPT");
- IDS.put(2205, "Icron Technologies Corporation");
- IDS.put(2247, "TAI TWUN ENTERPRISE CO., LTD.");
- IDS.put(2276, "Pioneer Corporation");
- IDS.put(2278, "Gemalto SA");
- IDS.put(2310, "FARADAY Technology Corp.");
- IDS.put(2313, "Audio-Technica Corp.");
- IDS.put(2316, "Silicon Motion, Inc. - Taiwan");
- IDS.put(2334, "Garmin International");
- IDS.put(2352, "Toshiba Corporation");
- IDS.put(2362, "Pixart Imaging, Inc.");
- IDS.put(2363, "Plextor LLC");
- IDS.put(2366, "J.S.T. Mfg. Co., Ltd.");
- IDS.put(2385, "Kingston Technology Company");
- IDS.put(2389, "NVIDIA");
- IDS.put(2395, "Medialogic Corporation");
- IDS.put(2397, "Polycom, Inc.");
- IDS.put(2468, "Contech Research, Inc.");
- IDS.put(2472, "Lin Shiung Enterprise Co., Ltd.");
- IDS.put(2475, "Japan Cash Machine Co., Ltd.");
- IDS.put(2498, "NISCA Corporation");
- IDS.put(2511, "Electronics Testing Center, Taiwan");
- IDS.put(2522, "A-FOUR TECH CO., LTD.");
- IDS.put(2555, "Altera");
- IDS.put(2578, "Cambridge Silicon Radio Ltd.");
- IDS.put(2583, "HOYA Corporation");
- IDS.put(2631, "Hirose Electric Co., Ltd.");
- IDS.put(2636, "COMPUTEX Co., Ltd.");
- IDS.put(2640, "Mimaki Engineering Co., Ltd.");
- IDS.put(2652, "Broadcom Corp.");
- IDS.put(2667, "Green House Co., Ltd.");
- IDS.put(2702, "Japan Aviation Electronics Industry Ltd. (JAE)");
- IDS.put(2727, "Wincor Nixdorf GmbH & Co KG");
- IDS.put(2733, "Rohde & Schwarz GmbH & Co. KG");
- IDS.put(2787, "Allion Labs, Inc.");
- IDS.put(2821, "ASUSTek Computer Inc.");
- IDS.put(2849, "Yokogawa Electric Corporation");
- IDS.put(2851, "Pan-Asia Electronics Co., Ltd.");
- IDS.put(2894, "Musical Electronics Ltd.");
- IDS.put(2907, "Anritsu Corporation");
- IDS.put(2922, "Maxim Integrated Products");
- IDS.put(2965, "ASIX Electronics Corporation");
- IDS.put(2967, "O2Micro, Inc.");
- IDS.put(3010, "Seagate Technology LLC");
- IDS.put(3034, "Realtek Semiconductor Corp.");
- IDS.put(3035, "Ericsson AB");
- IDS.put(3044, "Elka International Ltd.");
- IDS.put(3056, "Pace Micro Technology PLC");
- IDS.put(3108, "Taiyo Yuden Co., Ltd.");
- IDS.put(3129, "Aeroflex");
- IDS.put(3132, "Radius Co., Ltd.");
- IDS.put(3141, "Sonix Technology Co., Ltd.");
- IDS.put(3158, "Billion Bright (HK) Corporation Limited");
- IDS.put(3161, "Dong Guan Shinko Wire Co., Ltd.");
- IDS.put(3170, "Chant Sincere Co., Ltd");
- IDS.put(3190, "Solid State System Co., Ltd.");
- IDS.put(3209, "Honda Tsushin Kogyo Co., Ltd");
- IDS.put(3245, "Motorola Solutions");
- IDS.put(3255, "Singatron Enterprise Co. Ltd.");
- IDS.put(3268, "emsys Embedded Systems GmbH");
- IDS.put(32902, "Intel Corporation");
- IDS.put(3294, "Z-Com INC.");
- IDS.put(3313, "e-CONN ELECTRONIC CO., LTD.");
- IDS.put(3314, "ENE Technology Inc.");
- IDS.put(3351, "NALTEC, Inc.");
- IDS.put(3402, "NF Corporation");
- IDS.put(3403, "Grape Systems Inc.");
- IDS.put(3409, "Volex (Asia) Pte Ltd");
- IDS.put(3425, "MEILU ELECTRONICS (SHENZHEN) CO., LTD.");
- IDS.put(3441, "Hirakawa Hewtech Corp.");
- IDS.put(3452, "Taiwan Line Tek Electronic Co., Ltd.");
- IDS.put(3463, "Dolby Laboratories Inc.");
- IDS.put(3468, "C-MEDIA ELECTRONICS INC.");
- IDS.put(3472, "Sure-Fire Electrical Corporation");
- IDS.put(3495, "IOGEAR, Inc.");
- IDS.put(3504, "Micro-Star International Co., Ltd.");
- IDS.put(3537, "Contek Electronics Co., Ltd.");
- IDS.put(3540, "Custom Engineering SPA");
- IDS.put(3641, "Smart Modular Technologies, Inc.");
- IDS.put(3658, "Shenzhen Bao Hing Electric Wire & Cable Mfr. Co.");
- IDS.put(3673, "Bourns, Inc.");
- IDS.put(3690, "Megawin Technology Co., Ltd.");
- IDS.put(3698, "Hsi-Chin Electronics Co., Ltd.");
- IDS.put(3714, "Ching Tai Electric Wire & Cable Co., Ltd.");
- IDS.put(3724, "Well Force Electronic Co., Ltd");
- IDS.put(3725, "MediaTek Inc.");
- IDS.put(3728, "CRU");
- IDS.put(3744, "Ours Technology Inc.");
- IDS.put(3762, "Y-S ELECTRONIC CO., LTD.");
- IDS.put(3778, "Sweetray Industrial Ltd.");
- IDS.put(3779, "Axell Corporation");
- IDS.put(3782, "InnoVISION Multimedia Limited");
- IDS.put(3790, "TaiSol Electronics Co., Ltd.");
- IDS.put(3812, "Sunrich Technology (H.K.) Ltd.");
- IDS.put(3868, "Funai Electric Co., Ltd.");
- IDS.put(3873, "IOI Technology Corporation");
- IDS.put(3890, "YFC-BonEagle Electric Co., Ltd.");
- IDS.put(3896, "Nien-Yi Industrial Corp.");
- IDS.put(3916, "WORLDWIDE CABLE OPTO CORP.");
- IDS.put(3923, "Taiyo Cable (Dongguan) Co. Ltd.");
- IDS.put(3924, "Kawai Musical Instruments Mfg. Co., Ltd.");
- IDS.put(3936, "GuangZhou Chief Tech Electronic Technology Co. Ltd.");
- IDS.put(3944, "UQUEST, LTD.");
- IDS.put(3991, "CviLux Corporation");
- IDS.put(4003, "Chief Land Electronic Co., Ltd.");
- IDS.put(4046, "Sony Mobile Communications");
- IDS.put(4087, "CHI SHING COMPUTER ACCESSORIES CO., LTD.");
- IDS.put(4096, "Speed Tech Corp.");
- IDS.put(4100, "LG Electronics Inc.");
- IDS.put(4101, "Apacer Technology Inc.");
- IDS.put(4134, "Newly Corporation");
- IDS.put(4168, "Targus Group International");
- IDS.put(4172, "AMCO TEC International Inc.");
- IDS.put(4183, "ON Semiconductor");
- IDS.put(4184, "Western Digital Technologies, Inc.");
- IDS.put(4227, "CANON ELECTRONICS INC.");
- IDS.put(4235, "Grand-tek Technology Co., Ltd.");
- IDS.put(4236, "Robert Bosch GmbH");
- IDS.put(4238, "Lotes Co., Ltd.");
- IDS.put(4266, "Cables To Go");
- IDS.put(4267, "Universal Global Scientific Industrial Co., Ltd.");
- IDS.put(4292, "Silicon Laboratories, Inc.");
- IDS.put(4301, "Kycon Inc.");
- IDS.put(4362, "Moxa Inc.");
- IDS.put(4370, "Golden Bright (Sichuan) Electronic Technology Co Ltd");
- IDS.put(4382, "VSO ELECTRONICS CO., LTD.");
- IDS.put(4398, "Master Hill Electric Wire and Cable Co., Ltd.");
- IDS.put(4477, "Santa Electronic Inc.");
- IDS.put(4505, "Sierra Wireless Inc.");
- IDS.put(4522, "GlobalMedia Group, LLC");
- IDS.put(4528, "ATECH FLASH TECHNOLOGY");
- IDS.put(4643, "SKYCABLE ENTERPRISE CO., LTD.");
- IDS.put(4703, "ADATA Technology Co., Ltd.");
- IDS.put(4716, "Aristocrat Technologies");
- IDS.put(4717, "Bel Stewart");
- IDS.put(4742, "MARVELL SEMICONDUCTOR, INC.");
- IDS.put(4756, "RISO KAGAKU CORP.");
- IDS.put(4792, "Zhejiang Xinya Electronic Technology Co., Ltd.");
- IDS.put(4817, "Huawei Technologies Co., Ltd.");
- IDS.put(4823, "Better Holdings (HK) Limited");
- IDS.put(4907, "Konica Minolta, Inc.");
- IDS.put(4925, "Jasco Products Company");
- IDS.put(4989, "Pericom Semiconductor Corp.");
- IDS.put(5008, "TomTom International B.V.");
- IDS.put(5075, "AzureWave Technologies, Inc.");
- IDS.put(5117, "Initio Corporation");
- IDS.put(5118, "Phison Electronics Corp.");
- IDS.put(5134, "Telechips, Inc.");
- IDS.put(5145, "ABILITY ENTERPRISE CO., LTD.");
- IDS.put(5148, "Leviton Manufacturing");
- IDS.put(5271, "Panstrong Company Ltd.");
- IDS.put(5293, "CTK Corporation");
- IDS.put(5296, "StarTech.com Ltd.");
- IDS.put(5376, "Ellisys");
- IDS.put(5404, "VeriSilicon Holdings Co., Ltd.");
- IDS.put(5421, "JMicron Technology Corp.");
- IDS.put(5422, "HLDS (Hitachi-LG Data Storage, Inc.)");
- IDS.put(5440, "Phihong Technology Co., Ltd.");
- IDS.put(5451, "PNY Technologies Inc.");
- IDS.put(5453, "Rapid Conn, Connect County Holdings Bhd");
- IDS.put(5454, "D & M Holdings, Inc.");
- IDS.put(5480, "Sunf Pu Technology Co., Ltd");
- IDS.put(5488, "ALLTOP TECHNOLOGY CO., LTD.");
- IDS.put(5510, "Palconn Technology Co., Ltd.");
- IDS.put(5528, "Kunshan Guoji Electronics Co., Ltd.");
- IDS.put(5546, "DongGuan Ya Lian Electronics Co., Ltd.");
- IDS.put(5645, "Samtec");
- IDS.put(5694, "HongLin Electronics Co., Ltd.");
- IDS.put(5753, "Total Phase");
- IDS.put(5766, "ZOOM Corporation");
- IDS.put(5836, "silex technology, Inc.");
- IDS.put(5946, "F. Hoffmann-La Roche AG");
- IDS.put(5960, "MQP Electronics Ltd.");
- IDS.put(5964, "ASMedia Technology Inc.");
- IDS.put(5998, "UD electronic corp.");
- IDS.put(6001, "Shenzhen Alex Connector Co., Ltd.");
- IDS.put(6002, "System Level Solutions, Inc.");
- IDS.put(6018, "Spreadtrum Hong Kong Limited");
- IDS.put(6024, "ShenZhen Litkconn Technology Co., Ltd.");
- IDS.put(6053, "Advanced Connection Technology Inc.");
- IDS.put(6095, "Hip Hing Cable & Plug Mfy. Ltd.");
- IDS.put(6121, "DisplayLink (UK) Ltd.");
- IDS.put(6127, "Lenovo");
- IDS.put(6133, "K.K. Rocky");
- IDS.put(6160, "Wanshih Electronic Co., Ltd.");
- IDS.put(6185, "Dongguan YuQiu Electronics Co., Ltd.");
- IDS.put(6193, "Gwo Jinn Industries Co., Ltd.");
- IDS.put(6297, "Linkiss Co., Ltd.");
- IDS.put(6353, "Google Inc.");
- IDS.put(6394, "Kuang Ying Computer Equipment Co., Ltd.");
- IDS.put(6421, "Nordic Semiconductor ASA");
- IDS.put(6448, "Shenzhen Xianhe Technology Co., Ltd.");
- IDS.put(6449, "Ningbo Broad Telecommunication Co., Ltd.");
- IDS.put(6470, "Irisguard UK Ltd");
- IDS.put(6473, "Lab126");
- IDS.put(6481, "Hyperstone GmbH");
- IDS.put(6487, "BIOS Corporation");
- IDS.put(6626, "Solomon Systech Limited");
- IDS.put(6639, "Pak Heng Technology (Shenzhen) Co., Ltd.");
- IDS.put(6655, "Best Buy China Ltd.");
- IDS.put(6666, "USB-IF non-workshop");
- IDS.put(6709, "Artesyn Technologies Inc.");
- IDS.put(6720, "TERMINUS TECHNOLOGY INC.");
- IDS.put(6766, "Global Unichip Corp.");
- IDS.put(6786, "Proconn Technology Co., Ltd.");
- IDS.put(6794, "Simula Technology Inc.");
- IDS.put(6795, "SGS Taiwan Ltd.");
- IDS.put(6830, "Johnson Component & Equipments Co., Ltd.");
- IDS.put(6834, "Allied Vision Technologies GmbH");
- IDS.put(6859, "Salcomp Plc");
- IDS.put(6865, "Desan Wire Co., Ltd.");
- IDS.put(6944, "MStar Semiconductor, Inc.");
- IDS.put(6984, "Plastron Precision Co., Ltd.");
- IDS.put(7013, "The Hong Kong Standards and Testing Centre Ltd.");
- IDS.put(7048, "ShenMing Electron (Dong Guan) Co., Ltd.");
- IDS.put(7086, "Vuzix Corporation");
- IDS.put(7108, "Ford Motor Co.");
- IDS.put(7118, "Contac Cable Industrial Limited");
- IDS.put(7119, "Sunplus Innovation Technology Inc.");
- IDS.put(7120, "Hangzhou Riyue Electronics Co., Ltd.");
- IDS.put(7158, "Orient Semiconductor Electronics, Ltd.");
- IDS.put(7207, "SHENZHEN DNS INDUSTRIES CO., LTD.");
- IDS.put(7217, "LS Mtron Ltd.");
- IDS.put(7229, "NONIN MEDICAL INC.");
- IDS.put(7275, "Philips & Lite-ON Digital Solutions Corporation");
- IDS.put(7310, "ASTRON INTERNATIONAL CORP.");
- IDS.put(7320, "ALPINE ELECTRONICS, INC.");
- IDS.put(7347, "Aces Electronics Co., Ltd.");
- IDS.put(7348, "OPEX CORPORATION");
- IDS.put(7390, "Telecommunications Technology Association (TTA)");
- IDS.put(7434, "Visteon Corporation");
- IDS.put(7465, "Horng Tong Enterprise Co., Ltd.");
- IDS.put(7501, "Pegatron Corporation");
- IDS.put(7516, "Fresco Logic Inc.");
- IDS.put(7529, "Walta Electronic Co., Ltd.");
- IDS.put(7543, "Yueqing Changling Electronic Instrument Corp., Ltd.");
- IDS.put(7584, "Parade Technologies, Inc.");
- IDS.put(7647, "L&T Technology Services");
- IDS.put(7649, "Actions Microelectronics Co., Ltd.");
- IDS.put(7666, "China Telecommunication Technology Labs - Terminals");
- IDS.put(7668, "SHEN ZHEN FORMAN PRECISION INDUSTRY CO., LTD.");
- IDS.put(7682, "GLOBEMASTER TECHNOLOGIES CO., LTD.");
- IDS.put(7696, "Point Grey Research Inc.");
- IDS.put(7751, "HUNG TA H.T.ENTERPRISE CO., LTD.");
- IDS.put(7758, "Etron Technology, Inc.");
- IDS.put(7795, "COMLINK ELECTRONICS CO., LTD.");
- IDS.put(7818, "HIBEST Electronic (DongGuan) Co., Ltd.");
- IDS.put(7825, "Other World Computing");
- IDS.put(7863, "WIN WIN PRECISION INDUSTRIAL CO., LTD.");
- IDS.put(7879, "Gefen Inc.");
- IDS.put(7881, "MOSER BAER INDIA LIMITED");
- IDS.put(7898, "AIRTIES WIRELESS NETWORKS");
- IDS.put(7956, "Astoria Networks GmbH");
- IDS.put(7969, "Scosche Industries");
- IDS.put(7976, "Cal-Comp Electronics & Communications");
- IDS.put(7977, "Analogix Semiconductor, Inc.");
- IDS.put(7989, "Amphenol ShouhMin Industry (ShenZhen) Co., Ltd");
- IDS.put(7996, "Chang Yang Electronics Company Ltd.");
- IDS.put(8073, "Dongguan Goldconn Electronics Co., Ltd.");
- IDS.put(8074, "Morning Star Industrial Co., Ltd.");
- IDS.put(8117, "Unify Software and Solutions GmbH & Co. KG");
- IDS.put(8137, "NXP Semiconductors");
- IDS.put(8181, "Changzhou Wujin BEST Electronic Cables Co., Ltd.");
- IDS.put(8205, "Belkin Electronic (Changzhou) Co., Ltd.");
- IDS.put(8220, "Freeport Resources Enterprises Corp.");
- IDS.put(8222, "Qingdao Haier Telecom Co., Ltd.");
- IDS.put(8284, "Shenzhen Tronixin Electronics Co., Ltd.");
- IDS.put(8294, "Unicorn Electronics Components Co., Ltd.");
- IDS.put(8334, "Luxshare-ICT");
- IDS.put(8341, "CE LINK LIMITED");
- IDS.put(8342, "Microconn Electronic Co., Ltd.");
- IDS.put(8367, "Shenzhen CARVE Electronics Co., Ltd.");
- IDS.put(8382, "BURY GmbH & Co. KG");
- IDS.put(8384, "FENGHUA KINGSUN CO., LTD.");
- IDS.put(8386, "Sumitomo Electric Ind., Ltd., Optical Comm. R&D Lab");
- IDS.put(8439, "XIMEA s.r.o.");
- IDS.put(8457, "VIA Labs, Inc.");
- IDS.put(8492, "Shenzhen Linoya Electronic Co., Ltd.");
- IDS.put(8494, "Amphenol AssembleTech (Xiamen) Co., Ltd.");
- IDS.put(8524, "Y Soft Corporation");
- IDS.put(8550, "JVC KENWOOD Corporation");
- IDS.put(8564, "Transcend Information, Inc.");
- IDS.put(8566, "TMC/Allion Test Labs");
- IDS.put(8613, "Genesis Technology USA, Inc.");
- IDS.put(8627, "Dongguan Teconn Electronics Technology Co., Ltd.");
- IDS.put(8644, "Netcom Technology (HK) Limited");
- IDS.put(8659, "Compupack Technology Co., Ltd.");
- IDS.put(8667, "G-Max Technology Co., Ltd.");
- IDS.put(8679, "Sagemcom Broadband SAS");
- IDS.put(8695, "Wuerth-Elektronik eiSos GmbH & Co. KG");
- IDS.put(8707, "Shin Shin Co., Ltd.");
- IDS.put(8709, "3eYamaichi Electronics Co., Ltd.");
- IDS.put(8710, "Wiretek International Investment Ltd.");
- IDS.put(8711, "Fuzhou Rockchip Electronics Co., Ltd.");
- IDS.put(8752, "Plugable Technologies");
- IDS.put(8756, "T-CONN PRECISION CORPORATION");
- IDS.put(8831, "Granite River Labs");
- IDS.put(8842, "Hotron Precision Electronic Ind. Corp.");
- IDS.put(8875, "Trigence Semiconductor, Inc.");
- IDS.put(8888, "Motorola Mobility Inc.");
- IDS.put(8904, "Karming Electronic (Shenzhen) Co., Ltd.");
- IDS.put(8981, "Avery Design Systems, Inc.");
- IDS.put(8993, "iKingdom Corp. (d.b.a. iConnectivity)");
- IDS.put(9051, "KangXiang Electronic Co., Ltd.");
- IDS.put(9068, "ZheJiang Chunsheng Electronics Co., Ltd.");
- IDS.put(9130, "DOK (HK) Trading Limited");
- IDS.put(9132, "Marunix Electron Limited");
- IDS.put(9165, "Avconn Precise Connector Co., Ltd.");
- IDS.put(9184, "BitifEye Digital Test Solutions GmbH");
- IDS.put(9205, "Speed Conn Co., Ltd.");
- IDS.put(9222, "INSIDE Secure");
- IDS.put(9292, "Minebea Co., Ltd.");
- IDS.put(9299, "BAANTO");
- IDS.put(9338, "Suzhou Jutze Technologies Co., Ltd");
- IDS.put(9355, "DONGGUAN SYNCONN PRECISION INDUSTRY CO. LTD.");
- IDS.put(9382, "Shenzhen Pangngai Industrial Co., Ltd.");
- IDS.put(9422, "Shenzhen Deren Electronic Co., Ltd.");
- IDS.put(9424, "Smith Micro Software, Inc.");
- IDS.put(9453, "ZEN FACTORY GROUP (ASIA) LTD.");
- IDS.put(9481, "Chain-In Electronic Co., Ltd.");
- IDS.put(9514, "SUZHOU KELI TECHNOLOGY DEVELOPMENT CO., LTD.");
- IDS.put(9515, "TOP Exactitude Industry (ShenZhen) Co., Ltd.");
- IDS.put(9525, "ShenZhen Hogend Precision Technology Co., Ltd.");
- IDS.put(9527, "Norel Systems Ltd.");
- IDS.put(9556, "ASSA ABLOY AB");
- IDS.put(9575, "DongGuan LongTao Electronic Co., Ltd.");
- IDS.put(9577, "DongGuan City MingJi Electronics Co., Ltd.");
- IDS.put(9589, "Weida Hi-Tech Co., Ltd.");
- IDS.put(9593, "Dongguan Wisechamp Electronic Co., Ltd.");
- IDS.put(9613, "Sequans Communications");
- IDS.put(9636, "ALGOLTEK, INC.");
- IDS.put(9651, "DongGuan Elinke Industrial Co., Ltd.");
- IDS.put(9679, "Corning Optical Communications LLC");
- IDS.put(9714, "Dongguan Jinyue Electronics Co., Ltd.");
- IDS.put(9723, "RICOH IMAGING COMPANY, LTD.");
- IDS.put(9742, "DongGuan HYX Industrial Co., Ltd.");
- IDS.put(9753, "Advanced Silicon SA");
- IDS.put(9756, "EISST Limited");
- IDS.put(9771, "YTOP Electronics Technical (Kunshan) Co., Ltd.");
- IDS.put(9841, "Innovative Logic");
- IDS.put(9842, "GoPro");
- IDS.put(9846, "Basler AG");
- IDS.put(9851, "Palpilot International Corp.");
- IDS.put(9896, "UNIREX CORPORATION");
- IDS.put(9917, "Integral Memory Plc.");
- IDS.put(9973, "Morning Star Digital Connector Co., Ltd.");
- IDS.put(9984, "MITACHI CO., LTD.");
- IDS.put(9999, "HGST, a Western Digital Company");
- }
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/UVCCamera.java b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/UVCCamera.java
deleted file mode 100755
index 20dda8572d..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/java/com/serenegiant/usb/UVCCamera.java
+++ /dev/null
@@ -1,1233 +0,0 @@
-/*
- * 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.util.ArrayList;
-import java.util.List;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.graphics.SurfaceTexture;
-import android.hardware.usb.UsbDevice;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-
-import com.serenegiant.usb.USBMonitor.UsbControlBlock;
-
-public class UVCCamera {
- private static final boolean DEBUG = false; // TODO set false when releasing
- private static final String TAG = UVCCamera.class.getSimpleName();
- private static final String DEFAULT_USBFS = "/dev/bus/usb";
-
- public static final int DEFAULT_PREVIEW_WIDTH = 640;
- public static final int DEFAULT_PREVIEW_HEIGHT = 480;
- public static final int DEFAULT_PREVIEW_MODE = 0;
- public static final int DEFAULT_PREVIEW_MIN_FPS = 1;
- public static final int DEFAULT_PREVIEW_MAX_FPS = 30;
- public static final float DEFAULT_BANDWIDTH = 1.0f;
-
- public static final int FRAME_FORMAT_YUYV = 0;
- public static final int FRAME_FORMAT_MJPEG = 1;
-
- public static final int PIXEL_FORMAT_RAW = 0;
- public static final int PIXEL_FORMAT_YUV = 1;
- public static final int PIXEL_FORMAT_RGB565 = 2;
- public static final int PIXEL_FORMAT_RGBX = 3;
- public static final int PIXEL_FORMAT_YUV420SP = 4;
- public static final int PIXEL_FORMAT_NV21 = 5; // = YVU420SemiPlanar
-
- //--------------------------------------------------------------------------------
- public static final int CTRL_SCANNING = 0x00000001; // D0: Scanning Mode
- public static final int CTRL_AE = 0x00000002; // D1: Auto-Exposure Mode
- public static final int CTRL_AE_PRIORITY = 0x00000004; // D2: Auto-Exposure Priority
- public static final int CTRL_AE_ABS = 0x00000008; // D3: Exposure Time (Absolute)
- public static final int CTRL_AR_REL = 0x00000010; // D4: Exposure Time (Relative)
- public static final int CTRL_FOCUS_ABS = 0x00000020; // D5: Focus (Absolute)
- public static final int CTRL_FOCUS_REL = 0x00000040; // D6: Focus (Relative)
- public static final int CTRL_IRIS_ABS = 0x00000080; // D7: Iris (Absolute)
- public static final int CTRL_IRIS_REL = 0x00000100; // D8: Iris (Relative)
- public static final int CTRL_ZOOM_ABS = 0x00000200; // D9: Zoom (Absolute)
- public static final int CTRL_ZOOM_REL = 0x00000400; // D10: Zoom (Relative)
- public static final int CTRL_PANTILT_ABS = 0x00000800; // D11: PanTilt (Absolute)
- public static final int CTRL_PANTILT_REL = 0x00001000; // D12: PanTilt (Relative)
- public static final int CTRL_ROLL_ABS = 0x00002000; // D13: Roll (Absolute)
- public static final int CTRL_ROLL_REL = 0x00004000; // D14: Roll (Relative)
- public static final int CTRL_FOCUS_AUTO = 0x00020000; // D17: Focus, Auto
- public static final int CTRL_PRIVACY = 0x00040000; // D18: Privacy
- public static final int CTRL_FOCUS_SIMPLE = 0x00080000; // D19: Focus, Simple
- public static final int CTRL_WINDOW = 0x00100000; // D20: Window
-
- public static final int PU_BRIGHTNESS = 0x80000001; // D0: Brightness
- public static final int PU_CONTRAST = 0x80000002; // D1: Contrast
- public static final int PU_HUE = 0x80000004; // D2: Hue
- public static final int PU_SATURATION = 0x80000008; // D3: Saturation
- public static final int PU_SHARPNESS = 0x80000010; // D4: Sharpness
- public static final int PU_GAMMA = 0x80000020; // D5: Gamma
- public static final int PU_WB_TEMP = 0x80000040; // D6: White Balance Temperature
- public static final int PU_WB_COMPO = 0x80000080; // D7: White Balance Component
- public static final int PU_BACKLIGHT = 0x80000100; // D8: Backlight Compensation
- public static final int PU_GAIN = 0x80000200; // D9: Gain
- public static final int PU_POWER_LF = 0x80000400; // D10: Power Line Frequency
- public static final int PU_HUE_AUTO = 0x80000800; // D11: Hue, Auto
- public static final int PU_WB_TEMP_AUTO = 0x80001000; // D12: White Balance Temperature, Auto
- public static final int PU_WB_COMPO_AUTO = 0x80002000; // D13: White Balance Component, Auto
- public static final int PU_DIGITAL_MULT = 0x80004000; // D14: Digital Multiplier
- public static final int PU_DIGITAL_LIMIT = 0x80008000; // D15: Digital Multiplier Limit
- public static final int PU_AVIDEO_STD = 0x80010000; // D16: Analog Video Standard
- public static final int PU_AVIDEO_LOCK = 0x80020000; // D17: Analog Video Lock Status
- public static final int PU_CONTRAST_AUTO = 0x80040000; // D18: Contrast, Auto
-
- // uvc_status_class from libuvc.h
- public static final int STATUS_CLASS_CONTROL = 0x10;
- public static final int STATUS_CLASS_CONTROL_CAMERA = 0x11;
- public static final int STATUS_CLASS_CONTROL_PROCESSING = 0x12;
-
- // uvc_status_attribute from libuvc.h
- public static final int STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00;
- public static final int STATUS_ATTRIBUTE_INFO_CHANGE = 0x01;
- public static final int STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02;
- public static final int STATUS_ATTRIBUTE_UNKNOWN = 0xff;
-
- private static boolean isLoaded;
- static {
- if (!isLoaded) {
- System.loadLibrary("jpeg-turbo1500");
- System.loadLibrary("usb100");
- System.loadLibrary("uvc");
- System.loadLibrary("UVCCamera");
- isLoaded = true;
- }
- }
-
- private UsbControlBlock mCtrlBlock;
- protected long mControlSupports; // カメラコントロールでサポートしている機能フラグ
- protected long mProcSupports; // プロセッシングユニットでサポートしている機能フラグ
- protected int mCurrentPreviewMode = -1; // MODIFIED
- protected int mCurrentPreviewWidth = DEFAULT_PREVIEW_WIDTH, mCurrentPreviewHeight = DEFAULT_PREVIEW_HEIGHT;
-
- protected int mCurrentFrameFormat = FRAME_FORMAT_MJPEG;
- protected int mCurrentWidth = DEFAULT_PREVIEW_WIDTH, mCurrentHeight = DEFAULT_PREVIEW_HEIGHT;
- protected float mCurrentBandwidthFactor = DEFAULT_BANDWIDTH;
- protected String mSupportedSize;
- protected List mCurrentSizeList;
- // these fields from here are accessed from native code and do not change name and remove
- protected long mNativePtr;
- protected int mScanningModeMin, mScanningModeMax, mScanningModeDef;
- protected int mExposureModeMin, mExposureModeMax, mExposureModeDef;
- protected int mExposurePriorityMin, mExposurePriorityMax, mExposurePriorityDef;
- protected int mExposureMin, mExposureMax, mExposureDef;
- protected int mAutoFocusMin, mAutoFocusMax, mAutoFocusDef;
- protected int mFocusMin, mFocusMax, mFocusDef;
- protected int mFocusRelMin, mFocusRelMax, mFocusRelDef;
- protected int mFocusSimpleMin, mFocusSimpleMax, mFocusSimpleDef;
- protected int mIrisMin, mIrisMax, mIrisDef;
- protected int mIrisRelMin, mIrisRelMax, mIrisRelDef;
- protected int mPanMin, mPanMax, mPanDef;
- protected int mTiltMin, mTiltMax, mTiltDef;
- protected int mRollMin, mRollMax, mRollDef;
- protected int mPanRelMin, mPanRelMax, mPanRelDef;
- protected int mTiltRelMin, mTiltRelMax, mTiltRelDef;
- protected int mRollRelMin, mRollRelMax, mRollRelDef;
- protected int mPrivacyMin, mPrivacyMax, mPrivacyDef;
- protected int mAutoWhiteBlanceMin, mAutoWhiteBlanceMax, mAutoWhiteBlanceDef;
- protected int mAutoWhiteBlanceCompoMin, mAutoWhiteBlanceCompoMax, mAutoWhiteBlanceCompoDef;
- protected int mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef;
- protected int mWhiteBlanceCompoMin, mWhiteBlanceCompoMax, mWhiteBlanceCompoDef;
- protected int mWhiteBlanceRelMin, mWhiteBlanceRelMax, mWhiteBlanceRelDef;
- protected int mBacklightCompMin, mBacklightCompMax, mBacklightCompDef;
- protected int mBrightnessMin, mBrightnessMax, mBrightnessDef;
- protected int mContrastMin, mContrastMax, mContrastDef;
- protected int mSharpnessMin, mSharpnessMax, mSharpnessDef;
- protected int mGainMin, mGainMax, mGainDef;
- protected int mGammaMin, mGammaMax, mGammaDef;
- protected int mSaturationMin, mSaturationMax, mSaturationDef;
- protected int mHueMin, mHueMax, mHueDef;
- protected int mZoomMin, mZoomMax, mZoomDef;
- protected int mZoomRelMin, mZoomRelMax, mZoomRelDef;
- protected int mPowerlineFrequencyMin, mPowerlineFrequencyMax, mPowerlineFrequencyDef;
- protected int mMultiplierMin, mMultiplierMax, mMultiplierDef;
- protected int mMultiplierLimitMin, mMultiplierLimitMax, mMultiplierLimitDef;
- protected int mAnalogVideoStandardMin, mAnalogVideoStandardMax, mAnalogVideoStandardDef;
- protected int mAnalogVideoLockStateMin, mAnalogVideoLockStateMax, mAnalogVideoLockStateDef;
- // until here
- /**
- * the sonctructor of this class should be call within the thread that has a looper
- * (UI thread or a thread that called Looper.prepare)
- */
- public UVCCamera() {
- mNativePtr = nativeCreate();
- mSupportedSize = null;
- }
-
- /**
- * connect to a UVC camera
- * USB permission is necessary before this method is called
- * @param ctrlBlock
- */
- public synchronized void open(final UsbControlBlock ctrlBlock) {
- int result;
- try {
- mCtrlBlock = ctrlBlock.clone();
- result = nativeConnect(mNativePtr,
- mCtrlBlock.getVenderId(), mCtrlBlock.getProductId(),
- mCtrlBlock.getFileDescriptor(),
- mCtrlBlock.getBusNum(),
- mCtrlBlock.getDevNum(),
- getUSBFSName(mCtrlBlock));
- } catch (final Exception e) {
- Log.w(TAG, e);
- result = -1;
- }
- if (result != 0) {
- throw new UnsupportedOperationException("open failed:result=" + result);
- }
- if (mNativePtr != 0 && TextUtils.isEmpty(mSupportedSize)) {
- mSupportedSize = nativeGetSupportedSize(mNativePtr);
- }
- nativeSetPreviewSize(mNativePtr, DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT,
- DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, DEFAULT_PREVIEW_MODE, DEFAULT_BANDWIDTH);
- }
-
- /**
- * set status callback
- * @param callback
- */
- public void setStatusCallback(final IStatusCallback callback) {
- if (mNativePtr != 0) {
- nativeSetStatusCallback(mNativePtr, callback);
- }
- }
-
- /**
- * set button callback
- * @param callback
- */
- public void setButtonCallback(final IButtonCallback callback) {
- if (mNativePtr != 0) {
- nativeSetButtonCallback(mNativePtr, callback);
- }
- }
-
- /**
- * close and release UVC camera
- */
- public synchronized void close() {
- stopPreview();
- if (mNativePtr != 0) {
- nativeRelease(mNativePtr);
-// mNativePtr = 0; // nativeDestroyを呼ぶのでここでクリアしちゃダメ
- }
- if (mCtrlBlock != null) {
- mCtrlBlock.close();
- mCtrlBlock = null;
- }
- mControlSupports = mProcSupports = 0;
- mCurrentFrameFormat = -1;
- mCurrentBandwidthFactor = 0;
- mSupportedSize = null;
- mCurrentSizeList = null;
- if (DEBUG) Log.v(TAG, "close:finished");
- }
-
- public UsbDevice getDevice() {
- return mCtrlBlock != null ? mCtrlBlock.getDevice() : null;
- }
-
- public String getDeviceName(){
- return mCtrlBlock != null ? mCtrlBlock.getDeviceName() : null;
- }
-
- public UsbControlBlock getUsbControlBlock() {
- return mCtrlBlock;
- }
-
- public synchronized String getSupportedSize() {
- return !TextUtils.isEmpty(mSupportedSize) ? mSupportedSize : (mSupportedSize = nativeGetSupportedSize(mNativePtr));
- }
-
- public Size getPreviewSize() {
- Size result = null;
- final List list = getSupportedSizeList();
- for (final Size sz: list) {
- if ((sz.width == mCurrentWidth)
- || (sz.height == mCurrentHeight)) {
- result =sz;
- break;
- }
- }
- return result;
- }
-
- /**
- * Set preview size and preview mode
- * @param width
- @param height
- */
- public void setPreviewSize(final int width, final int height) {
- setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, mCurrentFrameFormat, mCurrentBandwidthFactor);
- }
-
- /**
- * Set preview size and preview mode
- * @param width
- * @param height
- * @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
- */
- public void setPreviewSize(final int width, final int height, final int frameFormat) {
- setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, frameFormat, mCurrentBandwidthFactor);
- }
-
- /**
- * Set preview size and preview mode
- * @param width
- @param height
- @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
- @param bandwidth [0.0f,1.0f]
- */
- public void setPreviewSize(final int width, final int height, final int frameFormat, final float bandwidth) {
- setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, frameFormat, bandwidth);
- }
-
- /**
- * Set preview size and preview mode
- * @param width
- * @param height
- * @param min_fps
- * @param max_fps
- * @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
- * @param bandwidthFactor
- */
- public void setPreviewSize(final int width, final int height, final int min_fps, final int max_fps, final int frameFormat, final float bandwidthFactor) {
- if ((width == 0) || (height == 0))
- throw new IllegalArgumentException("invalid preview size");
- if (mNativePtr != 0) {
- final int result = nativeSetPreviewSize(mNativePtr, width, height, min_fps, max_fps, frameFormat, bandwidthFactor);
- if (result != 0)
- throw new IllegalArgumentException("Failed to set preview size");
- mCurrentFrameFormat = frameFormat;
- mCurrentWidth = width;
- mCurrentHeight = height;
- mCurrentBandwidthFactor = bandwidthFactor;
- }
- }
-
- public List getSupportedSizeList() {
- final int type = (mCurrentFrameFormat > 0) ? 6 : 4;
- return getSupportedSize(type, mSupportedSize);
- }
-
- public static List getSupportedSize(final int type, final String supportedSize) {
- final List result = new ArrayList();
- if (!TextUtils.isEmpty(supportedSize))
- try {
- final JSONObject json = new JSONObject(supportedSize);
- final JSONArray formats = json.getJSONArray("formats");
- final int format_nums = formats.length();
- for (int i = 0; i < format_nums; i++) {
- final JSONObject format = formats.getJSONObject(i);
- final int format_type = format.getInt("type");
- if ((format_type == type) || (type == -1)) {
- addSize(format, format_type, 0, result);
- }
- }
- } catch (final JSONException e) {
- }
- return result;
- }
-
- private static final void addSize(final JSONObject format, final int formatType, final int frameType, final List size_list) throws JSONException {
- final JSONArray size = format.getJSONArray("size");
- final int size_nums = size.length();
- for (int j = 0; j < size_nums; j++) {
- final String[] sz = size.getString(j).split("x");
- try {
- size_list.add(new Size(formatType, frameType, j, Integer.parseInt(sz[0]), Integer.parseInt(sz[1])));
- } catch (final Exception e) {
- break;
- }
- }
- }
-
- /**
- * set preview surface with SurfaceHolder
- * you can use SurfaceHolder came from SurfaceView/GLSurfaceView
- * @param holder
- */
- public synchronized void setPreviewDisplay(final SurfaceHolder holder) {
- nativeSetPreviewDisplay(mNativePtr, holder.getSurface());
- }
-
- /**
- * set preview surface with SurfaceTexture.
- * this method require API >= 14
- * @param texture
- */
- public synchronized void setPreviewTexture(final SurfaceTexture texture) { // API >= 11
- final Surface surface = new Surface(texture); // XXX API >= 14
- nativeSetPreviewDisplay(mNativePtr, surface);
- }
- // MODIFIED
- public void setPreviewFrameCallback(final IPreviewFrameCallback callback, final int pixelFormat) {
- if (mNativePtr != 0) {
- nativeSetPreviewFrameCallback(mNativePtr, callback, pixelFormat);
- }
- }
-
-
- /**
- * set preview surface with Surface
- * @param surface
- */
- public synchronized void setPreviewDisplay(final Surface surface) {
- nativeSetPreviewDisplay(mNativePtr, surface);
- }
-
- /**
- * set frame callback
- * @param callback
- * @param pixelFormat
- */
- public void setFrameCallback(final IFrameCallback callback, final int pixelFormat) {
- if (mNativePtr != 0) {
- nativeSetFrameCallback(mNativePtr, callback, pixelFormat);
- }
- }
-
- /**
- * start preview
- */
- public synchronized void startPreview() {
- if (mCtrlBlock != null) {
- nativeStartPreview(mNativePtr);
- }
- }
-
- /**
- * stop preview
- */
- public synchronized void stopPreview() {
- setFrameCallback(null, 0);
- if (mCtrlBlock != null) {
- nativeStopPreview(mNativePtr);
- }
- }
-
- /**
- * destroy UVCCamera object
- */
- public synchronized void destroy() {
- close();
- if (mNativePtr != 0) {
- nativeDestroy(mNativePtr);
- mNativePtr = 0;
- }
- }
-
- // wrong result may return when you call this just after camera open.
- // it is better to wait several hundreads millseconds.
- public boolean checkSupportFlag(final long flag) {
- updateCameraParams();
- if ((flag & 0x80000000) == 0x80000000)
- return ((mProcSupports & flag) == (flag & 0x7ffffffF));
- else
- return (mControlSupports & flag) == flag;
- }
-
-//================================================================================
- public synchronized void setAutoFocus(final boolean autoFocus) {
- if (mNativePtr != 0) {
- nativeSetAutoFocus(mNativePtr, autoFocus);
- }
- }
-
- public synchronized boolean getAutoFocus() {
- boolean result = true;
- if (mNativePtr != 0) {
- result = nativeGetAutoFocus(mNativePtr) > 0;
- }
- return result;
- }
-//================================================================================
- /**
- * @param focus [%]
- */
- public synchronized void setFocus(final int focus) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mFocusMax - mFocusMin);
- if (range > 0)
- nativeSetFocus(mNativePtr, (int)(focus / 100.f * range) + mFocusMin);
- }
- }
-
- /**
- * @param focus_abs
- * @return focus[%]
- */
- public synchronized int getFocus(final int focus_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateFocusLimit(mNativePtr);
- final float range = Math.abs(mFocusMax - mFocusMin);
- if (range > 0) {
- result = (int)((focus_abs - mFocusMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return focus[%]
- */
- public synchronized int getFocus() {
- return getFocus(nativeGetFocus(mNativePtr));
- }
-
- public synchronized void resetFocus() {
- if (mNativePtr != 0) {
- nativeSetFocus(mNativePtr, mFocusDef);
- }
- }
-
-//================================================================================
- public synchronized void setAutoWhiteBlance(final boolean autoWhiteBlance) {
- if (mNativePtr != 0) {
- nativeSetAutoWhiteBlance(mNativePtr, autoWhiteBlance);
- }
- }
-
- public synchronized boolean getAutoWhiteBlance() {
- boolean result = true;
- if (mNativePtr != 0) {
- result = nativeGetAutoWhiteBlance(mNativePtr) > 0;
- }
- return result;
- }
-
-//================================================================================
- /**
- * @param whiteBlance [%]
- */
- public synchronized void setWhiteBlance(final int whiteBlance) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mWhiteBlanceMax - mWhiteBlanceMin);
- if (range > 0)
- nativeSetWhiteBlance(mNativePtr, (int)(whiteBlance / 100.f * range) + mWhiteBlanceMin);
- }
- }
-
- /**
- * @param whiteBlance_abs
- * @return whiteBlance[%]
- */
- public synchronized int getWhiteBlance(final int whiteBlance_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateWhiteBlanceLimit(mNativePtr);
- final float range = Math.abs(mWhiteBlanceMax - mWhiteBlanceMin);
- if (range > 0) {
- result = (int)((whiteBlance_abs - mWhiteBlanceMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return white blance[%]
- */
- public synchronized int getWhiteBlance() {
- return getFocus(nativeGetWhiteBlance(mNativePtr));
- }
-
- public synchronized void resetWhiteBlance() {
- if (mNativePtr != 0) {
- nativeSetWhiteBlance(mNativePtr, mWhiteBlanceDef);
- }
- }
-//================================================================================
- /**
- * @param brightness [%]
- */
- public synchronized void setBrightness(final int brightness) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mBrightnessMax - mBrightnessMin);
- if (range > 0)
- nativeSetBrightness(mNativePtr, (int)(brightness / 100.f * range) + mBrightnessMin);
- }
- }
-
- /**
- * @param brightness_abs
- * @return brightness[%]
- */
- public synchronized int getBrightness(final int brightness_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateBrightnessLimit(mNativePtr);
- final float range = Math.abs(mBrightnessMax - mBrightnessMin);
- if (range > 0) {
- result = (int)((brightness_abs - mBrightnessMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return brightness[%]
- */
- public synchronized int getBrightness() {
- return getBrightness(nativeGetBrightness(mNativePtr));
- }
-
- public synchronized void resetBrightness() {
- if (mNativePtr != 0) {
- nativeSetBrightness(mNativePtr, mBrightnessDef);
- }
- }
-
-//================================================================================
- /**
- * @param contrast [%]
- */
- public synchronized void setContrast(final int contrast) {
- if (mNativePtr != 0) {
- nativeUpdateContrastLimit(mNativePtr);
- final float range = Math.abs(mContrastMax - mContrastMin);
- if (range > 0)
- nativeSetContrast(mNativePtr, (int)(contrast / 100.f * range) + mContrastMin);
- }
- }
-
- /**
- * @param contrast_abs
- * @return contrast[%]
- */
- public synchronized int getContrast(final int contrast_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- final float range = Math.abs(mContrastMax - mContrastMin);
- if (range > 0) {
- result = (int)((contrast_abs - mContrastMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return contrast[%]
- */
- public synchronized int getContrast() {
- return getContrast(nativeGetContrast(mNativePtr));
- }
-
- public synchronized void resetContrast() {
- if (mNativePtr != 0) {
- nativeSetContrast(mNativePtr, mContrastDef);
- }
- }
-
-//================================================================================
- /**
- * @param sharpness [%]
- */
- public synchronized void setSharpness(final int sharpness) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mSharpnessMax - mSharpnessMin);
- if (range > 0)
- nativeSetSharpness(mNativePtr, (int)(sharpness / 100.f * range) + mSharpnessMin);
- }
- }
-
- /**
- * @param sharpness_abs
- * @return sharpness[%]
- */
- public synchronized int getSharpness(final int sharpness_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateSharpnessLimit(mNativePtr);
- final float range = Math.abs(mSharpnessMax - mSharpnessMin);
- if (range > 0) {
- result = (int)((sharpness_abs - mSharpnessMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return sharpness[%]
- */
- public synchronized int getSharpness() {
- return getSharpness(nativeGetSharpness(mNativePtr));
- }
-
- public synchronized void resetSharpness() {
- if (mNativePtr != 0) {
- nativeSetSharpness(mNativePtr, mSharpnessDef);
- }
- }
-//================================================================================
- /**
- * @param gain [%]
- */
- public synchronized void setGain(final int gain) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mGainMax - mGainMin);
- if (range > 0)
- nativeSetGain(mNativePtr, (int)(gain / 100.f * range) + mGainMin);
- }
- }
-
- /**
- * @param gain_abs
- * @return gain[%]
- */
- public synchronized int getGain(final int gain_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateGainLimit(mNativePtr);
- final float range = Math.abs(mGainMax - mGainMin);
- if (range > 0) {
- result = (int)((gain_abs - mGainMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return gain[%]
- */
- public synchronized int getGain() {
- return getGain(nativeGetGain(mNativePtr));
- }
-
- public synchronized void resetGain() {
- if (mNativePtr != 0) {
- nativeSetGain(mNativePtr, mGainDef);
- }
- }
-
-//================================================================================
- /**
- * @param gamma [%]
- */
- public synchronized void setGamma(final int gamma) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mGammaMax - mGammaMin);
- if (range > 0)
- nativeSetGamma(mNativePtr, (int)(gamma / 100.f * range) + mGammaMin);
- }
- }
-
- /**
- * @param gamma_abs
- * @return gamma[%]
- */
- public synchronized int getGamma(final int gamma_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateGammaLimit(mNativePtr);
- final float range = Math.abs(mGammaMax - mGammaMin);
- if (range > 0) {
- result = (int)((gamma_abs - mGammaMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return gamma[%]
- */
- public synchronized int getGamma() {
- return getGamma(nativeGetGamma(mNativePtr));
- }
-
- public synchronized void resetGamma() {
- if (mNativePtr != 0) {
- nativeSetGamma(mNativePtr, mGammaDef);
- }
- }
-
-//================================================================================
- /**
- * @param saturation [%]
- */
- public synchronized void setSaturation(final int saturation) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mSaturationMax - mSaturationMin);
- if (range > 0)
- nativeSetSaturation(mNativePtr, (int)(saturation / 100.f * range) + mSaturationMin);
- }
- }
-
- /**
- * @param saturation_abs
- * @return saturation[%]
- */
- public synchronized int getSaturation(final int saturation_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateSaturationLimit(mNativePtr);
- final float range = Math.abs(mSaturationMax - mSaturationMin);
- if (range > 0) {
- result = (int)((saturation_abs - mSaturationMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return saturation[%]
- */
- public synchronized int getSaturation() {
- return getSaturation(nativeGetSaturation(mNativePtr));
- }
-
- public synchronized void resetSaturation() {
- if (mNativePtr != 0) {
- nativeSetSaturation(mNativePtr, mSaturationDef);
- }
- }
-//================================================================================
- /**
- * @param hue [%]
- */
- public synchronized void setHue(final int hue) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mHueMax - mHueMin);
- if (range > 0)
- nativeSetHue(mNativePtr, (int)(hue / 100.f * range) + mHueMin);
- }
- }
-
- /**
- * @param hue_abs
- * @return hue[%]
- */
- public synchronized int getHue(final int hue_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateHueLimit(mNativePtr);
- final float range = Math.abs(mHueMax - mHueMin);
- if (range > 0) {
- result = (int)((hue_abs - mHueMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return hue[%]
- */
- public synchronized int getHue() {
- return getHue(nativeGetHue(mNativePtr));
- }
-
- public synchronized void resetHue() {
- if (mNativePtr != 0) {
- nativeSetHue(mNativePtr, mSaturationDef);
- }
- }
-
-//================================================================================
- public void setPowerlineFrequency(final int frequency) {
- if (mNativePtr != 0)
- nativeSetPowerlineFrequency(mNativePtr, frequency);
- }
-
- public int getPowerlineFrequency() {
- return nativeGetPowerlineFrequency(mNativePtr);
- }
-
-//================================================================================
- /**
- * this may not work well with some combination of camera and device
- * @param zoom [%]
- */
- public synchronized void setZoom(final int zoom) {
- if (mNativePtr != 0) {
- final float range = Math.abs(mZoomMax - mZoomMin);
- if (range > 0) {
- final int z = (int)(zoom / 100.f * range) + mZoomMin;
-// Log.d(TAG, "setZoom:zoom=" + zoom + " ,value=" + z);
- nativeSetZoom(mNativePtr, z);
- }
- }
- }
-
- /**
- * @param zoom_abs
- * @return zoom[%]
- */
- public synchronized int getZoom(final int zoom_abs) {
- int result = 0;
- if (mNativePtr != 0) {
- nativeUpdateZoomLimit(mNativePtr);
- final float range = Math.abs(mZoomMax - mZoomMin);
- if (range > 0) {
- result = (int)((zoom_abs - mZoomMin) * 100.f / range);
- }
- }
- return result;
- }
-
- /**
- * @return zoom[%]
- */
- public synchronized int getZoom() {
- return getZoom(nativeGetZoom(mNativePtr));
- }
-
- public synchronized void resetZoom() {
- if (mNativePtr != 0) {
- nativeSetZoom(mNativePtr, mZoomDef);
- }
- }
-
-//================================================================================
- public synchronized void updateCameraParams() {
- if (mNativePtr != 0) {
- if ((mControlSupports == 0) || (mProcSupports == 0)) {
- // サポートしている機能フラグを取得
- if (mControlSupports == 0)
- mControlSupports = nativeGetCtrlSupports(mNativePtr);
- if (mProcSupports == 0)
- mProcSupports = nativeGetProcSupports(mNativePtr);
- // 設定値を取得
- if ((mControlSupports != 0) && (mProcSupports != 0)) {
- nativeUpdateBrightnessLimit(mNativePtr);
- nativeUpdateContrastLimit(mNativePtr);
- nativeUpdateSharpnessLimit(mNativePtr);
- nativeUpdateGainLimit(mNativePtr);
- nativeUpdateGammaLimit(mNativePtr);
- nativeUpdateSaturationLimit(mNativePtr);
- nativeUpdateHueLimit(mNativePtr);
- nativeUpdateZoomLimit(mNativePtr);
- nativeUpdateWhiteBlanceLimit(mNativePtr);
- nativeUpdateFocusLimit(mNativePtr);
- }
- if (DEBUG) {
- dumpControls(mControlSupports);
- dumpProc(mProcSupports);
- Log.v(TAG, String.format("Brightness:min=%d,max=%d,def=%d", mBrightnessMin, mBrightnessMax, mBrightnessDef));
- Log.v(TAG, String.format("Contrast:min=%d,max=%d,def=%d", mContrastMin, mContrastMax, mContrastDef));
- Log.v(TAG, String.format("Sharpness:min=%d,max=%d,def=%d", mSharpnessMin, mSharpnessMax, mSharpnessDef));
- Log.v(TAG, String.format("Gain:min=%d,max=%d,def=%d", mGainMin, mGainMax, mGainDef));
- Log.v(TAG, String.format("Gamma:min=%d,max=%d,def=%d", mGammaMin, mGammaMax, mGammaDef));
- Log.v(TAG, String.format("Saturation:min=%d,max=%d,def=%d", mSaturationMin, mSaturationMax, mSaturationDef));
- Log.v(TAG, String.format("Hue:min=%d,max=%d,def=%d", mHueMin, mHueMax, mHueDef));
- Log.v(TAG, String.format("Zoom:min=%d,max=%d,def=%d", mZoomMin, mZoomMax, mZoomDef));
- Log.v(TAG, String.format("WhiteBlance:min=%d,max=%d,def=%d", mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef));
- Log.v(TAG, String.format("Focus:min=%d,max=%d,def=%d", mFocusMin, mFocusMax, mFocusDef));
- }
- }
- } else {
- mControlSupports = mProcSupports = 0;
- }
- }
-
- private static final String[] SUPPORTS_CTRL = {
- "D0: Scanning Mode",
- "D1: Auto-Exposure Mode",
- "D2: Auto-Exposure Priority",
- "D3: Exposure Time (Absolute)",
- "D4: Exposure Time (Relative)",
- "D5: Focus (Absolute)",
- "D6: Focus (Relative)",
- "D7: Iris (Absolute)",
- "D8: Iris (Relative)",
- "D9: Zoom (Absolute)",
- "D10: Zoom (Relative)",
- "D11: PanTilt (Absolute)",
- "D12: PanTilt (Relative)",
- "D13: Roll (Absolute)",
- "D14: Roll (Relative)",
- "D15: Reserved",
- "D16: Reserved",
- "D17: Focus, Auto",
- "D18: Privacy",
- "D19: Focus, Simple",
- "D20: Window",
- "D21: Region of Interest",
- "D22: Reserved, set to zero",
- "D23: Reserved, set to zero",
- };
-
- private static final String[] SUPPORTS_PROC = {
- "D0: Brightness",
- "D1: Contrast",
- "D2: Hue",
- "D3: Saturation",
- "D4: Sharpness",
- "D5: Gamma",
- "D6: White Balance Temperature",
- "D7: White Balance Component",
- "D8: Backlight Compensation",
- "D9: Gain",
- "D10: Power Line Frequency",
- "D11: Hue, Auto",
- "D12: White Balance Temperature, Auto",
- "D13: White Balance Component, Auto",
- "D14: Digital Multiplier",
- "D15: Digital Multiplier Limit",
- "D16: Analog Video Standard",
- "D17: Analog Video Lock Status",
- "D18: Contrast, Auto",
- "D19: Reserved. Set to zero",
- "D20: Reserved. Set to zero",
- "D21: Reserved. Set to zero",
- "D22: Reserved. Set to zero",
- "D23: Reserved. Set to zero",
- };
-
- private static final void dumpControls(final long controlSupports) {
- Log.i(TAG, String.format("controlSupports=%x", controlSupports));
- for (int i = 0; i < SUPPORTS_CTRL.length; i++) {
- Log.i(TAG, SUPPORTS_CTRL[i] + ((controlSupports & (0x1 << i)) != 0 ? "=enabled" : "=disabled"));
- }
- }
-
- private static final void dumpProc(final long procSupports) {
- Log.i(TAG, String.format("procSupports=%x", procSupports));
- for (int i = 0; i < SUPPORTS_PROC.length; i++) {
- Log.i(TAG, SUPPORTS_PROC[i] + ((procSupports & (0x1 << i)) != 0 ? "=enabled" : "=disabled"));
- }
- }
-
- private final String getUSBFSName(final UsbControlBlock ctrlBlock) {
- String result = null;
- final String name = ctrlBlock.getDeviceName();
- final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null;
- if ((v != null) && (v.length > 2)) {
- final StringBuilder sb = new StringBuilder(v[0]);
- for (int i = 1; i < v.length - 2; i++)
- sb.append("/").append(v[i]);
- result = sb.toString();
- }
- if (TextUtils.isEmpty(result)) {
- Log.w(TAG, "failed to get USBFS path, try to use default path:" + name);
- result = DEFAULT_USBFS;
- }
- return result;
- }
-
- // #nativeCreate and #nativeDestroy are not static methods.
- private final native long nativeCreate();
- private final native void nativeDestroy(final long id_camera);
-
- private final native int nativeConnect(long id_camera, int venderId, int productId, int fileDescriptor, int busNum, int devAddr, String usbfs);
- private static final native int nativeRelease(final long id_camera);
-
- private static final native int nativeSetStatusCallback(final long mNativePtr, final IStatusCallback callback);
- private static final native int nativeSetButtonCallback(final long mNativePtr, final IButtonCallback callback);
-
- private static final native int nativeSetPreviewSize(final long id_camera, final int width, final int height, final int min_fps, final int max_fps, final int mode, final float bandwidth);
- private static final native String nativeGetSupportedSize(final long id_camera);
- private static final native int nativeStartPreview(final long id_camera);
- private static final native int nativeStopPreview(final long id_camera);
- private static final native int nativeSetPreviewDisplay(final long id_camera, final Surface surface);
- private static final native int nativeSetFrameCallback(final long mNativePtr, final IFrameCallback callback, final int pixelFormat);
-
-//**********************************************************************
- /**
- * start movie capturing(this should call while previewing)
- * @param surface
- */
- public void startCapture(final Surface surface) {
- if (mCtrlBlock != null && surface != null) {
- nativeSetCaptureDisplay(mNativePtr, surface);
- } else
- throw new NullPointerException("startCapture");
- }
-
- /**
- * stop movie capturing
- */
- public void stopCapture() {
- if (mCtrlBlock != null) {
- nativeSetCaptureDisplay(mNativePtr, null);
- }
- }
- private static final native int nativeSetCaptureDisplay(final long id_camera, final Surface surface);
-
- private static final native long nativeGetCtrlSupports(final long id_camera);
- private static final native long nativeGetProcSupports(final long id_camera);
-
- private final native int nativeUpdateScanningModeLimit(final long id_camera);
- private static final native int nativeSetScanningMode(final long id_camera, final int scanning_mode);
- private static final native int nativeGetScanningMode(final long id_camera);
-
- private final native int nativeUpdateExposureModeLimit(final long id_camera);
- private static final native int nativeSetExposureMode(final long id_camera, final int exposureMode);
- private static final native int nativeGetExposureMode(final long id_camera);
-
- private final native int nativeUpdateExposurePriorityLimit(final long id_camera);
- private static final native int nativeSetExposurePriority(final long id_camera, final int priority);
- private static final native int nativeGetExposurePriority(final long id_camera);
-
- private final native int nativeUpdateExposureLimit(final long id_camera);
- private static final native int nativeSetExposure(final long id_camera, final int exposure);
- private static final native int nativeGetExposure(final long id_camera);
-
- private final native int nativeUpdateExposureRelLimit(final long id_camera);
- private static final native int nativeSetExposureRel(final long id_camera, final int exposure_rel);
- private static final native int nativeGetExposureRel(final long id_camera);
-
- private final native int nativeUpdateAutoFocusLimit(final long id_camera);
- private static final native int nativeSetAutoFocus(final long id_camera, final boolean autofocus);
- private static final native int nativeGetAutoFocus(final long id_camera);
-
- private final native int nativeUpdateFocusLimit(final long id_camera);
- private static final native int nativeSetFocus(final long id_camera, final int focus);
- private static final native int nativeGetFocus(final long id_camera);
-
- private final native int nativeUpdateFocusRelLimit(final long id_camera);
- private static final native int nativeSetFocusRel(final long id_camera, final int focus_rel);
- private static final native int nativeGetFocusRel(final long id_camera);
-
- private final native int nativeUpdateIrisLimit(final long id_camera);
- private static final native int nativeSetIris(final long id_camera, final int iris);
- private static final native int nativeGetIris(final long id_camera);
-
- private final native int nativeUpdateIrisRelLimit(final long id_camera);
- private static final native int nativeSetIrisRel(final long id_camera, final int iris_rel);
- private static final native int nativeGetIrisRel(final long id_camera);
-
- private final native int nativeUpdatePanLimit(final long id_camera);
- private static final native int nativeSetPan(final long id_camera, final int pan);
- private static final native int nativeGetPan(final long id_camera);
-
- private final native int nativeUpdatePanRelLimit(final long id_camera);
- private static final native int nativeSetPanRel(final long id_camera, final int pan_rel);
- private static final native int nativeGetPanRel(final long id_camera);
-
- private final native int nativeUpdateTiltLimit(final long id_camera);
- private static final native int nativeSetTilt(final long id_camera, final int tilt);
- private static final native int nativeGetTilt(final long id_camera);
-
- private final native int nativeUpdateTiltRelLimit(final long id_camera);
- private static final native int nativeSetTiltRel(final long id_camera, final int tilt_rel);
- private static final native int nativeGetTiltRel(final long id_camera);
-
- private final native int nativeUpdateRollLimit(final long id_camera);
- private static final native int nativeSetRoll(final long id_camera, final int roll);
- private static final native int nativeGetRoll(final long id_camera);
-
- private final native int nativeUpdateRollRelLimit(final long id_camera);
- private static final native int nativeSetRollRel(final long id_camera, final int roll_rel);
- private static final native int nativeGetRollRel(final long id_camera);
-
- private final native int nativeUpdateAutoWhiteBlanceLimit(final long id_camera);
- private static final native int nativeSetAutoWhiteBlance(final long id_camera, final boolean autoWhiteBlance);
- private static final native int nativeGetAutoWhiteBlance(final long id_camera);
-
- private final native int nativeUpdateAutoWhiteBlanceCompoLimit(final long id_camera);
- private static final native int nativeSetAutoWhiteBlanceCompo(final long id_camera, final boolean autoWhiteBlanceCompo);
- private static final native int nativeGetAutoWhiteBlanceCompo(final long id_camera);
-
- private final native int nativeUpdateWhiteBlanceLimit(final long id_camera);
- private static final native int nativeSetWhiteBlance(final long id_camera, final int whiteBlance);
- private static final native int nativeGetWhiteBlance(final long id_camera);
-
- private final native int nativeUpdateWhiteBlanceCompoLimit(final long id_camera);
- private static final native int nativeSetWhiteBlanceCompo(final long id_camera, final int whiteBlance_compo);
- private static final native int nativeGetWhiteBlanceCompo(final long id_camera);
-
- private final native int nativeUpdateBacklightCompLimit(final long id_camera);
- private static final native int nativeSetBacklightComp(final long id_camera, final int backlight_comp);
- private static final native int nativeGetBacklightComp(final long id_camera);
-
- private final native int nativeUpdateBrightnessLimit(final long id_camera);
- private static final native int nativeSetBrightness(final long id_camera, final int brightness);
- private static final native int nativeGetBrightness(final long id_camera);
-
- private final native int nativeUpdateContrastLimit(final long id_camera);
- private static final native int nativeSetContrast(final long id_camera, final int contrast);
- private static final native int nativeGetContrast(final long id_camera);
-
- private final native int nativeUpdateAutoContrastLimit(final long id_camera);
- private static final native int nativeSetAutoContrast(final long id_camera, final boolean autocontrast);
- private static final native int nativeGetAutoContrast(final long id_camera);
-
- private final native int nativeUpdateSharpnessLimit(final long id_camera);
- private static final native int nativeSetSharpness(final long id_camera, final int sharpness);
- private static final native int nativeGetSharpness(final long id_camera);
-
- private final native int nativeUpdateGainLimit(final long id_camera);
- private static final native int nativeSetGain(final long id_camera, final int gain);
- private static final native int nativeGetGain(final long id_camera);
-
- private final native int nativeUpdateGammaLimit(final long id_camera);
- private static final native int nativeSetGamma(final long id_camera, final int gamma);
- private static final native int nativeGetGamma(final long id_camera);
-
- private final native int nativeUpdateSaturationLimit(final long id_camera);
- private static final native int nativeSetSaturation(final long id_camera, final int saturation);
- private static final native int nativeGetSaturation(final long id_camera);
-
- private final native int nativeUpdateHueLimit(final long id_camera);
- private static final native int nativeSetHue(final long id_camera, final int hue);
- private static final native int nativeGetHue(final long id_camera);
-
- private final native int nativeUpdateAutoHueLimit(final long id_camera);
- private static final native int nativeSetAutoHue(final long id_camera, final boolean autohue);
- private static final native int nativeGetAutoHue(final long id_camera);
-
- private final native int nativeUpdatePowerlineFrequencyLimit(final long id_camera);
- private static final native int nativeSetPowerlineFrequency(final long id_camera, final int frequency);
- private static final native int nativeGetPowerlineFrequency(final long id_camera);
-
- private final native int nativeUpdateZoomLimit(final long id_camera);
- private static final native int nativeSetZoom(final long id_camera, final int zoom);
- private static final native int nativeGetZoom(final long id_camera);
-
- private final native int nativeUpdateZoomRelLimit(final long id_camera);
- private static final native int nativeSetZoomRel(final long id_camera, final int zoom_rel);
- private static final native int nativeGetZoomRel(final long id_camera);
-
- private final native int nativeUpdateDigitalMultiplierLimit(final long id_camera);
- private static final native int nativeSetDigitalMultiplier(final long id_camera, final int multiplier);
- private static final native int nativeGetDigitalMultiplier(final long id_camera);
-
- private final native int nativeUpdateDigitalMultiplierLimitLimit(final long id_camera);
- private static final native int nativeSetDigitalMultiplierLimit(final long id_camera, final int multiplier_limit);
- private static final native int nativeGetDigitalMultiplierLimit(final long id_camera);
-
- private final native int nativeUpdateAnalogVideoStandardLimit(final long id_camera);
- private static final native int nativeSetAnalogVideoStandard(final long id_camera, final int standard);
- private static final native int nativeGetAnalogVideoStandard(final long id_camera);
-
- private final native int nativeUpdateAnalogVideoLockStateLimit(final long id_camera);
- private static final native int nativeSetAnalogVideoLoackState(final long id_camera, final int state);
- private static final native int nativeGetAnalogVideoLoackState(final long id_camera);
-
- private final native int nativeUpdatePrivacyLimit(final long id_camera);
- private static final native int nativeSetPrivacy(final long id_camera, final boolean privacy);
- private static final native int nativeGetPrivacy(final long id_camera);
- private static final native int nativeSetPreviewFrameCallback(final long mNativePtr, final IPreviewFrameCallback callback, final int pixelFormat); // MODIFIED
-
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/Android.mk b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/Android.mk
deleted file mode 100755
index eb8ddeec09..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/Android.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-#include $(call all-subdir-makefiles)
-PROJ_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-include $(PROJ_PATH)/UVCCamera/Android.mk
-include $(PROJ_PATH)/libjpeg-turbo-1.5.0/Android.mk
-include $(PROJ_PATH)/libusb/android/jni/Android.mk
-include $(PROJ_PATH)/libuvc/android/jni/Android.mk
\ No newline at end of file
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/Application.mk b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/Application.mk
deleted file mode 100755
index 1af65a36cb..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/Application.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-#/*
-# * UVCCamera
-# * library and sample to access to UVC web camera on non-rooted Android device
-# *
-# * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
-# *
-# * File name: Application.mk
-# *
-# * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-#*/
-
-# This is just for mips, if you really needs MSA, un-comment and build with GCC.
-# Note: Supporting GCC on NDK is already deprecated and GCC will be removed from NDK soon.
-NDK_TOOLCHAIN_VERSION := 4.9
-
-APP_PLATFORM := android-14
-APP_ABI := armeabi-v7a arm64-v8a x86
-#APP_OPTIM := debug
-APP_OPTIM := release
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Android.mk b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Android.mk
deleted file mode 100755
index a7d67e4c5f..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Android.mk
+++ /dev/null
@@ -1,66 +0,0 @@
-#/*
-# * UVCCamera
-# * library and sample to access to UVC web camera on non-rooted Android device
-# *
-# * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
-# *
-# * File name: Android.mk
-# *
-# * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-#*/
-
-######################################################################
-# Make shared library libUVCCamera.so
-######################################################################
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-######################################################################
-# Make shared library libUVCCamera.so
-######################################################################
-CFLAGS := -Werror
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/ \
- $(LOCAL_PATH)/../ \
- $(LOCAL_PATH)/../rapidjson/include \
-
-LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%)
-LOCAL_CFLAGS += -DANDROID_NDK
-LOCAL_CFLAGS += -DLOG_NDEBUG
-LOCAL_CFLAGS += -DACCESS_RAW_DESCRIPTORS
-LOCAL_CFLAGS += -O3 -fstrict-aliasing -fprefetch-loop-arrays
-
-LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl
-LOCAL_LDLIBS += -llog
-LOCAL_LDLIBS += -landroid
-
-LOCAL_SHARED_LIBRARIES += usb100 uvc
-
-LOCAL_ARM_MODE := arm
-
-LOCAL_SRC_FILES := \
- _onload.cpp \
- utilbase.cpp \
- UVCCamera.cpp \
- UVCPreview.cpp \
- UVCButtonCallback.cpp \
- UVCStatusCallback.cpp \
- Parameters.cpp \
- serenegiant_usb_UVCCamera.cpp
-
-LOCAL_MODULE := UVCCamera
-include $(BUILD_SHARED_LIBRARY)
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Parameters.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Parameters.cpp
deleted file mode 100755
index 1caf0f0e00..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Parameters.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: Parameters.cpp
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-#define LOG_TAG "Parameters"
-
-#include "Parameters.h"
-#include "rapidjson/rapidjson.h"
-#include "rapidjson/stringbuffer.h"
-#include "rapidjson/writer.h"
-#include "libuvc/libuvc_internal.h"
-
-using namespace rapidjson;
-
-static void write(Writer &writer, const char *key, const char *value) {
- writer.String(key);
- writer.String(value);
-}
-
-static void write(Writer &writer, const char *key, uint16_t value) {
- writer.String(key);
- writer.Uint(value);
-}
-
-static void write(Writer &writer, const char *key, int32_t value) {
- writer.String(key);
- writer.Int(value);
-}
-
-static void write(Writer &writer, const char *key, uint32_t value) {
- writer.String(key);
- writer.Uint(value);
-}
-
-static void write(Writer &writer, const char *key, int64_t value) {
- writer.String(key);
- writer.Int64(value);
-}
-
-static void write(Writer &writer, const char *key, uint64_t value) {
- writer.String(key);
- writer.Uint64(value);
-}
-
-static const char *_uvc_name_for_format_subtype(uint8_t subtype) {
- switch (subtype) {
- case UVC_VS_FORMAT_UNCOMPRESSED:
- return "UncompressedFormat";
- case UVC_VS_FORMAT_MJPEG:
- return "MJPEGFormat";
- default:
- return "Unknown";
- }
-}
-
-#define INDEX "index"
-#define TYPE "type"
-#define SUBTYPE "subType"
-#define WIDTH "width"
-#define HEIGHT "height"
-#define VALUE "value"
-#define DETAIL "detail"
-
-#define DESCRIPTION "description"
-#define DESC_SUBTYPE SUBTYPE
-#define DESC_VENDERID "venderId"
-#define DESC_PRODUCTID "productId"
-#define DESC_SERIALNUMBER "serialNumber"
-#define DESC_MANIFUCTURE "manifuctureName"
-#define DESC_PRODUCT "productName"
-#define DESC_UVC "uvc"
-#define DESC_VIDEO_CONTROL "videoControl"
-#define DESC_INTERFACES "interfaces"
-
-#define INTERFACE_TYPE TYPE
-#define INTERFACE_TYPE_VIDEOSTREAM "videoStreaming"
-#define INTERFACE_TYPE_AUDIOSTREAM "audioStreaming"
-#define INTERFACE_INDEX INDEX
-#define INTERFACE_ENDPOINT_ADDR "endpointAddress"
-
-#define FORMATS "formats"
-#define FORMAT_INDEX INDEX
-#define FORMAT_NAME "format"
-#define FORMAT_DETAIL DETAIL
-#define FORMAT_BITS_PER_PIXEL "bitsPerPixel"
-#define FORMAT_GUID "GUID"
-#define FORMAT_DEFAULT_FRAME_INDEX "defaultFrameIndex"
-#define FORMAT_ASPECTRATIO_X "aspectRatioX"
-#define FORMAT_ASPECTRATIO_Y "aspectRatioY"
-#define FORMAT_INTERLACE_FLAGS "interlaceFlags"
-#define FORMAT_COPY_PROTECT "copyProtect"
-#define FORMAT_FRAMEDESCRIPTORS "frameDescriptors"
-
-#define FRAME_INDEX INDEX
-#define FRAME_CAPABILITIES "capabilities"
-#define FRAME_WIDTH WIDTH
-#define FRAME_HEIGHT HEIGHT
-#define FRAME_BITRATE_MIN "minBitRate"
-#define FRAME_BITRATE_MAX "maxBitRate"
-#define FRAME_FRAMEBUFFERSIZE_MAX "maxFrameBufferSize"
-#define FRAME_INTERVAL_DEFAULT "defaultFrameInterval"
-#define FRAME_FPS_DEFAULT "defaultFps"
-#define FRAME_INTERVALS "intervals"
-#define FRAME_INTERVAL_INDEX INDEX
-#define FRAME_INTERVAL_VALUE VALUE
-#define FRAME_INTERVAL_FPS "fps"
-#define FRAME_INTERVAL_MIN "minFrameInterval"
-#define FRAME_INTERVAL_MAX "maxFrameInterval"
-#define FRAME_INTERVAL_STEP "frameIntervalStep"
-
-static void writerFormat(Writer &writer, uvc_format_desc_t *fmt_desc) {
- uvc_frame_desc_t *frame_desc;
- char work[256];
-
- writer.String(FORMAT_DETAIL);
- writer.StartObject();
- {
- write(writer, FORMAT_BITS_PER_PIXEL, fmt_desc->bBitsPerPixel);
- sprintf(work,
- "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- fmt_desc->guidFormat[0], fmt_desc->guidFormat[1],
- fmt_desc->guidFormat[2], fmt_desc->guidFormat[3],
- fmt_desc->guidFormat[4], fmt_desc->guidFormat[5],
- fmt_desc->guidFormat[6], fmt_desc->guidFormat[7],
- fmt_desc->guidFormat[8], fmt_desc->guidFormat[9],
- fmt_desc->guidFormat[10], fmt_desc->guidFormat[11],
- fmt_desc->guidFormat[12], fmt_desc->guidFormat[13],
- fmt_desc->guidFormat[14], fmt_desc->guidFormat[15]);
- write(writer, FORMAT_GUID, work);
- write(writer, FORMAT_DEFAULT_FRAME_INDEX, fmt_desc->bDefaultFrameIndex);
- write(writer, FORMAT_ASPECTRATIO_X, fmt_desc->bAspectRatioX);
- write(writer, FORMAT_ASPECTRATIO_Y, fmt_desc->bAspectRatioY);
- write(writer, FORMAT_INTERLACE_FLAGS, fmt_desc->bmInterlaceFlags);
- write(writer, FORMAT_COPY_PROTECT, fmt_desc->bCopyProtect);
-
- writer.String(FORMAT_FRAMEDESCRIPTORS);
- writer.StartArray();
- DL_FOREACH(fmt_desc->frame_descs, frame_desc)
- {
- uint32_t *interval_ptr;
-
- writer.StartObject();
- {
- write(writer, FRAME_INDEX, frame_desc->bFrameIndex);
- write(writer, FRAME_CAPABILITIES, frame_desc->bmCapabilities);
- write(writer, FRAME_WIDTH, frame_desc->wWidth);
- write(writer, FRAME_HEIGHT, frame_desc->wHeight);
- write(writer, FRAME_BITRATE_MIN, frame_desc->dwMinBitRate);
- write(writer, FRAME_BITRATE_MAX, frame_desc->dwMaxBitRate);
- write(writer, FRAME_FRAMEBUFFERSIZE_MAX, frame_desc->dwMaxVideoFrameBufferSize);
- write(writer, FRAME_INTERVAL_DEFAULT, frame_desc->dwDefaultFrameInterval);
- write(writer, FRAME_FPS_DEFAULT, 10000000 / frame_desc->dwDefaultFrameInterval);
-
- if (frame_desc->intervals) {
- writer.String(FRAME_INTERVALS);
- writer.StartArray();
- for (interval_ptr = frame_desc->intervals; *interval_ptr; ++interval_ptr) {
- writer.StartObject();
- write(writer, FRAME_INTERVAL_INDEX, (int ) (interval_ptr - frame_desc->intervals));
- write(writer, FRAME_INTERVAL_VALUE, *interval_ptr);
- write(writer, FRAME_INTERVAL_FPS, 10000000 / *interval_ptr);
- writer.EndObject();
- }
- writer.EndArray();
- } else {
- // 最小fps
- writer.String(FRAME_INTERVAL_MIN);
- writer.StartObject();
- {
- write(writer, FRAME_INTERVAL_INDEX, frame_desc->dwMinFrameInterval);
- write(writer, FRAME_INTERVAL_VALUE, frame_desc->dwMinFrameInterval);
- write(writer, FRAME_INTERVAL_FPS, 10000000 / frame_desc->dwMinFrameInterval);
- }
- writer.EndObject();
- // 最大fps
- writer.String(FRAME_INTERVAL_MAX);
- writer.StartObject();
- {
- write(writer, FRAME_INTERVAL_INDEX, frame_desc->dwMaxFrameInterval);
- write(writer, FRAME_INTERVAL_VALUE, frame_desc->dwMaxFrameInterval);
- write(writer, FRAME_INTERVAL_FPS, 10000000 / frame_desc->dwMaxFrameInterval);
- }
- writer.EndObject();
- if (frame_desc->dwFrameIntervalStep) {
- // fpsステップ
- writer.String(FRAME_INTERVAL_STEP);
- writer.StartObject();
- {
- write(writer, FRAME_INTERVAL_INDEX, frame_desc->dwFrameIntervalStep);
- write(writer, FRAME_INTERVAL_VALUE, frame_desc->dwFrameIntervalStep);
- write(writer, FRAME_INTERVAL_FPS, 10000000 / frame_desc->dwFrameIntervalStep);
- }
- writer.EndObject();
- }
- }
- }
- writer.EndObject();
- }
- writer.EndArray(); // end of FORMAT_FRAMEDESCRIPTORS
- }
- writer.EndObject(); // end of FORMAT_DETAIL
-}
-
-static void writerFormatDescriptions(Writer &writer, uvc_streaming_interface_t *stream_if) {
-
- uvc_format_desc_t *fmt_desc;
- int i;
-
- writer.String(FORMATS);
- writer.StartArray();
- DL_FOREACH(stream_if->format_descs, fmt_desc)
- {
- writer.StartObject();
- {
- write(writer, FORMAT_INDEX, fmt_desc->bFormatIndex);
- write(writer, DESC_SUBTYPE, fmt_desc->bDescriptorSubtype);
- write(writer, FORMAT_NAME, _uvc_name_for_format_subtype(fmt_desc->bDescriptorSubtype));
- switch (fmt_desc->bDescriptorSubtype) {
- case UVC_VS_FORMAT_UNCOMPRESSED:
- case UVC_VS_FORMAT_MJPEG:
- writerFormat(writer, fmt_desc);
- break;
- default:
- break;
- }
- }
- writer.EndObject();
- }
- writer.EndArray(); // end of FORMATS
-}
-
-UVCDiags::UVCDiags() {}
-UVCDiags::~UVCDiags() {};
-
-char *UVCDiags::getDescriptions(const uvc_device_handle_t *deviceHandle) {
- StringBuffer buffer;
- Writer writer(buffer);
- char work[256];
-
- ENTER();
- writer.StartObject();
- {
- writer.String(DESCRIPTION);
- writer.StartObject();
- {
- uvc_device_descriptor_t *desc;
- uvc_get_device_descriptor(deviceHandle->dev, &desc);
- write(writer, DESC_VENDERID, desc->idVendor);
- write(writer, DESC_PRODUCTID, desc->idProduct);
- write(writer, DESC_SERIALNUMBER, desc->serialNumber ? desc->serialNumber : "[none]");
- write(writer, DESC_MANIFUCTURE, desc->manufacturer ? desc->manufacturer : "[unknown]");
-// write(writer, DESC_PRODUCT, desc->product ? desc->product : "UVC Camera");
- if (desc->product)
- write(writer, DESC_PRODUCT, desc->product);
- else {
- sprintf(work, "UVC Camera (%x:%x)", desc->idVendor, desc->idProduct);
- write(writer, DESC_PRODUCT, work);
- }
- uvc_free_device_descriptor(desc);
-
- if (deviceHandle->info->ctrl_if.bcdUVC) {
- writer.String(DESC_UVC);
- writer.StartObject();
- {
- write(writer, DESC_VIDEO_CONTROL, deviceHandle->info->ctrl_if.bcdUVC);
-
- writer.String(DESC_INTERFACES);
- writer.StartArray();
- {
- assert(deviceHandle->info->stream_ifs);
- uvc_streaming_interface_t *stream_if;
- int stream_idx = 0;
-
- DL_FOREACH(deviceHandle->info->stream_ifs, stream_if)
- {
- ++stream_idx;
- writer.StartObject();
- {
- write(writer, INTERFACE_TYPE, INTERFACE_TYPE_VIDEOSTREAM);
- write(writer, INTERFACE_INDEX, stream_idx);
- write(writer, INTERFACE_ENDPOINT_ADDR, stream_if->bEndpointAddress);
- writerFormatDescriptions(writer, stream_if);
- }
- writer.EndObject();
- }
- }
- writer.EndArray(); // end of DESC_INTERFACES
- }
- writer.EndObject(); // end of DESC_UVC
- }
- // XXX other interfaces
- }
- writer.EndObject(); // end of DESCRIPTION
- }
- writer.EndObject();
- RETURN(strdup(buffer.GetString()), char *);
-}
-
-char *UVCDiags::getCurrentStream(const uvc_stream_ctrl_t *ctrl) {
- StringBuffer buffer;
- Writer writer(buffer);
-
- ENTER();
- writer.StartObject();
- {
- write(writer, "hint", ctrl->bmHint);
- write(writer, "formatIndex", ctrl->bFormatIndex);
- write(writer, "frameIndex", ctrl->bFrameIndex);
- write(writer, "frameInterval", ctrl->dwFrameInterval);
- write(writer, "keyFrameRate", ctrl->wKeyFrameRate);
- write(writer, "frameRate", ctrl->wPFrameRate);
- write(writer, "compQuality", ctrl->wCompQuality);
- write(writer, "compWindowSize", ctrl->wCompWindowSize);
- write(writer, "delay", ctrl->wDelay);
- write(writer, "maxVideoFrameSize", ctrl->dwMaxVideoFrameSize);
- write(writer, "maxPayloadTransferSize", ctrl->dwMaxPayloadTransferSize);
- write(writer, "interfaceNumber", ctrl->bInterfaceNumber);
- }
- writer.EndObject();
- RETURN(strdup(buffer.GetString()), char *);
-}
-
-char *UVCDiags::getSupportedSize(const uvc_device_handle_t *deviceHandle) {
- StringBuffer buffer;
- Writer writer(buffer);
- char buf[256];
-
- ENTER();
- writer.StartObject();
- {
- if (deviceHandle->info->stream_ifs) {
- uvc_streaming_interface_t *stream_if;
- int stream_idx = 0;
-
- writer.String("formats");
- writer.StartArray();
- DL_FOREACH(deviceHandle->info->stream_ifs, stream_if)
- {
- ++stream_idx;
- uvc_format_desc_t *fmt_desc;
- uvc_frame_desc_t *frame_desc;
- DL_FOREACH(stream_if->format_descs, fmt_desc)
- {
- writer.StartObject();
- {
- switch (fmt_desc->bDescriptorSubtype) {
- case UVC_VS_FORMAT_UNCOMPRESSED:
- case UVC_VS_FORMAT_MJPEG:
- // MODIFIED--->
- case UVC_VS_FORMAT_MPEG2TS:
- case UVC_VS_FORMAT_DV:
- case UVC_VS_FORMAT_FRAME_BASED:
- // <---MODIFIED
- write(writer, "index", fmt_desc->bFormatIndex);
- write(writer, "type", fmt_desc->bDescriptorSubtype);
- write(writer, "default", fmt_desc->bDefaultFrameIndex);
- writer.String("size");
- writer.StartArray();
- DL_FOREACH(fmt_desc->frame_descs, frame_desc)
- {
- snprintf(buf, sizeof(buf), "%dx%d", frame_desc->wWidth, frame_desc->wHeight);
- buf[sizeof(buf)-1] = '\0';
- writer.String(buf);
- }
- writer.EndArray();
- break;
- default:
- break;
- }
- }
- writer.EndObject();
- }
- }
- writer.EndArray();
- // FIXME still image is not supported now
- }
- }
- writer.EndObject();
- RETURN(strdup(buffer.GetString()), char *);
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Parameters.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Parameters.h
deleted file mode 100755
index 436a4ef41f..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/Parameters.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2015-2017 saki t_saki@serenegiant.com
- *
- * File name: Parameters.h
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-#ifndef PARAMETERS_H_
-#define PARAMETERS_H_
-
-#pragma interface
-
-#include "libUVCCamera.h"
-
-class UVCDiags {
-private:
-public:
- UVCDiags();
- ~UVCDiags();
- char *getDescriptions(const uvc_device_handle_t *deviceHandle);
- char *getCurrentStream(const uvc_stream_ctrl_t *ctrl);
- char *getSupportedSize(const uvc_device_handle_t *deviceHandle);
-};
-
-#endif /* PARAMETERS_H_ */
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCButtonCallback.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCButtonCallback.cpp
deleted file mode 100755
index 76fa288a81..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCButtonCallback.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include
-#include
-#include
-#include "utilbase.h"
-#include "UVCButtonCallback.h"
-#include "libuvc_internal.h"
-
-#define LOCAL_DEBUG 0
-
-UVCButtonCallback::UVCButtonCallback(uvc_device_handle_t *devh)
-: mDeviceHandle(devh),
- mButtonCallbackObj(NULL) {
-
- ENTER();
- pthread_mutex_init(&button_mutex, NULL);
-
- uvc_set_button_callback(mDeviceHandle, uvc_button_callback, (void *)this);
- EXIT();
-}
-
-UVCButtonCallback::~UVCButtonCallback() {
-
- ENTER();
- pthread_mutex_destroy(&button_mutex);
- EXIT();
-}
-
-int UVCButtonCallback::setCallback(JNIEnv *env, jobject button_callback_obj) {
-
- ENTER();
- pthread_mutex_lock(&button_mutex);
- {
- if (!env->IsSameObject(mButtonCallbackObj, button_callback_obj)) {
- ibuttoncallback_fields.onButton = NULL;
- if (mButtonCallbackObj) {
- env->DeleteGlobalRef(mButtonCallbackObj);
- }
- mButtonCallbackObj = button_callback_obj;
- if (button_callback_obj) {
- // get method IDs of Java object for callback
- jclass clazz = env->GetObjectClass(button_callback_obj);
- if (LIKELY(clazz)) {
- ibuttoncallback_fields.onButton = env->GetMethodID(clazz,
- "onButton", "(II)V");
- } else {
- LOGW("failed to get object class");
- }
- env->ExceptionClear();
- if (!ibuttoncallback_fields.onButton) {
- LOGE("Can't find IButtonCallback#onButton");
- env->DeleteGlobalRef(button_callback_obj);
- mButtonCallbackObj = button_callback_obj = NULL;
- }
- }
- }
- }
- pthread_mutex_unlock(&button_mutex);
- RETURN(0, int);
-}
-
-void UVCButtonCallback::notifyButtonCallback(JNIEnv* env, int button, int state) {
-
- pthread_mutex_lock(&button_mutex);
- {
- if (mButtonCallbackObj) {
- env->CallVoidMethod(mButtonCallbackObj, ibuttoncallback_fields.onButton, button, state);
- env->ExceptionClear();
- }
- }
- pthread_mutex_unlock(&button_mutex);
-}
-
-void UVCButtonCallback::uvc_button_callback(int button, int state, void *user_ptr) {
-
- UVCButtonCallback *buttonCallback = reinterpret_cast(user_ptr);
-
- JavaVM *vm = getVM();
- JNIEnv *env;
- // attach to JavaVM
- vm->AttachCurrentThread(&env, NULL);
-
- buttonCallback->notifyButtonCallback(env, button, state);
-
- vm->DetachCurrentThread();
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCButtonCallback.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCButtonCallback.h
deleted file mode 100755
index 1da96e5825..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCButtonCallback.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef UVCBUTTONCALLBACK_H_
-#define UVCBUTTONCALLBACK_H_
-
-#include "libUVCCamera.h"
-#include
-#include
-#include "objectarray.h"
-
-#pragma interface
-
-// for callback to Java object
-typedef struct {
- jmethodID onButton;
-} Fields_ibuttoncallback;
-
-class UVCButtonCallback {
-private:
- uvc_device_handle_t *mDeviceHandle;
- pthread_mutex_t button_mutex;
- jobject mButtonCallbackObj;
- Fields_ibuttoncallback ibuttoncallback_fields;
- void notifyButtonCallback(JNIEnv *env, int button, int state);
- static void uvc_button_callback(int button, int state, void *user_ptr);
-public:
- UVCButtonCallback(uvc_device_handle_t *devh);
- ~UVCButtonCallback();
-
- int setCallback(JNIEnv *env, jobject button_callback_obj);
-};
-
-#endif /* UVCBUTTONCALLBACK_H_ */
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCCamera.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCCamera.cpp
deleted file mode 100755
index c7993b9e74..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCCamera.cpp
+++ /dev/null
@@ -1,2295 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: UVCCamera.cpp
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#define LOG_TAG "UVCCamera"
-#if 1 // デバッグ情報を出さない時1
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // LOGV/LOGD/MARKを出力しない時
- #endif
- #undef USE_LOGALL // 指定したLOGxだけを出力
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG
- #define GET_RAW_DESCRIPTOR
-#endif
-
-//**********************************************************************
-//
-//**********************************************************************
-#include
-#include
-#include
-#include
-#include "UVCCamera.h"
-#include "Parameters.h"
-#include "libuvc_internal.h"
-
-#define LOCAL_DEBUG 0
-
-//**********************************************************************
-//
-//**********************************************************************
-/**
- * コンストラクタ
- */
-UVCCamera::UVCCamera()
-: mFd(0),
- mUsbFs(NULL),
- mContext(NULL),
- mDevice(NULL),
- mDeviceHandle(NULL),
- mStatusCallback(NULL),
- mButtonCallback(NULL),
- mPreview(NULL),
- mCtrlSupports(0),
- mPUSupports(0) {
-
- ENTER();
- clearCameraParams();
- EXIT();
-}
-
-/**
- * デストラクタ
- */
-UVCCamera::~UVCCamera() {
- ENTER();
- release();
- if (mContext) {
- uvc_exit(mContext);
- mContext = NULL;
- }
- if (mUsbFs) {
- free(mUsbFs);
- mUsbFs = NULL;
- }
- EXIT();
-}
-
-void UVCCamera::clearCameraParams() {
- mCtrlSupports = mPUSupports = 0;
- mScanningMode.min = mScanningMode.max = mScanningMode.def = 0;
- mExposureMode.min = mExposureMode.max = mExposureMode.def = 0;
- mExposurePriority.min = mExposurePriority.max = mExposurePriority.def = 0;
- mExposureAbs.min = mExposureAbs.max = mExposureAbs.def = 0;
- mAutoFocus.min = mAutoFocus.max = mAutoFocus.def = 0;
- mAutoWhiteBlance.min = mAutoWhiteBlance.max = mAutoWhiteBlance.def = 0;
- mWhiteBlance.min = mWhiteBlance.max = mWhiteBlance.def = 0;
- mAutoWhiteBlanceCompo.min = mAutoWhiteBlanceCompo.max = mAutoWhiteBlanceCompo.def = 0;
- mWhiteBlanceCompo.min = mWhiteBlanceCompo.max = mWhiteBlanceCompo.def = 0;
- mBacklightComp.min = mBacklightComp.max = mBacklightComp.def = 0;
- mBrightness.min = mBrightness.max = mBrightness.def = 0;
- mContrast.min = mContrast.max = mContrast.def = 0;
- mAutoContrast.min = mAutoContrast.max = mAutoContrast.def = 0;
- mSharpness.min = mSharpness.max = mSharpness.def = 0;
- mGain.min = mGain.max = mGain.def = 0;
- mGamma.min = mGamma.max = mGamma.def = 0;
- mSaturation.min = mSaturation.max = mSaturation.def = 0;
- mHue.min = mHue.max = mHue.def = 0;
- mAutoHue.min = mAutoHue.max = mAutoHue.def = 0;
- mZoom.min = mZoom.max = mZoom.def = 0;
- mZoomRel.min = mZoomRel.max = mZoomRel.def = 0;
- mFocus.min = mFocus.max = mFocus.def = 0;
- mFocusRel.min = mFocusRel.max = mFocusRel.def = 0;
- mFocusSimple.min = mFocusSimple.max = mFocusSimple.def = 0;
- mIris.min = mIris.max = mIris.def = 0;
- mIrisRel.min = mIrisRel.max = mIrisRel.def = 0;
- mPan.min = mPan.max = mPan.def = 0; mPan.current = -1;
- mTilt.min = mTilt.max = mTilt.def = 0; mTilt.current = -1;
- mRoll.min = mRoll.max = mRoll.def = 0;
- mPanRel.min = mPanRel.max = mPanRel.def = 0; mPanRel.current = -1;
- mTiltRel.min = mTiltRel.max = mTiltRel.def = 0; mTiltRel.current = -1;
- mRollRel.min = mRollRel.max = mRollRel.def = 0;
- mPrivacy.min = mPrivacy.max = mPrivacy.def = 0;
- mPowerlineFrequency.min = mPowerlineFrequency.max = mPowerlineFrequency.def = 0;
- mMultiplier.min = mMultiplier.max = mMultiplier.def = 0;
- mMultiplierLimit.min = mMultiplierLimit.max = mMultiplierLimit.def = 0;
- mAnalogVideoStandard.min = mAnalogVideoStandard.max = mAnalogVideoStandard.def = 0;
- mAnalogVideoLockState.min = mAnalogVideoLockState.max = mAnalogVideoLockState.def = 0;
-}
-
-//======================================================================
-/**
- * カメラへ接続する
- */
-int UVCCamera::connect(int vid, int pid, int fd, int busnum, int devaddr, const char *usbfs) {
- ENTER();
- uvc_error_t result = UVC_ERROR_BUSY;
- if (!mDeviceHandle && fd) {
- if (mUsbFs)
- free(mUsbFs);
- mUsbFs = strdup(usbfs);
- if (UNLIKELY(!mContext)) {
- result = uvc_init2(&mContext, NULL, mUsbFs);
-// libusb_set_debug(mContext->usb_ctx, LIBUSB_LOG_LEVEL_DEBUG);
- if (UNLIKELY(result < 0)) {
- LOGD("failed to init libuvc");
- RETURN(result, int);
- }
- }
- // カメラ機能フラグをクリア
- clearCameraParams();
- fd = dup(fd);
- // 指定したvid,idを持つデバイスを検索, 見つかれば0を返してmDeviceに見つかったデバイスをセットする(既に1回uvc_ref_deviceを呼んである)
-// result = uvc_find_device2(mContext, &mDevice, vid, pid, NULL, fd);
- result = uvc_get_device_with_fd(mContext, &mDevice, vid, pid, NULL, fd, busnum, devaddr);
- if (LIKELY(!result)) {
- // カメラのopen処理
- result = uvc_open(mDevice, &mDeviceHandle);
- if (LIKELY(!result)) {
- // open出来た時
-#if LOCAL_DEBUG
- uvc_print_diag(mDeviceHandle, stderr);
-#endif
- mFd = fd;
- mStatusCallback = new UVCStatusCallback(mDeviceHandle);
- mButtonCallback = new UVCButtonCallback(mDeviceHandle);
- mPreview = new UVCPreview(mDeviceHandle);
- } else {
- // open出来なかった時
- LOGE("could not open camera:err=%d", result);
- uvc_unref_device(mDevice);
-// SAFE_DELETE(mDevice); // 参照カウンタが0ならuvc_unref_deviceでmDeviceがfreeされるから不要 XXX クラッシュ, 既に破棄されているのを再度破棄しようとしたからみたい
- mDevice = NULL;
- mDeviceHandle = NULL;
- close(fd);
- }
- } else {
- LOGE("could not find camera:err=%d", result);
- close(fd);
- }
- } else {
- // カメラが既にopenしている時
- LOGW("camera is already opened. you should release first");
- }
- RETURN(result, int);
-}
-
-// カメラを開放する
-int UVCCamera::release() {
- ENTER();
- stopPreview();
- // カメラのclose処理
- if (LIKELY(mDeviceHandle)) {
- MARK("カメラがopenしていたら開放する");
- // ステータスコールバックオブジェクトを破棄
- SAFE_DELETE(mStatusCallback);
- SAFE_DELETE(mButtonCallback);
- // プレビューオブジェクトを破棄
- SAFE_DELETE(mPreview);
- // カメラをclose
- uvc_close(mDeviceHandle);
- mDeviceHandle = NULL;
- }
- if (LIKELY(mDevice)) {
- MARK("カメラを開放");
- uvc_unref_device(mDevice);
- mDevice = NULL;
- }
- // カメラ機能フラグをクリア
- clearCameraParams();
- if (mUsbFs) {
- close(mFd);
- mFd = 0;
- free(mUsbFs);
- mUsbFs = NULL;
- }
- RETURN(0, int);
-}
-
-int UVCCamera::setStatusCallback(JNIEnv *env, jobject status_callback_obj) {
- ENTER();
- int result = EXIT_FAILURE;
- if (mStatusCallback) {
- result = mStatusCallback->setCallback(env, status_callback_obj);
- }
- RETURN(result, int);
-}
-
-int UVCCamera::setButtonCallback(JNIEnv *env, jobject button_callback_obj) {
- ENTER();
- int result = EXIT_FAILURE;
- if (mButtonCallback) {
- result = mButtonCallback->setCallback(env, button_callback_obj);
- }
- RETURN(result, int);
-}
-
-char *UVCCamera::getSupportedSize() {
- ENTER();
- if (mDeviceHandle) {
- UVCDiags params;
- RETURN(params.getSupportedSize(mDeviceHandle), char *)
- }
- RETURN(NULL, char *);
-}
-
-int UVCCamera::setPreviewSize(int width, int height, int min_fps, int max_fps, int mode, float bandwidth) {
- ENTER();
- int result = EXIT_FAILURE;
- if (mPreview) {
- result = mPreview->setPreviewSize(width, height, min_fps, max_fps, mode, bandwidth);
- }
- RETURN(result, int);
-}
-
-int UVCCamera::setPreviewDisplay(ANativeWindow *preview_window) {
- ENTER();
- int result = EXIT_FAILURE;
- if (mPreview) {
- result = mPreview->setPreviewDisplay(preview_window);
- }
- RETURN(result, int);
-}
-
-int UVCCamera::setFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format) {
- ENTER();
- int result = EXIT_FAILURE;
- if (mPreview) {
- result = mPreview->setFrameCallback(env, frame_callback_obj, pixel_format);
- }
- RETURN(result, int);
-}
-// MODIFIED
-int UVCCamera::setPreviewFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format) {
- ENTER();
- int result = EXIT_FAILURE;
- if (mPreview) {
- result = mPreview->setPreviewFrameCallback(env, frame_callback_obj, pixel_format);
- }
- RETURN(result, int);
-}
-
-
-int UVCCamera::startPreview() {
- ENTER();
-
- int result = EXIT_FAILURE;
- if (mDeviceHandle) {
- return mPreview->startPreview();
- }
- RETURN(result, int);
-}
-
-int UVCCamera::stopPreview() {
- ENTER();
- if (LIKELY(mPreview)) {
- mPreview->stopPreview();
- }
- RETURN(0, int);
-}
-
-int UVCCamera::setCaptureDisplay(ANativeWindow *capture_window) {
- ENTER();
- int result = EXIT_FAILURE;
- if (mPreview) {
- result = mPreview->setCaptureDisplay(capture_window);
- }
- RETURN(result, int);
-}
-
-//======================================================================
-// カメラのサポートしているコントロール機能を取得する
-int UVCCamera::getCtrlSupports(uint64_t *supports) {
- ENTER();
- uvc_error_t ret = UVC_ERROR_NOT_FOUND;
- if (LIKELY(mDeviceHandle)) {
- if (!mCtrlSupports) {
- // 何個あるのかわからへんねんけど、試した感じは1個みたいやからとりあえず先頭のを返す
- const uvc_input_terminal_t *input_terminals = uvc_get_input_terminals(mDeviceHandle);
- const uvc_input_terminal_t *it;
- DL_FOREACH(input_terminals, it)
- {
- if (it) {
- mCtrlSupports = it->bmControls;
- MARK("getCtrlSupports=%lx", (unsigned long)mCtrlSupports);
- ret = UVC_SUCCESS;
- break;
- }
- }
- } else
- ret = UVC_SUCCESS;
- }
- if (supports)
- *supports = mCtrlSupports;
- RETURN(ret, int);
-}
-
-int UVCCamera::getProcSupports(uint64_t *supports) {
- ENTER();
- uvc_error_t ret = UVC_ERROR_NOT_FOUND;
- if (LIKELY(mDeviceHandle)) {
- if (!mPUSupports) {
- // 何個あるのかわからへんねんけど、試した感じは1個みたいやからとりあえず先頭のを返す
- const uvc_processing_unit_t *proc_units = uvc_get_processing_units(mDeviceHandle);
- const uvc_processing_unit_t *pu;
- DL_FOREACH(proc_units, pu)
- {
- if (pu) {
- mPUSupports = pu->bmControls;
- MARK("getProcSupports=%lx", (unsigned long)mPUSupports);
- ret = UVC_SUCCESS;
- break;
- }
- }
- } else
- ret = UVC_SUCCESS;
- }
- if (supports)
- *supports = mPUSupports;
- RETURN(ret, int);
-}
-
-//======================================================================
-#define CTRL_BRIGHTNESS 0
-#define CTRL_CONTRAST 1
-#define CTRL_SHARPNESS 2
-#define CTRL_GAIN 3
-#define CTRL_WHITEBLANCE 4
-#define CTRL_FOCUS 5
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_i16 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- int16_t value;
- ret = get_func(devh, &value, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = value;
- LOGV("update_params:min value=%d,min=%d", value, values.min);
- ret = get_func(devh, &value, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = value;
- LOGV("update_params:max value=%d,max=%d", value, values.max);
- ret = get_func(devh, &value, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = value;
- LOGV("update_params:def value=%d,def=%d", value, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_u16 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- uint16_t value;
- ret = get_func(devh, &value, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = value;
- LOGV("update_params:min value=%d,min=%d", value, values.min);
- ret = get_func(devh, &value, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = value;
- LOGV("update_params:max value=%d,max=%d", value, values.max);
- ret = get_func(devh, &value, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = value;
- LOGV("update_params:def value=%d,def=%d", value, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_i8 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- int8_t value;
- ret = get_func(devh, &value, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = value;
- LOGV("update_params:min value=%d,min=%d", value, values.min);
- ret = get_func(devh, &value, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = value;
- LOGV("update_params:max value=%d,max=%d", value, values.max);
- ret = get_func(devh, &value, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = value;
- LOGV("update_params:def value=%d,def=%d", value, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_u8 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- uint8_t value;
- ret = get_func(devh, &value, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = value;
- LOGV("update_params:min value=%d,min=%d", value, values.min);
- ret = get_func(devh, &value, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = value;
- LOGV("update_params:max value=%d,max=%d", value, values.max);
- ret = get_func(devh, &value, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = value;
- LOGV("update_params:def value=%d,def=%d", value, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_u8u8 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- uint8_t value1, value2;
- ret = get_func(devh, &value1, &value2, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = (value1 << 8) + value2;
- LOGV("update_params:min value1=%d,value2=%d,min=%d", value1, value2, values.min);
- ret = get_func(devh, &value1, &value2, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = (value1 << 8) + value2;
- LOGV("update_params:max value1=%d,value2=%d,max=%d", value1, value2, values.max);
- ret = get_func(devh, &value1, &value2, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = (value1 << 8) + value2;
- LOGV("update_params:def value1=%d,value2=%ddef=%d", value1, value2, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_i8u8 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- int8_t value1;
- uint8_t value2;
- ret = get_func(devh, &value1, &value2, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = (value1 << 8) + value2;
- LOGV("update_params:min value1=%d,value2=%d,min=%d", value1, value2, values.min);
- ret = get_func(devh, &value1, &value2, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = (value1 << 8) + value2;
- LOGV("update_params:max value1=%d,value2=%d,max=%d", value1, value2, values.max);
- ret = get_func(devh, &value1, &value2, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = (value1 << 8) + value2;
- LOGV("update_params:def value1=%d,value2=%ddef=%d", value1, value2, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_i8u8u8 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- int8_t value1;
- uint8_t value2;
- uint8_t value3;
- ret = get_func(devh, &value1, &value2, &value3, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = (value1 << 16) + (value2 <<8) +value3;
- LOGV("update_params:min value1=%d,value2=%d,value3=%d,min=%d", value1, value2, value3, values.min);
- ret = get_func(devh, &value1, &value2, &value3, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = (value1 << 16) + (value2 <<8) +value3;
- LOGV("update_params:max value1=%d,value2=%d,value3=%d,max=%d", value1, value2, value3, values.max);
- ret = get_func(devh, &value1, &value2, &value3, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = (value1 << 16) + (value2 <<8) +value3;
- LOGV("update_params:def value1=%d,value2=%d,value3=%d,def=%d", value1, value2, value3, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_i32 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- int32_t value;
- ret = get_func(devh, &value, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = value;
- LOGV("update_params:min value=%d,min=%d", value, values.min);
- ret = get_func(devh, &value, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = value;
- LOGV("update_params:max value=%d,max=%d", value, values.max);
- ret = get_func(devh, &value, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = value;
- LOGV("update_params:def value=%d,def=%d", value, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values,
- paramget_func_u32 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if (!values.min && !values.max) {
- uint32_t value;
- ret = get_func(devh, &value, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values.min = value;
- LOGV("update_params:min value=%d,min=%d", value, values.min);
- ret = get_func(devh, &value, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values.max = value;
- LOGV("update_params:max value=%d,max=%d", value, values.max);
- ret = get_func(devh, &value, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values.def = value;
- LOGV("update_params:def value=%d,def=%d", value, values.def);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-static uvc_error_t update_ctrl_values(uvc_device_handle_t *devh, control_value_t &values1, control_value_t &values2,
- paramget_func_i32i32 get_func) {
-
- ENTER();
-
- uvc_error_t ret = UVC_SUCCESS;
- if ((!values1.min && !values1.max) ||(!values2.min && !values2.max)) {
- int32_t value1, value2;
- ret = get_func(devh, &value1, &value2, UVC_GET_MIN);
- if (LIKELY(!ret)) {
- values1.min = value1;
- values2.min = value2;
- LOGV("update_params:min value1=%d,value2=%d", value1, value2);
- ret = get_func(devh, &value1, &value2, UVC_GET_MAX);
- if (LIKELY(!ret)) {
- values1.max = value1;
- values2.max = value2;
- LOGV("update_params:max value1=%d,value2=%d", value1, value2);
- ret = get_func(devh, &value1, &value2, UVC_GET_DEF);
- if (LIKELY(!ret)) {
- values1.def = value1;
- values2.def = value2;
- LOGV("update_params:def value1=%d,value2=%d", value1, value2);
- }
- }
- }
- }
- if (UNLIKELY(ret)) {
- LOGD("update_params failed:err=%d", ret);
- }
- RETURN(ret, uvc_error_t);
-}
-
-#define UPDATE_CTRL_VALUES(VAL,FUNC) \
- ret = update_ctrl_values(mDeviceHandle, VAL, FUNC); \
- if (LIKELY(!ret)) { \
- min = VAL.min; \
- max = VAL.max; \
- def = VAL.def; \
- } else { \
- MARK("failed to UPDATE_CTRL_VALUES"); \
- } \
-
-/**
- * カメラコントロール設定の下請け
- */
-int UVCCamera::internalSetCtrlValue(control_value_t &values, int8_t value,
- paramget_func_i8 get_func, paramset_func_i8 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- value = value < values.min
- ? values.min
- : (value > values.max ? values.max : value);
- set_func(mDeviceHandle, value);
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::internalSetCtrlValue(control_value_t &values, uint8_t value,
- paramget_func_u8 get_func, paramset_func_u8 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- value = value < values.min
- ? values.min
- : (value > values.max ? values.max : value);
- set_func(mDeviceHandle, value);
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::internalSetCtrlValue(control_value_t &values, uint8_t value1, uint8_t value2,
- paramget_func_u8u8 get_func, paramset_func_u8u8 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint8_t v1min = (uint8_t)((values.min >> 8) & 0xff);
- uint8_t v2min = (uint8_t)(values.min & 0xff);
- uint8_t v1max = (uint8_t)((values.max >> 8) & 0xff);
- uint8_t v2max = (uint8_t)(values.max & 0xff);
- value1 = value1 < v1min
- ? v1min
- : (value1 > v1max ? v1max : value1);
- value2 = value2 < v2min
- ? v2min
- : (value2 > v2max ? v2max : value2);
- set_func(mDeviceHandle, value1, value2);
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::internalSetCtrlValue(control_value_t &values, int8_t value1, uint8_t value2,
- paramget_func_i8u8 get_func, paramset_func_i8u8 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int8_t v1min = (int8_t)((values.min >> 8) & 0xff);
- uint8_t v2min = (uint8_t)(values.min & 0xff);
- int8_t v1max = (int8_t)((values.max >> 8) & 0xff);
- uint8_t v2max = (uint8_t)(values.max & 0xff);
- value1 = value1 < v1min
- ? v1min
- : (value1 > v1max ? v1max : value1);
- value2 = value2 < v2min
- ? v2min
- : (value2 > v2max ? v2max : value2);
- set_func(mDeviceHandle, value1, value2);
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::internalSetCtrlValue(control_value_t &values, int8_t value1, uint8_t value2, uint8_t value3,
- paramget_func_i8u8u8 get_func, paramset_func_i8u8u8 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int8_t v1min = (int8_t)((values.min >> 16) & 0xff);
- uint8_t v2min = (uint8_t)((values.min >> 8) & 0xff);
- uint8_t v3min = (uint8_t)(values.min & 0xff);
- int8_t v1max = (int8_t)((values.max >> 16) & 0xff);
- uint8_t v2max = (uint8_t)((values.max >> 8) & 0xff);
- uint8_t v3max = (uint8_t)(values.max & 0xff);
- value1 = value1 < v1min
- ? v1min
- : (value1 > v1max ? v1max : value1);
- value2 = value2 < v2min
- ? v2min
- : (value2 > v2max ? v2max : value2);
- value3 = value3 < v3min
- ? v3min
- : (value3 > v3max ? v3max : value3);
- set_func(mDeviceHandle, value1, value2, value3);
- }
- RETURN(ret, int);
-}
-
-/**
- * カメラコントロール設定の下請け
- */
-int UVCCamera::internalSetCtrlValue(control_value_t &values, int16_t value,
- paramget_func_i16 get_func, paramset_func_i16 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- value = value < values.min
- ? values.min
- : (value > values.max ? values.max : value);
- set_func(mDeviceHandle, value);
- }
- RETURN(ret, int);
-}
-
-/**
- * カメラコントロール設定の下請け
- */
-int UVCCamera::internalSetCtrlValue(control_value_t &values, uint16_t value,
- paramget_func_u16 get_func, paramset_func_u16 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- value = value < values.min
- ? values.min
- : (value > values.max ? values.max : value);
- set_func(mDeviceHandle, value);
- }
- RETURN(ret, int);
-}
-
-/**
- * カメラコントロール設定の下請け
- */
-int UVCCamera::internalSetCtrlValue(control_value_t &values, int32_t value,
- paramget_func_i32 get_func, paramset_func_i32 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- value = value < values.min
- ? values.min
- : (value > values.max ? values.max : value);
- set_func(mDeviceHandle, value);
- }
- RETURN(ret, int);
-}
-
-/**
- * カメラコントロール設定の下請け
- */
-int UVCCamera::internalSetCtrlValue(control_value_t &values, uint32_t value,
- paramget_func_u32 get_func, paramset_func_u32 set_func) {
- int ret = update_ctrl_values(mDeviceHandle, values, get_func);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- value = value < values.min
- ? values.min
- : (value > values.max ? values.max : value);
- set_func(mDeviceHandle, value);
- }
- RETURN(ret, int);
-}
-
-//======================================================================
-// スキャニングモード
-int UVCCamera::updateScanningModeLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & CTRL_SCANNING) {
- UPDATE_CTRL_VALUES(mScanningMode, uvc_get_scanning_mode);
- }
- RETURN(ret, int);
-}
-
-// スキャニングモードをセット
-int UVCCamera::setScanningMode(int mode) {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_SCANNING)) {
-// LOGI("ae:%d", mode);
- r = uvc_set_scanning_mode(mDeviceHandle, mode/* & 0xff*/);
- }
- RETURN(r, int);
-}
-
-// スキャニングモード設定を取得
-int UVCCamera::getScanningMode() {
-
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_SCANNING)) {
- uint8_t mode;
- r = uvc_get_scanning_mode(mDeviceHandle, &mode, UVC_GET_CUR);
-// LOGI("ae:%d", mode);
- if (LIKELY(!r)) {
- r = mode;
- }
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// 露出モード
-int UVCCamera::updateExposureModeLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & CTRL_AE) {
- UPDATE_CTRL_VALUES(mExposureMode, uvc_get_ae_mode);
- }
- RETURN(ret, int);
-}
-
-// 露出をセット
-int UVCCamera::setExposureMode(int mode) {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE)) {
-// LOGI("ae:%d", mode);
- r = uvc_set_ae_mode(mDeviceHandle, mode/* & 0xff*/);
- }
- RETURN(r, int);
-}
-
-// 露出設定を取得
-int UVCCamera::getExposureMode() {
-
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE)) {
- uint8_t mode;
- r = uvc_get_ae_mode(mDeviceHandle, &mode, UVC_GET_CUR);
-// LOGI("ae:%d", mode);
- if (LIKELY(!r)) {
- r = mode;
- }
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// 露出優先設定
-int UVCCamera::updateExposurePriorityLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & CTRL_AE_PRIORITY) {
- UPDATE_CTRL_VALUES(mExposurePriority, uvc_get_ae_priority);
- }
- RETURN(ret, int);
-}
-
-// 露出優先設定をセット
-int UVCCamera::setExposurePriority(int priority) {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE_PRIORITY)) {
-// LOGI("ae priority:%d", priority);
- r = uvc_set_ae_priority(mDeviceHandle, priority/* & 0xff*/);
- }
- RETURN(r, int);
-}
-
-// 露出優先設定を取得
-int UVCCamera::getExposurePriority() {
-
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE_PRIORITY)) {
- uint8_t priority;
- r = uvc_get_ae_priority(mDeviceHandle, &priority, UVC_GET_CUR);
-// LOGI("ae priority:%d", priority);
- if (LIKELY(!r)) {
- r = priority;
- }
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// 露出(絶対値)設定
-int UVCCamera::updateExposureLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & CTRL_AE_ABS) {
- UPDATE_CTRL_VALUES(mExposureAbs, uvc_get_exposure_abs);
- }
- RETURN(ret, int);
-}
-
-// 露出(絶対値)設定をセット
-int UVCCamera::setExposure(int ae_abs) {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE_ABS)) {
-// LOGI("ae_abs:%d", ae_abs);
- r = uvc_set_exposure_abs(mDeviceHandle, ae_abs/* & 0xff*/);
- }
- RETURN(r, int);
-}
-
-// 露出(絶対値)設定を取得
-int UVCCamera::getExposure() {
-
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE_ABS)) {
- int ae_abs;
- r = uvc_get_exposure_abs(mDeviceHandle, &ae_abs, UVC_GET_CUR);
-// LOGI("ae_abs:%d", ae_abs);
- if (LIKELY(!r)) {
- r = ae_abs;
- }
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// 露出(相対値)設定
-int UVCCamera::updateExposureRelLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & CTRL_AE_REL) {
- UPDATE_CTRL_VALUES(mExposureAbs, uvc_get_exposure_rel);
- }
- RETURN(ret, int);
-}
-
-// 露出(相対値)設定をセット
-int UVCCamera::setExposureRel(int ae_rel) {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE_REL)) {
-// LOGI("ae_rel:%d", ae_rel);
- r = uvc_set_exposure_rel(mDeviceHandle, ae_rel/* & 0xff*/);
- }
- RETURN(r, int);
-}
-
-// 露出(相対値)設定を取得
-int UVCCamera::getExposureRel() {
-
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_AE_REL)) {
- int ae_rel;
- r = uvc_get_exposure_rel(mDeviceHandle, &ae_rel, UVC_GET_CUR);
-// LOGI("ae_rel:%d", ae_rel);
- if (LIKELY(!r)) {
- r = ae_rel;
- }
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// オートフォーカス
-int UVCCamera::updateAutoFocusLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & CTRL_FOCUS_AUTO) {
- UPDATE_CTRL_VALUES(mAutoFocus, uvc_get_focus_auto);
- }
- RETURN(ret, int);
-}
-
-// オートフォーカスをon/off
-int UVCCamera::setAutoFocus(bool autoFocus) {
- ENTER();
-
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_FOCUS_AUTO)) {
- r = uvc_set_focus_auto(mDeviceHandle, autoFocus);
- }
- RETURN(r, int);
-}
-
-// オートフォーカスのon/off状態を取得
-bool UVCCamera::getAutoFocus() {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mCtrlSupports & CTRL_FOCUS_AUTO)) {
- uint8_t autoFocus;
- r = uvc_get_focus_auto(mDeviceHandle, &autoFocus, UVC_GET_CUR);
- if (LIKELY(!r))
- r = autoFocus;
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// フォーカス(絶対値)調整
-int UVCCamera::updateFocusLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_FOCUS_ABS) {
- UPDATE_CTRL_VALUES(mFocus, uvc_get_focus_abs);
- }
- RETURN(ret, int);
-}
-
-// フォーカス(絶対値)を設定
-int UVCCamera::setFocus(int focus) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_FOCUS_ABS) {
- ret = internalSetCtrlValue(mFocus, focus, uvc_get_focus_abs, uvc_set_focus_abs);
- }
- RETURN(ret, int);
-}
-
-// フォーカス(絶対値)の現在値を取得
-int UVCCamera::getFocus() {
- ENTER();
- if (mCtrlSupports & CTRL_FOCUS_ABS) {
- int ret = update_ctrl_values(mDeviceHandle, mFocus, uvc_get_focus_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int16_t value;
- ret = uvc_get_focus_abs(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// フォーカス(相対値)調整
-int UVCCamera::updateFocusRelLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_FOCUS_REL) {
- UPDATE_CTRL_VALUES(mFocusRel, uvc_get_focus_rel);
- }
- RETURN(ret, int);
-}
-
-// フォーカス(相対値)を設定
-int UVCCamera::setFocusRel(int focus_rel) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_FOCUS_REL) {
- ret = internalSetCtrlValue(mFocusRel, (int8_t)((focus_rel >> 8) & 0xff), (uint8_t)(focus_rel &0xff), uvc_get_focus_rel, uvc_set_focus_rel);
- }
- RETURN(ret, int);
-}
-
-// フォーカス(相対値)の現在値を取得
-int UVCCamera::getFocusRel() {
- ENTER();
- if (mCtrlSupports & CTRL_FOCUS_REL) {
- int ret = update_ctrl_values(mDeviceHandle, mFocusRel, uvc_get_focus_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int8_t focus;
- uint8_t speed;
- ret = uvc_get_focus_rel(mDeviceHandle, &focus, &speed, UVC_GET_CUR);
- if (LIKELY(!ret))
- return (focus <<8) + speed;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-/*
-// フォーカス(シンプル)調整
-int UVCCamera::updateFocusSimpleLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_FOCUS_SIMPLE) {
- UPDATE_CTRL_VALUES(mFocusSimple, uvc_get_focus_simple_range);
- }
- RETURN(ret, int);
-}
-
-// フォーカス(シンプル)を設定
-int UVCCamera::setFocusSimple(int focus) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_FOCUS_SIMPLE) {
- ret = internalSetCtrlValue(mFocusSimple, focus, uvc_get_focus_simple_range, uvc_set_focus_simple_range);
- }
- RETURN(ret, int);
-}
-
-// フォーカス(シンプル)の現在値を取得
-int UVCCamera::getFocusSimple() {
- ENTER();
- if (mCtrlSupports & CTRL_FOCUS_SIMPLE) {
- int ret = update_ctrl_values(mDeviceHandle, mFocusSimple, uvc_get_focus_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint8_t value;
- ret = uvc_get_focus_simple_range(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-*/
-
-//======================================================================
-// 絞り(絶対値)調整
-int UVCCamera::updateIrisLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_IRIS_ABS) {
- UPDATE_CTRL_VALUES(mIris, uvc_get_iris_abs);
- }
- RETURN(ret, int);
-}
-
-// 絞り(絶対値)を設定
-int UVCCamera::setIris(int iris) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_IRIS_ABS) {
- ret = internalSetCtrlValue(mIris, iris, uvc_get_iris_abs, uvc_set_iris_abs);
- }
- RETURN(ret, int);
-}
-
-// 絞り(絶対値)の現在値を取得
-int UVCCamera::getIris() {
- ENTER();
- if (mCtrlSupports & CTRL_IRIS_ABS) {
- int ret = update_ctrl_values(mDeviceHandle, mIris, uvc_get_iris_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_iris_abs(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// 絞り(相対値)調整
-int UVCCamera::updateIrisRelLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_IRIS_REL) {
- UPDATE_CTRL_VALUES(mIris, uvc_get_iris_rel);
- }
- RETURN(ret, int);
-}
-
-// 絞り(相対値)を設定
-int UVCCamera::setIrisRel(int iris_rel) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_IRIS_REL) {
- ret = internalSetCtrlValue(mIris, iris_rel, uvc_get_iris_rel, uvc_set_iris_rel);
- }
- RETURN(ret, int);
-}
-
-// 絞り(相対値)の現在値を取得
-int UVCCamera::getIrisRel() {
- ENTER();
- if (mCtrlSupports & CTRL_IRIS_REL) {
- int ret = update_ctrl_values(mDeviceHandle, mIris, uvc_get_iris_rel);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint8_t iris_rel;
- ret = uvc_get_iris_rel(mDeviceHandle, &iris_rel, UVC_GET_CUR);
- if (LIKELY(!ret))
- return iris_rel;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// Pan(絶対値)調整
-int UVCCamera::updatePanLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_PANTILT_ABS) {
- update_ctrl_values(mDeviceHandle, mPan, mTilt, uvc_get_pantilt_abs);
- }
- RETURN(ret, int);
-}
-
-// Pan(絶対値)を設定
-int UVCCamera::setPan(int pan) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_PANTILT_ABS) {
- ret = update_ctrl_values(mDeviceHandle, mPan, mTilt, uvc_get_pantilt_abs);
- if (LIKELY(!ret)) {
- pan = pan < mPan.min
- ? mPan.min
- : (pan > mPan.max ? mPan.max : pan);
- int tilt = mTilt.current < 0 ? mTilt.def : mTilt.current;
- ret = uvc_set_pantilt_abs(mDeviceHandle, pan, tilt);
- if (LIKELY(!ret)) {
- mPan.current = pan;
- mTilt.current = tilt;
- }
- }
- }
- RETURN(ret, int);
-}
-
-// Pan(絶対値)の現在値を取得
-int UVCCamera::getPan() {
- ENTER();
- if (mCtrlSupports & CTRL_PANTILT_ABS) {
- int ret = update_ctrl_values(mDeviceHandle, mPan, mTilt, uvc_get_pantilt_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int32_t pan, tilt;
- ret = uvc_get_pantilt_abs(mDeviceHandle, &pan, &tilt, UVC_GET_CUR);
- if (LIKELY(!ret)) {
- mPan.current = pan;
- mTilt.current = tilt;
- return pan;
- }
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// Tilt(絶対値)調整
-int UVCCamera::updateTiltLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_PANTILT_ABS) {
- update_ctrl_values(mDeviceHandle, mPan, mTilt, uvc_get_pantilt_abs);
- }
- RETURN(ret, int);
-}
-
-// Tilt(絶対値)を設定
-int UVCCamera::setTilt(int tilt) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_PANTILT_ABS) {
- ret = update_ctrl_values(mDeviceHandle, mPan, mTilt, uvc_get_pantilt_abs);
- if (LIKELY(!ret)) {
- tilt = tilt < mTilt.min
- ? mTilt.min
- : (tilt > mTilt.max ? mTilt.max : tilt);
- int pan = mPan.current < 0 ? mPan.def : mPan.current;
- ret = uvc_set_pantilt_abs(mDeviceHandle, pan, tilt);
- if (LIKELY(!ret)) {
- mPan.current = pan;
- mTilt.current = tilt;
- }
- }
- }
- RETURN(ret, int);
-}
-
-// Tilt(絶対値)の現在値を取得
-int UVCCamera::getTilt() {
- ENTER();
- if (mCtrlSupports & CTRL_PANTILT_ABS) {
- int ret = update_ctrl_values(mDeviceHandle, mPan, mTilt, uvc_get_pantilt_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int32_t pan, tilt;
- ret = uvc_get_pantilt_abs(mDeviceHandle, &pan, &tilt, UVC_GET_CUR);
- if (LIKELY(!ret)) {
- mPan.current = pan;
- mTilt.current = tilt;
- return tilt;
- }
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// Roll(絶対値)調整
-int UVCCamera::updateRollLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_ROLL_ABS) {
- UPDATE_CTRL_VALUES(mRoll, uvc_get_roll_abs);
- }
- RETURN(ret, int);
-}
-
-// Roll(絶対値)を設定
-int UVCCamera::setRoll(int roll) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_ROLL_ABS) {
- ret = internalSetCtrlValue(mRoll, roll, uvc_get_roll_abs, uvc_set_roll_abs);
- }
- RETURN(ret, int);
-}
-
-// Roll(絶対値)の現在値を取得
-int UVCCamera::getRoll() {
- ENTER();
- if (mCtrlSupports & CTRL_ROLL_ABS) {
- int ret = update_ctrl_values(mDeviceHandle, mRoll, uvc_get_roll_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int16_t roll;
- ret = uvc_get_roll_abs(mDeviceHandle, &roll, UVC_GET_CUR);
- if (LIKELY(!ret)) {
- mRoll.current = roll;
- return roll;
- }
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-int UVCCamera::updatePanRelLimit(int &min, int &max, int &def) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-int UVCCamera::setPanRel(int pan_rel) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-int UVCCamera::getPanRel() {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-//======================================================================
-int UVCCamera::updateTiltRelLimit(int &min, int &max, int &def) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-int UVCCamera::setTiltRel(int tilt_rel) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-int UVCCamera::getTiltRel() {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-//======================================================================
-int UVCCamera::updateRollRelLimit(int &min, int &max, int &def) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-int UVCCamera::setRollRel(int roll_rel) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-int UVCCamera::getRollRel() {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-//======================================================================
-// プライバシーモード
-int UVCCamera::updatePrivacyLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_PRIVACY) {
- UPDATE_CTRL_VALUES(mPrivacy, uvc_get_focus_abs);
- }
- RETURN(ret, int);
-}
-
-// プライバシーモードを設定
-int UVCCamera::setPrivacy(int privacy) {
- ENTER();
- int ret = UVC_ERROR_ACCESS;
- if (mCtrlSupports & CTRL_PRIVACY) {
- ret = internalSetCtrlValue(mPrivacy, privacy, uvc_get_privacy, uvc_set_privacy);
- }
- RETURN(ret, int);
-}
-
-// プライバシーモードの現在値を取得
-int UVCCamera::getPrivacy() {
- ENTER();
- if (mCtrlSupports & CTRL_PRIVACY) {
- int ret = update_ctrl_values(mDeviceHandle, mPrivacy, uvc_get_privacy);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint8_t privacy;
- ret = uvc_get_privacy(mDeviceHandle, &privacy, UVC_GET_CUR);
- if (LIKELY(!ret))
- return privacy;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-/*
-// DigitalWindow
-int UVCCamera::updateDigitalWindowLimit(...not defined...) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-// DigitalWindowを設定
-int UVCCamera::setDigitalWindow(int top, int reft, int bottom, int right) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-// DigitalWindowの現在値を取得
-int UVCCamera::getDigitalWindow(int &top, int &reft, int &bottom, int &right) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-*/
-
-//======================================================================
-/*
-// DigitalRoi
-int UVCCamera::updateDigitalRoiLimit(...not defined...) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-// DigitalRoiを設定
-int UVCCamera::setDigitalRoi(int top, int reft, int bottom, int right) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-
-// DigitalRoiの現在値を取得
-int UVCCamera::getDigitalRoi(int &top, int &reft, int &bottom, int &right) {
- ENTER();
- // FIXME not implemented yet
- RETURN(UVC_ERROR_ACCESS, int);
-}
-*/
-
-//======================================================================
-// backlight_compensation
-int UVCCamera::updateBacklightCompLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_BACKLIGHT) {
- UPDATE_CTRL_VALUES(mBacklightComp, uvc_get_backlight_compensation);
- }
- RETURN(ret, int);
-}
-
-// backlight_compensationを設定
-int UVCCamera::setBacklightComp(int backlight) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_BACKLIGHT) {
- ret = internalSetCtrlValue(mBacklightComp, backlight, uvc_get_backlight_compensation, uvc_set_backlight_compensation);
- }
- RETURN(ret, int);
-}
-
-// backlight_compensationの現在値を取得
-int UVCCamera::getBacklightComp() {
- ENTER();
- if (mPUSupports & PU_BACKLIGHT) {
- int ret = update_ctrl_values(mDeviceHandle, mBacklightComp, uvc_get_backlight_compensation);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int16_t value;
- ret = uvc_get_backlight_compensation(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-
-//======================================================================
-// 明るさ
-int UVCCamera::updateBrightnessLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_BRIGHTNESS) {
- UPDATE_CTRL_VALUES(mBrightness, uvc_get_brightness);
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::setBrightness(int brightness) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_BRIGHTNESS) {
- ret = internalSetCtrlValue(mBrightness, brightness, uvc_get_brightness, uvc_set_brightness);
- }
- RETURN(ret, int);
-}
-
-// 明るさの現在値を取得
-int UVCCamera::getBrightness() {
- ENTER();
- if (mPUSupports & PU_BRIGHTNESS) {
- int ret = update_ctrl_values(mDeviceHandle, mBrightness, uvc_get_brightness);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int16_t value;
- ret = uvc_get_brightness(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// コントラスト調整
-int UVCCamera::updateContrastLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_CONTRAST) {
- UPDATE_CTRL_VALUES(mContrast, uvc_get_contrast);
- }
- RETURN(ret, int);
-}
-
-// コントラストを設定
-int UVCCamera::setContrast(uint16_t contrast) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_CONTRAST) {
- ret = internalSetCtrlValue(mContrast, contrast, uvc_get_contrast, uvc_set_contrast);
- }
- RETURN(ret, int);
-}
-
-// コントラストの現在値を取得
-int UVCCamera::getContrast() {
- ENTER();
- if (mPUSupports & PU_CONTRAST) {
- int ret = update_ctrl_values(mDeviceHandle, mContrast, uvc_get_contrast);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_contrast(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// オートコントラスト
-int UVCCamera::updateAutoContrastLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_CONTRAST_AUTO) {
- UPDATE_CTRL_VALUES(mAutoFocus, uvc_get_contrast_auto);
- }
- RETURN(ret, int);
-}
-
-// オートコントラストをon/off
-int UVCCamera::setAutoContrast(bool autoContrast) {
- ENTER();
-
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_CONTRAST_AUTO)) {
- r = uvc_set_contrast_auto(mDeviceHandle, autoContrast);
- }
- RETURN(r, int);
-}
-
-// オートコントラストのon/off状態を取得
-bool UVCCamera::getAutoContrast() {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_CONTRAST_AUTO)) {
- uint8_t autoContrast;
- r = uvc_get_contrast_auto(mDeviceHandle, &autoContrast, UVC_GET_CUR);
- if (LIKELY(!r))
- r = autoContrast;
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// シャープネス調整
-int UVCCamera::updateSharpnessLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_SHARPNESS) {
- UPDATE_CTRL_VALUES(mSharpness, uvc_get_sharpness);
- }
- RETURN(ret, int);
-}
-
-// シャープネスを設定
-int UVCCamera::setSharpness(int sharpness) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_SHARPNESS) {
- ret = internalSetCtrlValue(mSharpness, sharpness, uvc_get_sharpness, uvc_set_sharpness);
- }
- RETURN(ret, int);
-}
-
-// シャープネスの現在値を取得
-int UVCCamera::getSharpness() {
- ENTER();
- if (mPUSupports & PU_SHARPNESS) {
- int ret = update_ctrl_values(mDeviceHandle, mSharpness, uvc_get_sharpness);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_sharpness(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// ゲイン調整
-int UVCCamera::updateGainLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_GAIN) {
- UPDATE_CTRL_VALUES(mGain, uvc_get_gain)
- }
- RETURN(ret, int);
-}
-
-// ゲインを設定
-int UVCCamera::setGain(int gain) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_GAIN) {
-// LOGI("gain:%d", gain);
- ret = internalSetCtrlValue(mGain, gain, uvc_get_gain, uvc_set_gain);
- }
- RETURN(ret, int);
-}
-
-// ゲインの現在値を取得
-int UVCCamera::getGain() {
- ENTER();
- if (mPUSupports & PU_GAIN) {
- int ret = update_ctrl_values(mDeviceHandle, mGain, uvc_get_gain);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_gain(mDeviceHandle, &value, UVC_GET_CUR);
-// LOGI("gain:%d", value);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// オートホワイトバランス(temp)
-int UVCCamera::updateAutoWhiteBlanceLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_WB_TEMP_AUTO) {
- UPDATE_CTRL_VALUES(mAutoWhiteBlance, uvc_get_white_balance_temperature_auto);
- }
- RETURN(ret, int);
-}
-
-// オートホワイトバランス(temp)をon/off
-int UVCCamera::setAutoWhiteBlance(bool autoWhiteBlance) {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_WB_TEMP_AUTO)) {
- r = uvc_set_white_balance_temperature_auto(mDeviceHandle, autoWhiteBlance);
- }
- RETURN(r, int);
-}
-
-// オートホワイトバランス(temp)のon/off状態を取得
-bool UVCCamera::getAutoWhiteBlance() {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_WB_TEMP_AUTO)) {
- uint8_t autoWhiteBlance;
- r = uvc_get_white_balance_temperature_auto(mDeviceHandle, &autoWhiteBlance, UVC_GET_CUR);
- if (LIKELY(!r))
- r = autoWhiteBlance;
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// オートホワイトバランス(compo)
-int UVCCamera::updateAutoWhiteBlanceCompoLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_WB_COMPO_AUTO) {
- UPDATE_CTRL_VALUES(mAutoWhiteBlanceCompo, uvc_get_white_balance_component_auto);
- }
- RETURN(ret, int);
-}
-
-// オートホワイトバランス(compo)をon/off
-int UVCCamera::setAutoWhiteBlanceCompo(bool autoWhiteBlanceCompo) {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_WB_COMPO_AUTO)) {
- r = uvc_set_white_balance_component_auto(mDeviceHandle, autoWhiteBlanceCompo);
- }
- RETURN(r, int);
-}
-
-// オートホワイトバランス(compo)のon/off状態を取得
-bool UVCCamera::getAutoWhiteBlanceCompo() {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_WB_COMPO_AUTO)) {
- uint8_t autoWhiteBlanceCompo;
- r = uvc_get_white_balance_component_auto(mDeviceHandle, &autoWhiteBlanceCompo, UVC_GET_CUR);
- if (LIKELY(!r))
- r = autoWhiteBlanceCompo;
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// ホワイトバランス色温度調整
-int UVCCamera::updateWhiteBlanceLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_WB_TEMP) {
- UPDATE_CTRL_VALUES(mWhiteBlance, uvc_get_white_balance_temperature)
- }
- RETURN(ret, int);
-}
-
-// ホワイトバランス色温度を設定
-int UVCCamera::setWhiteBlance(int white_blance) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_WB_TEMP) {
- ret = internalSetCtrlValue(mWhiteBlance, white_blance,
- uvc_get_white_balance_temperature, uvc_set_white_balance_temperature);
- }
- RETURN(ret, int);
-}
-
-// ホワイトバランス色温度の現在値を取得
-int UVCCamera::getWhiteBlance() {
- ENTER();
- if (mPUSupports & PU_WB_TEMP) {
- int ret = update_ctrl_values(mDeviceHandle, mWhiteBlance, uvc_get_white_balance_temperature);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_white_balance_temperature(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// ホワイトバランスcompo調整
-int UVCCamera::updateWhiteBlanceCompoLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_WB_COMPO) {
- UPDATE_CTRL_VALUES(mWhiteBlanceCompo, uvc_get_white_balance_component)
- }
- RETURN(ret, int);
-}
-
-// ホワイトバランスcompoを設定
-int UVCCamera::setWhiteBlanceCompo(int white_blance_compo) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_WB_COMPO) {
- ret = internalSetCtrlValue(mWhiteBlanceCompo, white_blance_compo,
- uvc_get_white_balance_component, uvc_set_white_balance_component);
- }
- RETURN(ret, int);
-}
-
-// ホワイトバランスcompoの現在値を取得
-int UVCCamera::getWhiteBlanceCompo() {
- ENTER();
- if (mPUSupports & PU_WB_COMPO) {
- int ret = update_ctrl_values(mDeviceHandle, mWhiteBlanceCompo, uvc_get_white_balance_component);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint32_t white_blance_compo;
- ret = uvc_get_white_balance_component(mDeviceHandle, &white_blance_compo, UVC_GET_CUR);
- if (LIKELY(!ret))
- return white_blance_compo;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// ガンマ調整
-int UVCCamera::updateGammaLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_GAMMA) {
- UPDATE_CTRL_VALUES(mGamma, uvc_get_gamma)
- }
- RETURN(ret, int);
-}
-
-// ガンマを設定
-int UVCCamera::setGamma(int gamma) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_GAMMA) {
-// LOGI("gamma:%d", gamma);
- ret = internalSetCtrlValue(mGamma, gamma, uvc_get_gamma, uvc_set_gamma);
- }
- RETURN(ret, int);
-}
-
-// ガンマの現在値を取得
-int UVCCamera::getGamma() {
- ENTER();
- if (mPUSupports & PU_GAMMA) {
- int ret = update_ctrl_values(mDeviceHandle, mGamma, uvc_get_gamma);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_gamma(mDeviceHandle, &value, UVC_GET_CUR);
-// LOGI("gamma:%d", ret);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// 彩度調整
-int UVCCamera::updateSaturationLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_SATURATION) {
- UPDATE_CTRL_VALUES(mSaturation, uvc_get_saturation)
- }
- RETURN(ret, int);
-}
-
-// 彩度を設定
-int UVCCamera::setSaturation(int saturation) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_SATURATION) {
- ret = internalSetCtrlValue(mSaturation, saturation, uvc_get_saturation, uvc_set_saturation);
- }
- RETURN(ret, int);
-}
-
-// 彩度の現在値を取得
-int UVCCamera::getSaturation() {
- ENTER();
- if (mPUSupports & PU_SATURATION) {
- int ret = update_ctrl_values(mDeviceHandle, mSaturation, uvc_get_saturation);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_saturation(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// 色相調整
-int UVCCamera::updateHueLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_HUE) {
- UPDATE_CTRL_VALUES(mHue, uvc_get_hue)
- }
- RETURN(ret, int);
-}
-
-// 色相を設定
-int UVCCamera::setHue(int hue) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_HUE) {
- ret = internalSetCtrlValue(mHue, hue, uvc_get_hue, uvc_set_hue);
- }
- RETURN(ret, int);
-}
-
-// 色相の現在値を取得
-int UVCCamera::getHue() {
- ENTER();
- if (mPUSupports & PU_HUE) {
- int ret = update_ctrl_values(mDeviceHandle, mHue, uvc_get_hue);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int16_t value;
- ret = uvc_get_hue(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// オート色相
-int UVCCamera::updateAutoHueLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_HUE_AUTO) {
- UPDATE_CTRL_VALUES(mAutoHue, uvc_get_hue_auto);
- }
- RETURN(ret, int);
-}
-
-// オート色相をon/off
-int UVCCamera::setAutoHue(bool autoHue) {
- ENTER();
-
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_HUE_AUTO)) {
- r = uvc_set_hue_auto(mDeviceHandle, autoHue);
- }
- RETURN(r, int);
-}
-
-// オート色相のon/off状態を取得
-bool UVCCamera::getAutoHue() {
- ENTER();
- int r = UVC_ERROR_ACCESS;
- if LIKELY((mDeviceHandle) && (mPUSupports & PU_HUE_AUTO)) {
- uint8_t autoHue;
- r = uvc_get_hue_auto(mDeviceHandle, &autoHue, UVC_GET_CUR);
- if (LIKELY(!r))
- r = autoHue;
- }
- RETURN(r, int);
-}
-
-//======================================================================
-// 電源周波数によるチラつき補正
-int UVCCamera::updatePowerlineFrequencyLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mCtrlSupports & PU_POWER_LF) {
- UPDATE_CTRL_VALUES(mPowerlineFrequency, uvc_get_powerline_freqency)
- }
- RETURN(ret, int);
-}
-
-// 電源周波数によるチラつき補正を設定
-int UVCCamera::setPowerlineFrequency(int frequency) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_POWER_LF) {
- if (frequency < 0) {
- uint8_t value;
- ret = uvc_get_powerline_freqency(mDeviceHandle, &value, UVC_GET_DEF);
- if LIKELY(ret)
- frequency = value;
- else
- RETURN(ret, int);
- }
- LOGD("frequency:%d", frequency);
- ret = uvc_set_powerline_freqency(mDeviceHandle, frequency);
- }
-
- RETURN(ret, int);
-}
-
-// 電源周波数によるチラつき補正値を取得
-int UVCCamera::getPowerlineFrequency() {
- ENTER();
- if (mPUSupports & PU_POWER_LF) {
- uint8_t value;
- int ret = uvc_get_powerline_freqency(mDeviceHandle, &value, UVC_GET_CUR);
- LOGD("frequency:%d", ret);
- if (LIKELY(!ret))
- return value;
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// ズーム(abs)調整
-int UVCCamera::updateZoomLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mCtrlSupports & CTRL_ZOOM_ABS) {
- UPDATE_CTRL_VALUES(mZoom, uvc_get_zoom_abs)
- }
- RETURN(ret, int);
-}
-
-// ズーム(abs)を設定
-int UVCCamera::setZoom(int zoom) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mCtrlSupports & CTRL_ZOOM_ABS) {
- ret = internalSetCtrlValue(mZoom, zoom, uvc_get_zoom_abs, uvc_set_zoom_abs);
- }
- RETURN(ret, int);
-}
-
-// ズーム(abs)の現在値を取得
-int UVCCamera::getZoom() {
- ENTER();
- if (mCtrlSupports & CTRL_ZOOM_ABS) {
- int ret = update_ctrl_values(mDeviceHandle, mZoom, uvc_get_zoom_abs);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t value;
- ret = uvc_get_zoom_abs(mDeviceHandle, &value, UVC_GET_CUR);
- if (LIKELY(!ret))
- return value;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// ズーム(相対値)調整
-int UVCCamera::updateZoomRelLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mCtrlSupports & CTRL_ZOOM_REL) {
- UPDATE_CTRL_VALUES(mZoomRel, uvc_get_zoom_rel)
- }
- RETURN(ret, int);
-}
-
-// ズーム(相対値)を設定
-int UVCCamera::setZoomRel(int zoom) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mCtrlSupports & CTRL_ZOOM_REL) {
- ret = internalSetCtrlValue(mZoomRel,
- (int8_t)((zoom >> 16) & 0xff), (uint8_t)((zoom >> 8) & 0xff), (uint8_t)(zoom & 0xff),
- uvc_get_zoom_rel, uvc_set_zoom_rel);
- }
- RETURN(ret, int);
-}
-
-// ズーム(相対値)の現在値を取得
-int UVCCamera::getZoomRel() {
- ENTER();
- if (mCtrlSupports & CTRL_ZOOM_REL) {
- int ret = update_ctrl_values(mDeviceHandle, mZoomRel, uvc_get_zoom_rel);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- int8_t zoom;
- uint8_t isdigital;
- uint8_t speed;
- ret = uvc_get_zoom_rel(mDeviceHandle, &zoom, &isdigital, &speed, UVC_GET_CUR);
- if (LIKELY(!ret))
- return (zoom << 16) +(isdigital << 8) + speed;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// digital multiplier調整
-int UVCCamera::updateDigitalMultiplierLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_DIGITAL_MULT) {
- UPDATE_CTRL_VALUES(mMultiplier, uvc_get_digital_multiplier)
- }
- RETURN(ret, int);
-}
-
-// digital multiplierを設定
-int UVCCamera::setDigitalMultiplier(int multiplier) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_DIGITAL_MULT) {
-// LOGI("multiplier:%d", multiplier);
- ret = internalSetCtrlValue(mMultiplier, multiplier, uvc_get_digital_multiplier, uvc_set_digital_multiplier);
- }
- RETURN(ret, int);
-}
-
-// digital multiplierの現在値を取得
-int UVCCamera::getDigitalMultiplier() {
- ENTER();
- if (mPUSupports & PU_DIGITAL_MULT) {
- int ret = update_ctrl_values(mDeviceHandle, mMultiplier, uvc_get_digital_multiplier);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t multiplier;
- ret = uvc_get_digital_multiplier(mDeviceHandle, &multiplier, UVC_GET_CUR);
-// LOGI("multiplier:%d", multiplier);
- if (LIKELY(!ret))
- return multiplier;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// digital multiplier limit調整
-int UVCCamera::updateDigitalMultiplierLimitLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_DIGITAL_LIMIT) {
- UPDATE_CTRL_VALUES(mMultiplierLimit, uvc_get_digital_multiplier_limit)
- }
- RETURN(ret, int);
-}
-
-// digital multiplier limitを設定
-int UVCCamera::setDigitalMultiplierLimit(int multiplier_limit) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_DIGITAL_LIMIT) {
-// LOGI("multiplier limit:%d", multiplier_limit);
- ret = internalSetCtrlValue(mMultiplierLimit, multiplier_limit, uvc_get_digital_multiplier_limit, uvc_set_digital_multiplier_limit);
- }
- RETURN(ret, int);
-}
-
-// digital multiplier limitの現在値を取得
-int UVCCamera::getDigitalMultiplierLimit() {
- ENTER();
- if (mPUSupports & PU_DIGITAL_LIMIT) {
- int ret = update_ctrl_values(mDeviceHandle, mMultiplierLimit, uvc_get_digital_multiplier_limit);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint16_t multiplier_limit;
- ret = uvc_get_digital_multiplier_limit(mDeviceHandle, &multiplier_limit, UVC_GET_CUR);
-// LOGI("multiplier_limit:%d", multiplier_limit);
- if (LIKELY(!ret))
- return multiplier_limit;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// AnalogVideoStandard
-int UVCCamera::updateAnalogVideoStandardLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_AVIDEO_STD) {
- UPDATE_CTRL_VALUES(mAnalogVideoStandard, uvc_get_analog_video_standard)
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::setAnalogVideoStandard(int standard) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_AVIDEO_STD) {
-// LOGI("standard:%d", standard);
- ret = internalSetCtrlValue(mAnalogVideoStandard, standard, uvc_get_analog_video_standard, uvc_set_analog_video_standard);
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::getAnalogVideoStandard() {
- ENTER();
- if (mPUSupports & PU_AVIDEO_STD) {
- int ret = update_ctrl_values(mDeviceHandle, mAnalogVideoStandard, uvc_get_analog_video_standard);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint8_t standard;
- ret = uvc_get_analog_video_standard(mDeviceHandle, &standard, UVC_GET_CUR);
-// LOGI("standard:%d", standard);
- if (LIKELY(!ret))
- return standard;
- }
- }
- RETURN(0, int);
-}
-
-//======================================================================
-// AnalogVideoLoackStatus
-int UVCCamera::updateAnalogVideoLockStateLimit(int &min, int &max, int &def) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_AVIDEO_LOCK) {
- UPDATE_CTRL_VALUES(mAnalogVideoLockState, uvc_get_analog_video_lockstate)
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::setAnalogVideoLockState(int state) {
- ENTER();
- int ret = UVC_ERROR_IO;
- if (mPUSupports & PU_AVIDEO_LOCK) {
-// LOGI("status:%d", status);
- ret = internalSetCtrlValue(mAnalogVideoLockState, state, uvc_get_analog_video_lockstate, uvc_set_analog_video_lockstate);
- }
- RETURN(ret, int);
-}
-
-int UVCCamera::getAnalogVideoLockState() {
- ENTER();
- if (mPUSupports & PU_AVIDEO_LOCK) {
- int ret = update_ctrl_values(mDeviceHandle, mAnalogVideoLockState, uvc_get_analog_video_lockstate);
- if (LIKELY(!ret)) { // 正常に最小・最大値を取得出来た時
- uint8_t status;
- ret = uvc_get_analog_video_lockstate(mDeviceHandle, &status, UVC_GET_CUR);
-// LOGI("status:%d", status);
- if (LIKELY(!ret))
- return status;
- }
- }
- RETURN(0, int);
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCCamera.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCCamera.h
deleted file mode 100755
index 72a2009538..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCCamera.h
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: UVCCamera.h
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#pragma interface
-
-#ifndef UVCCAMERA_H_
-#define UVCCAMERA_H_
-
-#include
-#include
-#include
-#include
-#include "UVCStatusCallback.h"
-#include "UVCButtonCallback.h"
-#include "UVCPreview.h"
-
-#define CTRL_SCANNING 0x000001 // D0: Scanning Mode
-#define CTRL_AE 0x000002 // D1: Auto-Exposure Mode
-#define CTRL_AE_PRIORITY 0x000004 // D2: Auto-Exposure Priority
-#define CTRL_AE_ABS 0x000008 // D3: Exposure Time (Absolute)
-#define CTRL_AE_REL 0x000010 // D4: Exposure Time (Relative)
-#define CTRL_FOCUS_ABS 0x000020 // D5: Focus (Absolute)
-#define CTRL_FOCUS_REL 0x000040 // D6: Focus (Relative)
-#define CTRL_IRIS_ABS 0x000080 // D7: Iris (Absolute)
-#define CTRL_IRIS_REL 0x000100 // D8: Iris (Relative)
-#define CTRL_ZOOM_ABS 0x000200 // D9: Zoom (Absolute)
-#define CTRL_ZOOM_REL 0x000400 // D10: Zoom (Relative)
-#define CTRL_PANTILT_ABS 0x000800 // D11: PanTilt (Absolute)
-#define CTRL_PANTILT_REL 0x001000 // D12: PanTilt (Relative)
-#define CTRL_ROLL_ABS 0x002000 // D13: Roll (Absolute)
-#define CTRL_ROLL_REL 0x004000 // D14: Roll (Relative)
-//#define CTRL_D15 0x008000 // D15: Reserved
-//#define CTRL_D16 0x010000 // D16: Reserved
-#define CTRL_FOCUS_AUTO 0x020000 // D17: Focus, Auto
-#define CTRL_PRIVACY 0x040000 // D18: Privacy
-#define CTRL_FOCUS_SIMPLE 0x080000 // D19: Focus, Simple
-#define CTRL_WINDOW 0x100000 // D20: Window
-
-#define PU_BRIGHTNESS 0x000001 // D0: Brightness
-#define PU_CONTRAST 0x000002 // D1: Contrast
-#define PU_HUE 0x000004 // D2: Hue
-#define PU_SATURATION 0x000008 // D3: Saturation
-#define PU_SHARPNESS 0x000010 // D4: Sharpness
-#define PU_GAMMA 0x000020 // D5: Gamma
-#define PU_WB_TEMP 0x000040 // D6: White Balance Temperature
-#define PU_WB_COMPO 0x000080 // D7: White Balance Component
-#define PU_BACKLIGHT 0x000100 // D8: Backlight Compensation
-#define PU_GAIN 0x000200 // D9: Gain
-#define PU_POWER_LF 0x000400 // D10: Power Line Frequency
-#define PU_HUE_AUTO 0x000800 // D11: Hue, Auto
-#define PU_WB_TEMP_AUTO 0x001000 // D12: White Balance Temperature, Auto
-#define PU_WB_COMPO_AUTO 0x002000 // D13: White Balance Component, Auto
-#define PU_DIGITAL_MULT 0x004000 // D14: Digital Multiplier
-#define PU_DIGITAL_LIMIT 0x008000 // D15: Digital Multiplier Limit
-#define PU_AVIDEO_STD 0x010000 // D16: Analog Video Standard
-#define PU_AVIDEO_LOCK 0x020000 // D17: Analog Video Lock Status
-#define PU_CONTRAST_AUTO 0x040000 // D18: Contrast, Auto
-
-typedef struct control_value {
- int res; // unused
- int min;
- int max;
- int def;
- int current;
-} control_value_t;
-
-typedef uvc_error_t (*paramget_func_i8)(uvc_device_handle_t *devh, int8_t *value, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_i16)(uvc_device_handle_t *devh, int16_t *value, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_i32)(uvc_device_handle_t *devh, int32_t *value, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_u8)(uvc_device_handle_t *devh, uint8_t *value, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_u16)(uvc_device_handle_t *devh, uint16_t *value, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_u32)(uvc_device_handle_t *devh, uint32_t *value, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_u8u8)(uvc_device_handle_t *devh, uint8_t *value1, uint8_t *value2, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_i8u8)(uvc_device_handle_t *devh, int8_t *value1, uint8_t *value2, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_i8u8u8)(uvc_device_handle_t *devh, int8_t *value1, uint8_t *value2, uint8_t *value3, enum uvc_req_code req_code);
-typedef uvc_error_t (*paramget_func_i32i32)(uvc_device_handle_t *devh, int32_t *value1, int32_t *value2, enum uvc_req_code req_code);
-
-typedef uvc_error_t (*paramset_func_i8)(uvc_device_handle_t *devh, int8_t value);
-typedef uvc_error_t (*paramset_func_i16)(uvc_device_handle_t *devh, int16_t value);
-typedef uvc_error_t (*paramset_func_i32)(uvc_device_handle_t *devh, int32_t value);
-typedef uvc_error_t (*paramset_func_u8)(uvc_device_handle_t *devh, uint8_t value);
-typedef uvc_error_t (*paramset_func_u16)(uvc_device_handle_t *devh, uint16_t value);
-typedef uvc_error_t (*paramset_func_u32)(uvc_device_handle_t *devh, uint32_t value);
-typedef uvc_error_t (*paramset_func_u8u8)(uvc_device_handle_t *devh, uint8_t value1, uint8_t value2);
-typedef uvc_error_t (*paramset_func_i8u8)(uvc_device_handle_t *devh, int8_t value1, uint8_t value2);
-typedef uvc_error_t (*paramset_func_i8u8u8)(uvc_device_handle_t *devh, int8_t value1, uint8_t value2, uint8_t value3);
-typedef uvc_error_t (*paramset_func_i32i32)(uvc_device_handle_t *devh, int32_t value1, int32_t value2);
-
-class UVCCamera {
- char *mUsbFs;
- uvc_context_t *mContext;
- int mFd;
- uvc_device_t *mDevice;
- uvc_device_handle_t *mDeviceHandle;
- UVCStatusCallback *mStatusCallback;
- UVCButtonCallback *mButtonCallback;
- // プレビュー用
- UVCPreview *mPreview;
- uint64_t mCtrlSupports;
- uint64_t mPUSupports;
- control_value_t mScanningMode;
- control_value_t mExposureMode;
- control_value_t mExposurePriority;
- control_value_t mExposureAbs;
- control_value_t mAutoFocus;
- control_value_t mAutoWhiteBlance;
- control_value_t mAutoWhiteBlanceCompo;
- control_value_t mWhiteBlance;
- control_value_t mWhiteBlanceCompo;
- control_value_t mBacklightComp;
- control_value_t mBrightness;
- control_value_t mContrast;
- control_value_t mAutoContrast;
- control_value_t mSharpness;
- control_value_t mGain;
- control_value_t mGamma;
- control_value_t mSaturation;
- control_value_t mHue;
- control_value_t mAutoHue;
- control_value_t mZoom;
- control_value_t mZoomRel;
- control_value_t mFocus;
- control_value_t mFocusRel;
- control_value_t mFocusSimple;
- control_value_t mIris;
- control_value_t mIrisRel;
- control_value_t mPan;
- control_value_t mTilt;
- control_value_t mRoll;
- control_value_t mPanRel;
- control_value_t mTiltRel;
- control_value_t mRollRel;
- control_value_t mPrivacy;
- control_value_t mPowerlineFrequency;
- control_value_t mMultiplier;
- control_value_t mMultiplierLimit;
- control_value_t mAnalogVideoStandard;
- control_value_t mAnalogVideoLockState;
-
- void clearCameraParams();
- int internalSetCtrlValue(control_value_t &values, int8_t value,
- paramget_func_i8 get_func, paramset_func_i8 set_func);
- int internalSetCtrlValue(control_value_t &values, uint8_t value,
- paramget_func_u8 get_func, paramset_func_u8 set_func);
- int internalSetCtrlValue(control_value_t &values, uint8_t value1, uint8_t value2,
- paramget_func_u8u8 get_func, paramset_func_u8u8 set_func);
- int internalSetCtrlValue(control_value_t &values, int8_t value1, uint8_t value2,
- paramget_func_i8u8 get_func, paramset_func_i8u8 set_func);
- int internalSetCtrlValue(control_value_t &values, int8_t value1, uint8_t value2, uint8_t value3,
- paramget_func_i8u8u8 get_func, paramset_func_i8u8u8 set_func);
- int internalSetCtrlValue(control_value_t &values, int16_t value,
- paramget_func_i16 get_func, paramset_func_i16 set_func);
- int internalSetCtrlValue(control_value_t &values, uint16_t value,
- paramget_func_u16 get_func, paramset_func_u16 set_func);
- int internalSetCtrlValue(control_value_t &values, int32_t value,
- paramget_func_i32 get_func, paramset_func_i32 set_func);
- int internalSetCtrlValue(control_value_t &values, uint32_t value,
- paramget_func_u32 get_func, paramset_func_u32 set_func);
-public:
- UVCCamera();
- ~UVCCamera();
-
- int connect(int vid, int pid, int fd, int busnum, int devaddr, const char *usbfs);
- int release();
-
- int setStatusCallback(JNIEnv *env, jobject status_callback_obj);
- int setButtonCallback(JNIEnv *env, jobject button_callback_obj);
-
- char *getSupportedSize();
- int setPreviewSize(int width, int height, int min_fps, int max_fps, int mode, float bandwidth = DEFAULT_BANDWIDTH);
- int setPreviewDisplay(ANativeWindow *preview_window);
- int setFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format);
- int setPreviewFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format); // MODIFIED
- int startPreview();
- int stopPreview();
- int setCaptureDisplay(ANativeWindow *capture_window);
-
- int getCtrlSupports(uint64_t *supports);
- int getProcSupports(uint64_t *supports);
-
- int updateScanningModeLimit(int &min, int &max, int &def);
- int setScanningMode(int mode);
- int getScanningMode();
-
- int updateExposureModeLimit(int &min, int &max, int &def);
- int setExposureMode(int mode);
- int getExposureMode();
-
- int updateExposurePriorityLimit(int &min, int &max, int &def);
- int setExposurePriority(int priority);
- int getExposurePriority();
-
- int updateExposureLimit(int &min, int &max, int &def);
- int setExposure(int ae_abs);
- int getExposure();
-
- int updateExposureRelLimit(int &min, int &max, int &def);
- int setExposureRel(int ae_rel);
- int getExposureRel();
-
- int updateAutoFocusLimit(int &min, int &max, int &def);
- int setAutoFocus(bool autoFocus);
- bool getAutoFocus();
-
- int updateFocusLimit(int &min, int &max, int &def);
- int setFocus(int focus);
- int getFocus();
-
- int updateFocusRelLimit(int &min, int &max, int &def);
- int setFocusRel(int focus);
- int getFocusRel();
-
-/* int updateFocusSimpleLimit(int &min, int &max, int &def);
- int setFocusSimple(int focus);
- int getFocusSimple(); */
-
- int updateIrisLimit(int &min, int &max, int &def);
- int setIris(int iris);
- int getIris();
-
- int updateIrisRelLimit(int &min, int &max, int &def);
- int setIrisRel(int iris);
- int getIrisRel();
-
- int updatePanLimit(int &min, int &max, int &def);
- int setPan(int pan);
- int getPan();
-
- int updateTiltLimit(int &min, int &max, int &def);
- int setTilt(int tilt);
- int getTilt();
-
- int updateRollLimit(int &min, int &max, int &def);
- int setRoll(int roll);
- int getRoll();
-
- int updatePanRelLimit(int &min, int &max, int &def);
- int setPanRel(int pan_rel);
- int getPanRel();
-
- int updateTiltRelLimit(int &min, int &max, int &def);
- int setTiltRel(int tilt_rel);
- int getTiltRel();
-
- int updateRollRelLimit(int &min, int &max, int &def);
- int setRollRel(int roll_rel);
- int getRollRel();
-
- int updatePrivacyLimit(int &min, int &max, int &def);
- int setPrivacy(int privacy);
- int getPrivacy();
-
- int updateAutoWhiteBlanceLimit(int &min, int &max, int &def);
- int setAutoWhiteBlance(bool autoWhiteBlance);
- bool getAutoWhiteBlance();
-
- int updateAutoWhiteBlanceCompoLimit(int &min, int &max, int &def);
- int setAutoWhiteBlanceCompo(bool autoWhiteBlanceCompo);
- bool getAutoWhiteBlanceCompo();
-
- int updateWhiteBlanceLimit(int &min, int &max, int &def);
- int setWhiteBlance(int temp);
- int getWhiteBlance();
-
- int updateWhiteBlanceCompoLimit(int &min, int &max, int &def);
- int setWhiteBlanceCompo(int white_blance_compo);
- int getWhiteBlanceCompo();
-
- int updateBacklightCompLimit(int &min, int &max, int &def);
- int setBacklightComp(int backlight);
- int getBacklightComp();
-
- int updateBrightnessLimit(int &min, int &max, int &def);
- int setBrightness(int brightness);
- int getBrightness();
-
- int updateContrastLimit(int &min, int &max, int &def);
- int setContrast(uint16_t contrast);
- int getContrast();
-
- int updateAutoContrastLimit(int &min, int &max, int &def);
- int setAutoContrast(bool autoFocus);
- bool getAutoContrast();
-
- int updateSharpnessLimit(int &min, int &max, int &def);
- int setSharpness(int sharpness);
- int getSharpness();
-
- int updateGainLimit(int &min, int &max, int &def);
- int setGain(int gain);
- int getGain();
-
- int updateGammaLimit(int &min, int &max, int &def);
- int setGamma(int gamma);
- int getGamma();
-
- int updateSaturationLimit(int &min, int &max, int &def);
- int setSaturation(int saturation);
- int getSaturation();
-
- int updateHueLimit(int &min, int &max, int &def);
- int setHue(int hue);
- int getHue();
-
- int updateAutoHueLimit(int &min, int &max, int &def);
- int setAutoHue(bool autoFocus);
- bool getAutoHue();
-
- int updatePowerlineFrequencyLimit(int &min, int &max, int &def);
- int setPowerlineFrequency(int frequency);
- int getPowerlineFrequency();
-
- int updateZoomLimit(int &min, int &max, int &def);
- int setZoom(int zoom);
- int getZoom();
-
- int updateZoomRelLimit(int &min, int &max, int &def);
- int setZoomRel(int zoom);
- int getZoomRel();
-
- int updateDigitalMultiplierLimit(int &min, int &max, int &def);
- int setDigitalMultiplier(int multiplier);
- int getDigitalMultiplier();
-
- int updateDigitalMultiplierLimitLimit(int &min, int &max, int &def);
- int setDigitalMultiplierLimit(int multiplier_limit);
- int getDigitalMultiplierLimit();
-
- int updateAnalogVideoStandardLimit(int &min, int &max, int &def);
- int setAnalogVideoStandard(int standard);
- int getAnalogVideoStandard();
-
- int updateAnalogVideoLockStateLimit(int &min, int &max, int &def);
- int setAnalogVideoLockState(int status);
- int getAnalogVideoLockState();
-};
-
-#endif /* UVCCAMERA_H_ */
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCPreview.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCPreview.cpp
deleted file mode 100755
index e104d90c34..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCPreview.cpp
+++ /dev/null
@@ -1,1039 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: UVCPreview.cpp
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#include
-#include
-#include
-
-#if 1 // set 1 if you don't need debug log
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // w/o LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
-// #undef NDEBUG
-#endif
-
-#include "utilbase.h"
-#include "UVCPreview.h"
-#include "libuvc_internal.h"
-#include "jpeglib.h"
-// MODIFIED
-#define MJPEG_FRAME_THRESHOLD_BYTE 10000
-//
-#define LOCAL_DEBUG 0
-#define MAX_FRAME 4
-#define PREVIEW_PIXEL_BYTES 4 // RGBA/RGBX
-#define FRAME_POOL_SZ MAX_FRAME + 2
-
-UVCPreview::UVCPreview(uvc_device_handle_t *devh)
-: mPreviewWindow(NULL),
- mCaptureWindow(NULL),
- mDeviceHandle(devh),
- requestWidth(DEFAULT_PREVIEW_WIDTH),
- requestHeight(DEFAULT_PREVIEW_HEIGHT),
- requestMinFps(DEFAULT_PREVIEW_FPS_MIN),
- requestMaxFps(DEFAULT_PREVIEW_FPS_MAX),
- requestMode(DEFAULT_PREVIEW_MODE),
- requestBandwidth(DEFAULT_BANDWIDTH),
- frameWidth(DEFAULT_PREVIEW_WIDTH),
- frameHeight(DEFAULT_PREVIEW_HEIGHT),
- frameBytes(DEFAULT_PREVIEW_WIDTH * DEFAULT_PREVIEW_HEIGHT * 2), // YUYV
- frameMode(0),
- previewBytes(DEFAULT_PREVIEW_WIDTH * DEFAULT_PREVIEW_HEIGHT * PREVIEW_PIXEL_BYTES),
- previewFormat(WINDOW_FORMAT_RGBA_8888),
- mIsRunning(false),
- mIsCapturing(false),
- mPreviewFrameCallbackObj(NULL), // MODIFIED
- captureQueu(NULL),
- mFrameCallbackObj(NULL),
- mFrameCallbackFunc(NULL),
- callbackPixelBytes(2) {
-
- ENTER();
- pthread_cond_init(&preview_sync, NULL);
- pthread_mutex_init(&preview_mutex, NULL);
-//
- pthread_cond_init(&capture_sync, NULL);
- pthread_mutex_init(&capture_mutex, NULL);
-//
- pthread_mutex_init(&pool_mutex, NULL);
- EXIT();
-}
-
-UVCPreview::~UVCPreview() {
-
- ENTER();
- if (mPreviewWindow)
- ANativeWindow_release(mPreviewWindow);
- mPreviewWindow = NULL;
- if (mCaptureWindow)
- ANativeWindow_release(mCaptureWindow);
- mCaptureWindow = NULL;
- clearPreviewFrame();
- clearCaptureFrame();
- clear_pool();
- pthread_mutex_destroy(&preview_mutex);
- pthread_cond_destroy(&preview_sync);
- pthread_mutex_destroy(&capture_mutex);
- pthread_cond_destroy(&capture_sync);
- pthread_mutex_destroy(&pool_mutex);
- EXIT();
-}
-
-/**
- * get uvc_frame_t from frame pool
- * if pool is empty, create new frame
- * this function does not confirm the frame size
- * and you may need to confirm the size
- */
-uvc_frame_t *UVCPreview::get_frame(size_t data_bytes) {
- uvc_frame_t *frame = NULL;
- pthread_mutex_lock(&pool_mutex);
- {
- if (!mFramePool.isEmpty()) {
- frame = mFramePool.last();
- }
- }
- pthread_mutex_unlock(&pool_mutex);
- if UNLIKELY(!frame) {
- LOGW("allocate new frame");
- frame = uvc_allocate_frame(data_bytes);
- }
- return frame;
-}
-
-void UVCPreview::recycle_frame(uvc_frame_t *frame) {
- pthread_mutex_lock(&pool_mutex);
- if (LIKELY(mFramePool.size() < FRAME_POOL_SZ)) {
- mFramePool.put(frame);
- frame = NULL;
- }
- pthread_mutex_unlock(&pool_mutex);
- if (UNLIKELY(frame)) {
- uvc_free_frame(frame);
- }
-}
-
-
-void UVCPreview::init_pool(size_t data_bytes) {
- ENTER();
-
- clear_pool();
- pthread_mutex_lock(&pool_mutex);
- {
- for (int i = 0; i < FRAME_POOL_SZ; i++) {
- mFramePool.put(uvc_allocate_frame(data_bytes));
- }
- }
- pthread_mutex_unlock(&pool_mutex);
-
- EXIT();
-}
-
-void UVCPreview::clear_pool() {
- ENTER();
-
- pthread_mutex_lock(&pool_mutex);
- {
- const int n = mFramePool.size();
- for (int i = 0; i < n; i++) {
- uvc_free_frame(mFramePool[i]);
- }
- mFramePool.clear();
- }
- pthread_mutex_unlock(&pool_mutex);
- EXIT();
-}
-
-inline const bool UVCPreview::isRunning() const {return mIsRunning; }
-
-int UVCPreview::setPreviewSize(int width, int height, int min_fps, int max_fps, int mode, float bandwidth) {
- ENTER();
-
- int result = 0;
- if ((requestWidth != width) || (requestHeight != height) || (requestMode != mode)) {
- requestWidth = width;
- requestHeight = height;
- requestMinFps = min_fps;
- requestMaxFps = max_fps;
- requestMode = mode;
- requestBandwidth = bandwidth;
-
- uvc_stream_ctrl_t ctrl;
- result = uvc_get_stream_ctrl_format_size_fps(mDeviceHandle, &ctrl,
- !requestMode ? UVC_FRAME_FORMAT_YUYV : UVC_FRAME_FORMAT_MJPEG,
- requestWidth, requestHeight, requestMinFps, requestMaxFps);
- }
-
- RETURN(result, int);
-}
-
-int UVCPreview::setPreviewDisplay(ANativeWindow *preview_window) {
- ENTER();
- pthread_mutex_lock(&preview_mutex);
- {
- if (mPreviewWindow != preview_window) {
- if (mPreviewWindow)
- ANativeWindow_release(mPreviewWindow);
- mPreviewWindow = preview_window;
- if (LIKELY(mPreviewWindow)) {
- ANativeWindow_setBuffersGeometry(mPreviewWindow,
- frameWidth, frameHeight, previewFormat);
- }
- }
- }
- pthread_mutex_unlock(&preview_mutex);
- RETURN(0, int);
-}
-
-int UVCPreview::setFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format) {
-
- ENTER();
- pthread_mutex_lock(&capture_mutex);
- {
- if (isRunning() && isCapturing()) {
- mIsCapturing = false;
- if (mFrameCallbackObj) {
- pthread_cond_signal(&capture_sync);
- pthread_cond_wait(&capture_sync, &capture_mutex); // wait finishing capturing
- }
- }
- if (!env->IsSameObject(mFrameCallbackObj, frame_callback_obj)) {
- iframecallback_fields.onFrame = NULL;
- if (mFrameCallbackObj) {
- env->DeleteGlobalRef(mFrameCallbackObj);
- }
- mFrameCallbackObj = frame_callback_obj;
- if (frame_callback_obj) {
- // get method IDs of Java object for callback
- jclass clazz = env->GetObjectClass(frame_callback_obj);
- if (LIKELY(clazz)) {
- iframecallback_fields.onFrame = env->GetMethodID(clazz,
- "onFrame", "(Ljava/nio/ByteBuffer;)V");
- } else {
- LOGW("failed to get object class");
- }
- env->ExceptionClear();
- if (!iframecallback_fields.onFrame) {
- LOGE("Can't find IFrameCallback#onFrame");
- env->DeleteGlobalRef(frame_callback_obj);
- mFrameCallbackObj = frame_callback_obj = NULL;
- }
- }
- }
- if (frame_callback_obj) {
- mPixelFormat = pixel_format;
- callbackPixelFormatChanged();
- }
- }
- pthread_mutex_unlock(&capture_mutex);
- RETURN(0, int);
-}
-
-// MODIFIED
-int UVCPreview::setPreviewFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format) {
-
- ENTER();
- if (!env->IsSameObject(mPreviewFrameCallbackObj, frame_callback_obj)) {
- ipreviewframecallback_fields.onFrame = NULL;
- if (mPreviewFrameCallbackObj) {
- env->DeleteGlobalRef(mPreviewFrameCallbackObj);
- }
- mPreviewFrameCallbackObj = frame_callback_obj;
- if (frame_callback_obj) {
- // get method IDs of Java object for callback
- jclass clazz = env->GetObjectClass(frame_callback_obj);
- if (LIKELY(clazz)) {
- ipreviewframecallback_fields.onFrame = env->GetMethodID(clazz,
- "onFrame", "([B)V");
- } else {
- LOGW("failed to get object class");
- }
- env->ExceptionClear();
- if (!ipreviewframecallback_fields.onFrame) {
- LOGE("Can't find IPreviewFrameCallback#onFrame");
- env->DeleteGlobalRef(frame_callback_obj);
- mPreviewFrameCallbackObj = frame_callback_obj = NULL;
- }
- }
- }
- if (frame_callback_obj) {
- mPixelFormat = pixel_format;
- callbackPixelFormatChanged();
- }
- RETURN(0, int);
-}
-
-
-
-void UVCPreview::callbackPixelFormatChanged() {
- mFrameCallbackFunc = NULL;
- const size_t sz = requestWidth * requestHeight;
- switch (mPixelFormat) {
- case PIXEL_FORMAT_RAW:
- LOGI("PIXEL_FORMAT_RAW:");
- callbackPixelBytes = sz * 2;
- break;
- case PIXEL_FORMAT_YUV:
- LOGI("PIXEL_FORMAT_YUV:");
- callbackPixelBytes = sz * 2;
- break;
- case PIXEL_FORMAT_RGB565:
- LOGI("PIXEL_FORMAT_RGB565:");
- mFrameCallbackFunc = uvc_any2rgb565;
- callbackPixelBytes = sz * 2;
- break;
- case PIXEL_FORMAT_RGBX:
- LOGI("PIXEL_FORMAT_RGBX:");
- mFrameCallbackFunc = uvc_any2rgbx;
- callbackPixelBytes = sz * 4;
- break;
- case PIXEL_FORMAT_YUV20SP:
- LOGI("PIXEL_FORMAT_YUV20SP:");
- mFrameCallbackFunc = uvc_yuyv2iyuv420SP;
- callbackPixelBytes = (sz * 3) / 2;
- break;
- case PIXEL_FORMAT_NV21:
- LOGI("PIXEL_FORMAT_NV21:");
- mFrameCallbackFunc = uvc_yuyv2yuv420SP;
- callbackPixelBytes = (sz * 3) / 2;
- break;
- }
-}
-
-void UVCPreview::clearDisplay() {
- ENTER();
-
- ANativeWindow_Buffer buffer;
- pthread_mutex_lock(&capture_mutex);
- {
- if (LIKELY(mCaptureWindow)) {
- if (LIKELY(ANativeWindow_lock(mCaptureWindow, &buffer, NULL) == 0)) {
- uint8_t *dest = (uint8_t *)buffer.bits;
- const size_t bytes = buffer.width * PREVIEW_PIXEL_BYTES;
- const int stride = buffer.stride * PREVIEW_PIXEL_BYTES;
- for (int i = 0; i < buffer.height; i++) {
- memset(dest, 0, bytes);
- dest += stride;
- }
- ANativeWindow_unlockAndPost(mCaptureWindow);
- }
- }
- }
- pthread_mutex_unlock(&capture_mutex);
- pthread_mutex_lock(&preview_mutex);
- {
- if (LIKELY(mPreviewWindow)) {
- if (LIKELY(ANativeWindow_lock(mPreviewWindow, &buffer, NULL) == 0)) {
- uint8_t *dest = (uint8_t *)buffer.bits;
- const size_t bytes = buffer.width * PREVIEW_PIXEL_BYTES;
- const int stride = buffer.stride * PREVIEW_PIXEL_BYTES;
- for (int i = 0; i < buffer.height; i++) {
- memset(dest, 0, bytes);
- dest += stride;
- }
- ANativeWindow_unlockAndPost(mPreviewWindow);
- }
- }
- }
- pthread_mutex_unlock(&preview_mutex);
-
- EXIT();
-}
-
-int UVCPreview::startPreview() {
- ENTER();
-
- int result = EXIT_FAILURE;
- if (!isRunning()) {
- mIsRunning = true;
- pthread_mutex_lock(&preview_mutex);
- {
- // MODIFIED
- //if (LIKELY(mPreviewWindow)) {
- result = pthread_create(&preview_thread, NULL, preview_thread_func, (void *)this);
- //}
- }
- pthread_mutex_unlock(&preview_mutex);
- if (UNLIKELY(result != EXIT_SUCCESS)) {
- LOGW("UVCCamera::window does not exist/already running/could not create thread etc.");
- mIsRunning = false;
- pthread_mutex_lock(&preview_mutex);
- {
- pthread_cond_signal(&preview_sync);
- }
- pthread_mutex_unlock(&preview_mutex);
- }
- }
- RETURN(result, int);
-}
-
-int UVCPreview::stopPreview() {
- ENTER();
- bool b = isRunning();
- if (LIKELY(b)) {
- mIsRunning = false;
- pthread_cond_signal(&preview_sync);
- pthread_cond_signal(&capture_sync);
- if (pthread_join(capture_thread, NULL) != EXIT_SUCCESS) {
- LOGW("UVCPreview::terminate capture thread: pthread_join failed");
- }
- if (pthread_join(preview_thread, NULL) != EXIT_SUCCESS) {
- LOGW("UVCPreview::terminate preview thread: pthread_join failed");
- }
- clearDisplay();
- }
- clearPreviewFrame();
- clearCaptureFrame();
- pthread_mutex_lock(&preview_mutex);
- if (mPreviewWindow) {
- ANativeWindow_release(mPreviewWindow);
- mPreviewWindow = NULL;
- }
- pthread_mutex_unlock(&preview_mutex);
- pthread_mutex_lock(&capture_mutex);
- if (mCaptureWindow) {
- ANativeWindow_release(mCaptureWindow);
- mCaptureWindow = NULL;
- }
- pthread_mutex_unlock(&capture_mutex);
- RETURN(0, int);
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-void UVCPreview::uvc_preview_frame_callback(uvc_frame_t *frame, void *vptr_args) {
- UVCPreview *preview = reinterpret_cast(vptr_args);
- if UNLIKELY(!preview->isRunning() || !frame || !frame->frame_format || !frame->data || !frame->data_bytes) return;
- if (UNLIKELY(
- ((frame->frame_format != UVC_FRAME_FORMAT_MJPEG) && (frame->actual_bytes < preview->frameBytes))
- || (frame->width != preview->frameWidth) || (frame->height != preview->frameHeight) )) {
-
-#if LOCAL_DEBUG
- LOGD("broken frame!:format=%d,actual_bytes=%d/%d(%d,%d/%d,%d)",
- frame->frame_format, frame->actual_bytes, preview->frameBytes,
- frame->width, frame->height, preview->frameWidth, preview->frameHeight);
-#endif
- return;
- }
- if (LIKELY(preview->isRunning())) {
- uvc_frame_t *copy = preview->get_frame(frame->data_bytes);
- if (UNLIKELY(!copy)) {
-#if LOCAL_DEBUG
- LOGE("uvc_callback:unable to allocate duplicate frame!");
-#endif
- return;
- }
- uvc_error_t ret = uvc_duplicate_frame(frame, copy);
- if (UNLIKELY(ret)) {
- preview->recycle_frame(copy);
- return;
- }
- preview->addPreviewFrame(copy);
- }
-}
-
-void UVCPreview::addPreviewFrame(uvc_frame_t *frame) {
-
- pthread_mutex_lock(&preview_mutex);
- if (isRunning() && (previewFrames.size() < MAX_FRAME)) {
- previewFrames.put(frame);
- frame = NULL;
- pthread_cond_signal(&preview_sync);
- }
- pthread_mutex_unlock(&preview_mutex);
- if (frame) {
- recycle_frame(frame);
- }
-}
-
-uvc_frame_t *UVCPreview::waitPreviewFrame() {
- uvc_frame_t *frame = NULL;
- pthread_mutex_lock(&preview_mutex);
- {
- if (!previewFrames.size()) {
- pthread_cond_wait(&preview_sync, &preview_mutex);
- }
- if (LIKELY(isRunning() && previewFrames.size() > 0)) {
- frame = previewFrames.remove(0);
- }
- }
- pthread_mutex_unlock(&preview_mutex);
- return frame;
-}
-
-void UVCPreview::clearPreviewFrame() {
- pthread_mutex_lock(&preview_mutex);
- {
- for (int i = 0; i < previewFrames.size(); i++)
- recycle_frame(previewFrames[i]);
- previewFrames.clear();
- }
- pthread_mutex_unlock(&preview_mutex);
-}
-
-void *UVCPreview::preview_thread_func(void *vptr_args) {
- int result;
-
- ENTER();
- UVCPreview *preview = reinterpret_cast(vptr_args);
- if (LIKELY(preview)) {
- uvc_stream_ctrl_t ctrl;
- result = preview->prepare_preview(&ctrl);
- if (LIKELY(!result)) {
- preview->do_preview(&ctrl);
- }
- }
- PRE_EXIT();
- pthread_exit(NULL);
-}
-
-int UVCPreview::prepare_preview(uvc_stream_ctrl_t *ctrl) {
- uvc_error_t result;
-
- ENTER();
- result = uvc_get_stream_ctrl_format_size_fps(mDeviceHandle, ctrl,
- !requestMode ? UVC_FRAME_FORMAT_YUYV : UVC_FRAME_FORMAT_MJPEG,
- requestWidth, requestHeight, requestMinFps, requestMaxFps
- );
- if (LIKELY(!result)) {
-#if LOCAL_DEBUG
- uvc_print_stream_ctrl(ctrl, stderr);
-#endif
- uvc_frame_desc_t *frame_desc;
- result = uvc_get_frame_desc(mDeviceHandle, ctrl, &frame_desc);
- if (LIKELY(!result)) {
- frameWidth = frame_desc->wWidth;
- frameHeight = frame_desc->wHeight;
- LOGI("frameSize=(%d,%d)@%s", frameWidth, frameHeight, (!requestMode ? "YUYV" : "MJPEG"));
- pthread_mutex_lock(&preview_mutex);
- if (LIKELY(mPreviewWindow)) {
- ANativeWindow_setBuffersGeometry(mPreviewWindow,
- frameWidth, frameHeight, previewFormat);
- }
- pthread_mutex_unlock(&preview_mutex);
- } else {
- frameWidth = requestWidth;
- frameHeight = requestHeight;
- }
- frameMode = requestMode;
- frameBytes = frameWidth * frameHeight * (!requestMode ? 2 : 4);
- previewBytes = frameWidth * frameHeight * PREVIEW_PIXEL_BYTES;
- } else {
- LOGE("could not negotiate with camera:err=%d", result);
- }
- RETURN(result, int);
-}
-
-void UVCPreview::do_preview(uvc_stream_ctrl_t *ctrl) {
- ENTER();
- // MODIFIED
- JavaVM *vm = getVM();
- JNIEnv *env;
- vm->AttachCurrentThread(&env, NULL);
-
- uvc_frame_t *frame = NULL;
- uvc_frame_t *frame_mjpeg = NULL;
- uvc_error_t result = uvc_start_streaming_bandwidth(
- mDeviceHandle, ctrl, uvc_preview_frame_callback, (void *)this, requestBandwidth, 0);
-
- if (LIKELY(!result)) {
- clearPreviewFrame();
- pthread_create(&capture_thread, NULL, capture_thread_func, (void *)this);
-
-#if LOCAL_DEBUG
- LOGI("Streaming...");
-#endif
- if (frameMode) {
- // MJPEG mode
- for ( ; LIKELY(isRunning()) ; ) {
- frame_mjpeg = waitPreviewFrame();
- if (LIKELY(frame_mjpeg)) {
- // MODIFIED:
- // Insert omitted meta data to JPEG frames from some web-cameras
- // which output Motion-JPEG movie encoded with AVI format,
- // so that BitmapFactory class of Android API decodes and re-sizes JPEG frames.
- // (Motion-JPEG as AVI movie is often omitted huffman tables.)
- do_preview_pass_through_mjpeg(env, frame_mjpeg);
-
- // MODIFIED:
- // frame = get_frame(frame_mjpeg->width * frame_mjpeg->height * 2);
- // result = uvc_mjpeg2yuyv(frame_mjpeg, frame); // MJPEG => yuyv
- if (frame_mjpeg->data_bytes > MJPEG_FRAME_THRESHOLD_BYTE) { // XXX
- //Restart streaming
- uvc_stop_streaming(mDeviceHandle);
- uvc_start_streaming_bandwidth(
- mDeviceHandle, ctrl, uvc_preview_frame_callback, (void *) this,
- requestBandwidth, 0);
- }
- recycle_frame(frame_mjpeg);
- //if (LIKELY(!result)) {
- // frame = draw_preview_one(frame, &mPreviewWindow, uvc_any2rgbx, 4);
- // addCaptureFrame(frame);
- //} else {
- // recycle_frame(frame);
- //}
- }
- }
- } else {
- // yuvyv mode
- for ( ; LIKELY(isRunning()) ; ) {
- frame = waitPreviewFrame();
- if (LIKELY(frame)) {
- // MODIFIED
- do_preview_pass_through(env, frame);
-
- frame = draw_preview_one(frame, &mPreviewWindow, uvc_any2rgbx, 4);
- addCaptureFrame(frame);
- }
- }
- }
- pthread_cond_signal(&capture_sync);
-#if LOCAL_DEBUG
- LOGI("preview_thread_func:wait for all callbacks complete");
-#endif
- uvc_stop_streaming(mDeviceHandle);
-#if LOCAL_DEBUG
- LOGI("Streaming finished");
-#endif
- } else {
- uvc_perror(result, "failed start_streaming");
- }
- // MODIFIED
- vm->DetachCurrentThread();
-
- EXIT();
-}
-
-static void copyFrame(const uint8_t *src, uint8_t *dest, const int width, int height, const int stride_src, const int stride_dest) {
- const int h8 = height % 8;
- for (int i = 0; i < h8; i++) {
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- }
- for (int i = 0; i < height; i += 8) {
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- }
-}
-
-
-// transfer specific frame data to the Surface(ANativeWindow)
-int copyToSurface(uvc_frame_t *frame, ANativeWindow **window) {
- // ENTER();
- int result = 0;
- if (LIKELY(*window)) {
- ANativeWindow_Buffer buffer;
- if (LIKELY(ANativeWindow_lock(*window, &buffer, NULL) == 0)) {
- // source = frame data
- const uint8_t *src = (uint8_t *)frame->data;
- const int src_w = frame->width * PREVIEW_PIXEL_BYTES;
- const int src_step = frame->width * PREVIEW_PIXEL_BYTES;
- // destination = Surface(ANativeWindow)
- uint8_t *dest = (uint8_t *)buffer.bits;
- const int dest_w = buffer.width * PREVIEW_PIXEL_BYTES;
- const int dest_step = buffer.stride * PREVIEW_PIXEL_BYTES;
- // use lower transfer bytes
- const int w = src_w < dest_w ? src_w : dest_w;
- // use lower height
- const int h = frame->height < buffer.height ? frame->height : buffer.height;
- // transfer from frame data to the Surface
- copyFrame(src, dest, w, h, src_step, dest_step);
- ANativeWindow_unlockAndPost(*window);
- } else {
- result = -1;
- }
- } else {
- result = -1;
- }
- return result; //RETURN(result, int);
-}
-
-// changed to return original frame instead of returning converted frame even if convert_func is not null.
-uvc_frame_t *UVCPreview::draw_preview_one(uvc_frame_t *frame, ANativeWindow **window, convFunc_t convert_func, int pixcelBytes) {
- // ENTER();
-
- int b = 0;
- pthread_mutex_lock(&preview_mutex);
- {
- b = *window != NULL;
- }
- pthread_mutex_unlock(&preview_mutex);
- if (LIKELY(b)) {
- uvc_frame_t *converted;
- if (convert_func) {
- converted = get_frame(frame->width * frame->height * pixcelBytes);
- if LIKELY(converted) {
- b = convert_func(frame, converted);
- if (!b) {
- pthread_mutex_lock(&preview_mutex);
- copyToSurface(converted, window);
- pthread_mutex_unlock(&preview_mutex);
- } else {
- LOGE("failed converting");
- }
- recycle_frame(converted);
- }
- } else {
- pthread_mutex_lock(&preview_mutex);
- copyToSurface(frame, window);
- pthread_mutex_unlock(&preview_mutex);
- }
- }
- return frame; //RETURN(frame, uvc_frame_t *);
-}
-
-//======================================================================
-//
-//======================================================================
-inline const bool UVCPreview::isCapturing() const { return mIsCapturing; }
-
-int UVCPreview::setCaptureDisplay(ANativeWindow *capture_window) {
- ENTER();
- pthread_mutex_lock(&capture_mutex);
- {
- if (isRunning() && isCapturing()) {
- mIsCapturing = false;
- if (mCaptureWindow) {
- pthread_cond_signal(&capture_sync);
- pthread_cond_wait(&capture_sync, &capture_mutex); // wait finishing capturing
- }
- }
- if (mCaptureWindow != capture_window) {
- // release current Surface if already assigned.
- if (UNLIKELY(mCaptureWindow))
- ANativeWindow_release(mCaptureWindow);
- mCaptureWindow = capture_window;
- // if you use Surface came from MediaCodec#createInputSurface
- // you could not change window format at least when you use
- // ANativeWindow_lock / ANativeWindow_unlockAndPost
- // to write frame data to the Surface...
- // So we need check here.
- if (mCaptureWindow) {
- int32_t window_format = ANativeWindow_getFormat(mCaptureWindow);
- if ((window_format != WINDOW_FORMAT_RGB_565)
- && (previewFormat == WINDOW_FORMAT_RGB_565)) {
- LOGE("window format mismatch, cancelled movie capturing.");
- ANativeWindow_release(mCaptureWindow);
- mCaptureWindow = NULL;
- }
- }
- }
- }
- pthread_mutex_unlock(&capture_mutex);
- RETURN(0, int);
-}
-
-void UVCPreview::addCaptureFrame(uvc_frame_t *frame) {
- pthread_mutex_lock(&capture_mutex);
- if (LIKELY(isRunning())) {
- // keep only latest one
- if (captureQueu) {
- recycle_frame(captureQueu);
- }
- captureQueu = frame;
- pthread_cond_broadcast(&capture_sync);
- }
- pthread_mutex_unlock(&capture_mutex);
-}
-
-/**
- * get frame data for capturing, if not exist, block and wait
- */
-uvc_frame_t *UVCPreview::waitCaptureFrame() {
- uvc_frame_t *frame = NULL;
- pthread_mutex_lock(&capture_mutex);
- {
- if (!captureQueu) {
- pthread_cond_wait(&capture_sync, &capture_mutex);
- }
- if (LIKELY(isRunning() && captureQueu)) {
- frame = captureQueu;
- captureQueu = NULL;
- }
- }
- pthread_mutex_unlock(&capture_mutex);
- return frame;
-}
-
-/**
- * clear drame data for capturing
- */
-void UVCPreview::clearCaptureFrame() {
- pthread_mutex_lock(&capture_mutex);
- {
- if (captureQueu)
- recycle_frame(captureQueu);
- captureQueu = NULL;
- }
- pthread_mutex_unlock(&capture_mutex);
-}
-
-//======================================================================
-/*
- * thread function
- * @param vptr_args pointer to UVCPreview instance
- */
-// static
-void *UVCPreview::capture_thread_func(void *vptr_args) {
- int result;
-
- ENTER();
- UVCPreview *preview = reinterpret_cast(vptr_args);
- if (LIKELY(preview)) {
- JavaVM *vm = getVM();
- JNIEnv *env;
- // attach to JavaVM
- vm->AttachCurrentThread(&env, NULL);
- preview->do_capture(env); // never return until finish previewing
- // detach from JavaVM
- vm->DetachCurrentThread();
- MARK("DetachCurrentThread");
- }
- PRE_EXIT();
- pthread_exit(NULL);
-}
-
-/**
- * the actual function for capturing
- */
-void UVCPreview::do_capture(JNIEnv *env) {
-
- ENTER();
-
- clearCaptureFrame();
- callbackPixelFormatChanged();
- for (; isRunning() ;) {
- mIsCapturing = true;
- if (mCaptureWindow) {
- do_capture_surface(env);
- } else {
- do_capture_idle_loop(env);
- }
- pthread_cond_broadcast(&capture_sync);
- } // end of for (; isRunning() ;)
- EXIT();
-}
-
-void UVCPreview::do_capture_idle_loop(JNIEnv *env) {
- ENTER();
-
- for (; isRunning() && isCapturing() ;) {
- do_capture_callback(env, waitCaptureFrame());
- }
-
- EXIT();
-}
-
-/**
- * write frame data to Surface for capturing
- */
-void UVCPreview::do_capture_surface(JNIEnv *env) {
- ENTER();
-
- uvc_frame_t *frame = NULL;
- uvc_frame_t *converted = NULL;
- char *local_picture_path;
-
- for (; isRunning() && isCapturing() ;) {
- frame = waitCaptureFrame();
- if (LIKELY(frame)) {
- // frame data is always YUYV format.
- if LIKELY(isCapturing()) {
- if (UNLIKELY(!converted)) {
- converted = get_frame(previewBytes);
- }
- if (LIKELY(converted)) {
- int b = uvc_any2rgbx(frame, converted);
- if (!b) {
- if (LIKELY(mCaptureWindow)) {
- copyToSurface(converted, &mCaptureWindow);
- }
- }
- }
- }
- do_capture_callback(env, frame);
- }
- }
- if (converted) {
- recycle_frame(converted);
- }
- if (mCaptureWindow) {
- ANativeWindow_release(mCaptureWindow);
- mCaptureWindow = NULL;
- }
-
- EXIT();
-}
-
-/**
-* call IFrameCallback#onFrame if needs
- */
-void UVCPreview::do_capture_callback(JNIEnv *env, uvc_frame_t *frame) {
- ENTER();
-
- if (LIKELY(frame)) {
- uvc_frame_t *callback_frame = frame;
- if (mFrameCallbackObj) {
- if (mFrameCallbackFunc) {
- callback_frame = get_frame(callbackPixelBytes);
- if (LIKELY(callback_frame)) {
- int b = mFrameCallbackFunc(frame, callback_frame);
- recycle_frame(frame);
- if (UNLIKELY(b)) {
- LOGW("failed to convert for callback frame");
- goto SKIP;
- }
- } else {
- LOGW("failed to allocate for callback frame");
- callback_frame = frame;
- goto SKIP;
- }
- }
- jobject buf = env->NewDirectByteBuffer(callback_frame->data, callbackPixelBytes);
- env->CallVoidMethod(mFrameCallbackObj, iframecallback_fields.onFrame, buf);
- env->ExceptionClear();
- env->DeleteLocalRef(buf);
- }
- SKIP:
- recycle_frame(callback_frame);
- }
- EXIT();
-}
-
-// MODIFIED
-void UVCPreview::do_preview_pass_through(JNIEnv *env, uvc_frame_t *frame) {
- ENTER();
- if (LIKELY(frame)) {
- if (mPreviewFrameCallbackObj) {
- int len = frame->data_bytes;
- jbyteArray array = env->NewByteArray(len);
- jbyte *elems = env->GetByteArrayElements(array, NULL);
- memcpy(elems, frame->data, len);
-
- env->CallVoidMethod(mPreviewFrameCallbackObj, ipreviewframecallback_fields.onFrame, array);
- env->ExceptionClear();
- env->ReleaseByteArrayElements(array, elems, 0);
- env->DeleteLocalRef(array);
- }
- }
- EXIT();
-}
-
-// MODIFIED
-void UVCPreview::do_preview_pass_through_mjpeg(JNIEnv *env, uvc_frame_t *frame) {
- ENTER();
-
- static const size_t huff_tbl_size = 432;
- static const UINT8 huff_tbl[huff_tbl_size] = {
- // dc_huff_tbl_0
- 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b,
-
- // ac_huff_tbl_0
- 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
- 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
- 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
- 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14,
- 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15,
- 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a,
- 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29,
- 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
- 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
- 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
- 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
- 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
- 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
- 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
- 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6,
- 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
- 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
- 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
- 0xf8, 0xf9, 0xfa,
-
- // dc_huff_tbl_1
- 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b,
-
- // ac_huff_tbl_1
- 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
- 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02,
- 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
- 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32,
- 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23,
- 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24,
- 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27,
- 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43,
- 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
- 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
- 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
- 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
- 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4,
- 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5,
- 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
- 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
- 0xf8, 0xf9, 0xfa
- };
-
- if (LIKELY(frame)) {
- if (mPreviewFrameCallbackObj) {
- int len = frame->data_bytes + huff_tbl_size;
- jbyteArray array = env->NewByteArray(len);
- jbyte *elems = env->GetByteArrayElements(array, NULL);
- memcpy(elems, frame->data, 2);
- memcpy((elems + 2), huff_tbl, huff_tbl_size);
- memcpy((elems + 2 + huff_tbl_size), (((UINT8 *) frame->data) + 2), frame->data_bytes - 2);
-
- env->CallVoidMethod(mPreviewFrameCallbackObj, ipreviewframecallback_fields.onFrame, array);
- env->ExceptionClear();
- env->ReleaseByteArrayElements(array, elems, 0);
- env->DeleteLocalRef(array);
- }
- }
- EXIT();
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCPreview.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCPreview.h
deleted file mode 100755
index c9cd556e9d..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCPreview.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: UVCPreview.h
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#ifndef UVCPREVIEW_H_
-#define UVCPREVIEW_H_
-
-#include "libUVCCamera.h"
-#include
-#include
-#include "objectarray.h"
-
-#pragma interface
-
-#define DEFAULT_PREVIEW_WIDTH 640
-#define DEFAULT_PREVIEW_HEIGHT 480
-#define DEFAULT_PREVIEW_FPS_MIN 1
-#define DEFAULT_PREVIEW_FPS_MAX 30
-#define DEFAULT_PREVIEW_MODE 0
-#define DEFAULT_BANDWIDTH 1.0f
-
-typedef uvc_error_t (*convFunc_t)(uvc_frame_t *in, uvc_frame_t *out);
-
-#define PIXEL_FORMAT_RAW 0 // same as PIXEL_FORMAT_YUV
-#define PIXEL_FORMAT_YUV 1
-#define PIXEL_FORMAT_RGB565 2
-#define PIXEL_FORMAT_RGBX 3
-#define PIXEL_FORMAT_YUV20SP 4
-#define PIXEL_FORMAT_NV21 5 // YVU420SemiPlanar
-
-// for callback to Java object
-typedef struct {
- jmethodID onFrame;
-} Fields_iframecallback;
-
-// MODIFIED
-typedef struct {
- jmethodID onFrame;
-} Fields_ipreviewframecallback;
-
-class UVCPreview {
-private:
- uvc_device_handle_t *mDeviceHandle;
- ANativeWindow *mPreviewWindow;
- volatile bool mIsRunning;
- int requestWidth, requestHeight, requestMode;
- int requestMinFps, requestMaxFps;
- float requestBandwidth;
- int frameWidth, frameHeight;
- int frameMode;
- size_t frameBytes;
- pthread_t preview_thread;
- pthread_mutex_t preview_mutex;
- pthread_cond_t preview_sync;
- ObjectArray previewFrames;
- int previewFormat;
- size_t previewBytes;
- // MODIFIED
- jobject mPreviewFrameCallbackObj;
- Fields_ipreviewframecallback ipreviewframecallback_fields;
-
- volatile bool mIsCapturing;
- ANativeWindow *mCaptureWindow;
- pthread_t capture_thread;
- pthread_mutex_t capture_mutex;
- pthread_cond_t capture_sync;
- uvc_frame_t *captureQueu; // keep latest frame
- jobject mFrameCallbackObj;
- convFunc_t mFrameCallbackFunc;
- Fields_iframecallback iframecallback_fields;
- int mPixelFormat;
- size_t callbackPixelBytes;
-// improve performance by reducing memory allocation
- pthread_mutex_t pool_mutex;
- ObjectArray mFramePool;
- uvc_frame_t *get_frame(size_t data_bytes);
- void recycle_frame(uvc_frame_t *frame);
- void init_pool(size_t data_bytes);
- void clear_pool();
-//
- void clearDisplay();
- static void uvc_preview_frame_callback(uvc_frame_t *frame, void *vptr_args);
- void addPreviewFrame(uvc_frame_t *frame);
- uvc_frame_t *waitPreviewFrame();
- void clearPreviewFrame();
- static void *preview_thread_func(void *vptr_args);
- int prepare_preview(uvc_stream_ctrl_t *ctrl);
- void do_preview(uvc_stream_ctrl_t *ctrl);
- uvc_frame_t *draw_preview_one(uvc_frame_t *frame, ANativeWindow **window, convFunc_t func, int pixelBytes);
- // MODIFIED
- void do_preview_pass_through(JNIEnv *env, uvc_frame_t *frame);
- void do_preview_pass_through_mjpeg(JNIEnv *env, uvc_frame_t *frame);
-
- void addCaptureFrame(uvc_frame_t *frame);
- uvc_frame_t *waitCaptureFrame();
- void clearCaptureFrame();
- static void *capture_thread_func(void *vptr_args);
- void do_capture(JNIEnv *env);
- void do_capture_surface(JNIEnv *env);
- void do_capture_idle_loop(JNIEnv *env);
- void do_capture_callback(JNIEnv *env, uvc_frame_t *frame);
- void callbackPixelFormatChanged();
-public:
- UVCPreview(uvc_device_handle_t *devh);
- ~UVCPreview();
-
- inline const bool isRunning() const;
- int setPreviewSize(int width, int height, int min_fps, int max_fps, int mode, float bandwidth = 1.0f);
- int setPreviewDisplay(ANativeWindow *preview_window);
- int setFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format);
- int setPreviewFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format); // MODIFIED
- int startPreview();
- int stopPreview();
- inline const bool isCapturing() const;
- int setCaptureDisplay(ANativeWindow *capture_window);
-};
-
-#endif /* UVCPREVIEW_H_ */
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCStatusCallback.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCStatusCallback.cpp
deleted file mode 100755
index 6a800fc472..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCStatusCallback.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-#include
-#include
-#include
-#include "utilbase.h"
-#include "UVCStatusCallback.h"
-#include "libuvc_internal.h"
-
-#define LOCAL_DEBUG 0
-
-UVCStatusCallback::UVCStatusCallback(uvc_device_handle_t *devh)
-: mDeviceHandle(devh),
- mStatusCallbackObj(NULL) {
-
- ENTER();
- pthread_mutex_init(&status_mutex, NULL);
-
- uvc_set_status_callback(mDeviceHandle, uvc_status_callback, (void *)this);
- EXIT();
-}
-
-UVCStatusCallback::~UVCStatusCallback() {
-
- ENTER();
- pthread_mutex_destroy(&status_mutex);
- EXIT();
-}
-
-int UVCStatusCallback::setCallback(JNIEnv *env, jobject status_callback_obj) {
-
- ENTER();
- pthread_mutex_lock(&status_mutex);
- {
- if (!env->IsSameObject(mStatusCallbackObj, status_callback_obj)) {
- istatuscallback_fields.onStatus = NULL;
- if (mStatusCallbackObj) {
- env->DeleteGlobalRef(mStatusCallbackObj);
- }
- mStatusCallbackObj = status_callback_obj;
- if (status_callback_obj) {
- // get method IDs of Java object for callback
- jclass clazz = env->GetObjectClass(status_callback_obj);
- if (LIKELY(clazz)) {
- istatuscallback_fields.onStatus = env->GetMethodID(clazz,
- "onStatus", "(IIIILjava/nio/ByteBuffer;)V");
- } else {
- LOGW("failed to get object class");
- }
- env->ExceptionClear();
- if (!istatuscallback_fields.onStatus) {
- LOGE("Can't find IStatusCallback#onStatus");
- env->DeleteGlobalRef(status_callback_obj);
- mStatusCallbackObj = status_callback_obj = NULL;
- }
- }
- }
- }
- pthread_mutex_unlock(&status_mutex);
- RETURN(0, int);
-}
-
-void UVCStatusCallback::notifyStatusCallback(JNIEnv* env, uvc_status_class status_class, int event, int selector, uvc_status_attribute status_attribute, void *data, size_t data_len) {
-
- pthread_mutex_lock(&status_mutex);
- {
- if (mStatusCallbackObj) {
- jobject buf = env->NewDirectByteBuffer(data, data_len);
- env->CallVoidMethod(mStatusCallbackObj, istatuscallback_fields.onStatus, (int)status_class, event, selector, (int)status_attribute, buf);
- env->ExceptionClear();
- env->DeleteLocalRef(buf);
- }
- }
- pthread_mutex_unlock(&status_mutex);
-}
-
-void UVCStatusCallback::uvc_status_callback(uvc_status_class status_class, int event, int selector, uvc_status_attribute status_attribute, void *data, size_t data_len, void *user_ptr) {
-
- UVCStatusCallback *statusCallback = reinterpret_cast(user_ptr);
-
- JavaVM *vm = getVM();
- JNIEnv *env;
- // attach to JavaVM
- vm->AttachCurrentThread(&env, NULL);
-
- statusCallback->notifyStatusCallback(env, status_class, event, selector, status_attribute, data, data_len);
-
- vm->DetachCurrentThread();
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCStatusCallback.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCStatusCallback.h
deleted file mode 100755
index 6f4138a105..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/UVCStatusCallback.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef UVCSTATUSCALLBACK_H_
-#define UVCSTATUSCALLBACK_H_
-
-#include "libUVCCamera.h"
-#include
-#include
-#include "objectarray.h"
-
-#pragma interface
-
-// for callback to Java object
-typedef struct {
- jmethodID onStatus;
-} Fields_istatuscallback;
-
-class UVCStatusCallback {
-private:
- uvc_device_handle_t *mDeviceHandle;
- pthread_mutex_t status_mutex;
- jobject mStatusCallbackObj;
- Fields_istatuscallback istatuscallback_fields;
- void notifyStatusCallback(JNIEnv *env, uvc_status_class status_class, int event, int selector, uvc_status_attribute status_attribute, void *data, size_t data_len);
- static void uvc_status_callback(uvc_status_class status_class, int event, int selector, uvc_status_attribute status_attribute, void *data, size_t data_len, void *user_ptr);
-public:
- UVCStatusCallback(uvc_device_handle_t *devh);
- ~UVCStatusCallback();
-
- int setCallback(JNIEnv *env, jobject status_callback_obj);
-};
-
-#endif /* UVCSTATUSCALLBACK_H_ */
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/_onload.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/_onload.cpp
deleted file mode 100755
index fd138caab0..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/_onload.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: _onload.cpp
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#include "_onload.h"
-#include "utilbase.h"
-
-#define LOCAL_DEBUG 0
-
-extern int register_uvccamera(JNIEnv *env);
-
-jint JNI_OnLoad(JavaVM *vm, void *reserved) {
-#if LOCAL_DEBUG
- LOGD("JNI_OnLoad");
-#endif
-
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) {
- return JNI_ERR;
- }
- // register native methods
- int result = register_uvccamera(env);
- setVM(vm);
-#if LOCAL_DEBUG
- LOGD("JNI_OnLoad:finshed:result=%d", result);
-#endif
- return JNI_VERSION_1_6;
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/_onload.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/_onload.h
deleted file mode 100755
index 42c11a686a..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/_onload.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: _onload.h
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#ifndef ONLOAD_H_
-#define ONLOAD_H_
-
-#pragma interface
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-jint JNI_OnLoad(JavaVM *vm, void *reserved);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ONLOAD_H_ */
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/libUVCCamera.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/libUVCCamera.h
deleted file mode 100755
index 5bb678f6ab..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/libUVCCamera.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: libUVCCamera.h
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#ifndef LIBUVCCAMERA_H_
-#define LIBUVCCAMERA_H_
-
-#include
-#include "libusb.h"
-#include "libuvc.h"
-#include "utilbase.h"
-
-#endif /* LIBUVCCAMERA_H_ */
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/objectarray.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/objectarray.h
deleted file mode 100755
index 2bfd56be2d..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/objectarray.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * UVCCamera
- * library and sample to access to UVC web camera on non-rooted Android device
- *
- * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
- *
- * File name: objectarray.h
- *
- * 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 jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
-*/
-
-#ifndef OBJECTARRAY_H_
-#define OBJECTARRAY_H_
-
-#include "utilbase.h"
-
-template
-class ObjectArray {
-private:
- T *m_elements;
- const int min_size;
- int m_max_size;
- int m_size;
-public:
- ObjectArray(int initial_capacity = 2)
- : m_elements(new T[initial_capacity]),
- m_max_size(initial_capacity),
- m_size(0),
- min_size(initial_capacity) {
- }
-
- ~ObjectArray() { SAFE_DELETE_ARRAY(m_elements); }
- void size(int new_size) {
- if (new_size != capacity()) {
- T *new_elements = new T[new_size];
- LOG_ASSERT(new_elements, "out of memory:size=%d,capacity=%d", new_size, m_max_size);
- const int n = (new_size < capacity()) ? new_size : capacity();
- for (int i = 0; i < n; i++) {
- new_elements[i] = m_elements[i];
- }
- SAFE_DELETE_ARRAY(m_elements);
- m_elements = new_elements;
- m_max_size = new_size;
- m_size = (m_size < new_size) ? m_size : new_size;
- }
- }
-
- inline int size() const { return m_size; }
- inline bool isEmpty() const { return (m_size < 1); }
- inline int capacity() const { return m_max_size; }
- inline T &operator[](int index) { return m_elements[index]; }
- inline const T &operator[](int index) const { return m_elements[index]; }
- int put(T object) {
- if LIKELY(object) {
- if UNLIKELY(size() >= capacity()) {
- size(capacity() ? capacity() * 2 : 2);
- }
- m_elements[m_size++] = object;
- }
- return m_size;
- }
- /**
- * remove T which posisioned on index
- */
- T remove(int index) {
- T obj = m_elements[index];
- for (int i = index; i < m_size - 1; i++) {
- m_elements[i] = m_elements[i+1];
- }
- m_size--;
- return obj;
- }
- /**
- * search the T object and remove if exist
- */
- void removeObject(T object) {
- for (int i = 0; i < size(); i++) {
- if (m_elements[i] == object) {
- remove(i);
- break;
- }
- }
- }
- /**
- * get last T and remove from this array ¥
- * this is faster than remove(size()-1)
- */
- inline T last() {
- if LIKELY(m_size > 0)
- return m_elements[--m_size];
- else
- return NULL;
- }
- /**
- * search the T object and return it's index
- * if the T is not in this array, return -1
- */
- int getIndex(const T object) {
- int result = -1;
- for (int i = 0; i < size(); i++) {
- if (m_elements[i] == object) {
- result = i;
- break;
- }
- }
- return result;
- }
-
- /**
- * clear the T array but never delete actual T instance
- */
- inline void clear() {
- size(min_size);
- m_size = 0;
- }
-};
-
-#endif // OBJECTARRAY_H_
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/AbstractBufferedPipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/AbstractBufferedPipeline.cpp
deleted file mode 100755
index cf5eed1b88..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/AbstractBufferedPipeline.cpp
+++ /dev/null
@@ -1,344 +0,0 @@
-//
-// Created by saki on 15/11/05.
-//
-
-#if 1 // set 1 if you don't need debug message
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // ignore LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG // depends on definition in Android.mk and Application.mk
-#endif
-
-#include "utilbase.h"
-#include "AbstractBufferedPipeline.h"
-
-/*public*/
-AbstractBufferedPipeline::AbstractBufferedPipeline(const int &_max_buffer_num, const int &_init_pool_num,
- const size_t &_default_frame_size, const bool &drop_frames_when_buffer_empty)
-: IPipeline(_default_frame_size),
- max_buffer_num(_max_buffer_num),
- init_pool_num(_init_pool_num),
- drop_frames(drop_frames_when_buffer_empty),
- total_frame_num(0)
-{
- ENTER();
-
- EXIT();
-}
-
-/*public*/
-AbstractBufferedPipeline::~AbstractBufferedPipeline() {
- ENTER();
-
- release();
- setState(PIPELINE_STATE_UNINITIALIZED);
-
- EXIT();
-}
-
-/*public*/
-int AbstractBufferedPipeline::release() {
- ENTER();
-
- setState(PIPELINE_STATE_RELEASING);
- stop();
- clear_frames();
- clear_pool();
- setState(PIPELINE_STATE_UNINITIALIZED);
-
- RETURN(0, int);
-}
-
-/*public*/
-int AbstractBufferedPipeline::start() {
- ENTER();
-
- int result = EXIT_FAILURE;
- if (!isRunning()) {
- mIsRunning = true;
- setState(PIPELINE_STATE_STARTING);
- buffer_mutex.lock();
- {
- result = pthread_create(&handler_thread, NULL, handler_thread_func, (void *) this);
- }
- buffer_mutex.unlock();
- if (UNLIKELY(result != EXIT_SUCCESS)) {
- LOGW("AbstractBufferedPipeline::already running/could not create thread etc.");
- setState(PIPELINE_STATE_INITIALIZED);
- mIsRunning = false;
- buffer_mutex.lock();
- {
- pool_sync.broadcast();
- buffer_sync.broadcast();
- }
- buffer_mutex.unlock();
- }
- }
- RETURN(result, int);
-}
-
-/*public*/
-int AbstractBufferedPipeline::stop() {
- ENTER();
-
- bool b = isRunning();
- if (LIKELY(b)) {
- setState(PIPELINE_STATE_STOPPING);
- mIsRunning = false;
- pool_sync.broadcast();
- buffer_sync.broadcast();
- LOGD("pthread_join:handler_thread");
- if (pthread_join(handler_thread, NULL) != EXIT_SUCCESS) {
- LOGW("PublisherPipeline::terminate publisher thread: pthread_join failed");
- }
- setState(PIPELINE_STATE_INITIALIZED);
- LOGD("handler_thread finished");
- }
- clear_frames();
-
- RETURN(0, int);
-}
-
-/*public*/
-int AbstractBufferedPipeline::queueFrame(uvc_frame_t *frame) {
- ENTER();
-
- int ret = UVC_ERROR_OTHER;
- if (LIKELY(frame)) {
- // get empty frame from frame pool
- uvc_frame_t *copy = get_frame(frame->data_bytes);
- if (UNLIKELY(!copy)) {
- LOGD("buffer pool is empty and exceeds the limit, drop frame");
- RETURN(UVC_ERROR_NO_MEM, int);
- }
- // duplicate frame buffer and pass copy to publisher
- ret = uvc_duplicate_frame(frame, copy);
- if (LIKELY(!ret)) {
- ret = add_frame(copy);
- } else {
- LOGW("uvc_duplicate_frame failed:%d", ret);
- recycle_frame(copy);
- }
- }
-
- RETURN(ret, int);
-}
-
-//********************************************************************************
-//
-//********************************************************************************
-/**
- * get uvc_frame_t from frame pool
- * if pool is empty, create new frame
- * this function does not confirm the frame size
- * and you may need to confirm the size
- */
-uvc_frame_t *AbstractBufferedPipeline::get_frame(const size_t &data_bytes) {
- uvc_frame_t *frame = NULL;
- Mutex::Autolock lock(pool_mutex);
-
- if (UNLIKELY(frame_pool.empty() && (total_frame_num < max_buffer_num))) {
- uint32_t n = total_frame_num * 2;
- if (n > max_buffer_num) {
- n = max_buffer_num;
- }
- n -= total_frame_num;
- if (LIKELY(n > 0)) {
- for (int i = 0; i < n; i++) {
- frame = uvc_allocate_frame(data_bytes);
- total_frame_num++;
- }
- LOGW("allocate new frame:%d", total_frame_num);
- } else {
- LOGW("number of allocated frame exceeds limit");
- }
- }
- if (UNLIKELY(frame_pool.empty() && !drop_frames)) {
- // if pool is empty and need to block(avoid dropping frames), wait frame recycling.
- for (; mIsRunning && frame_pool.empty() ; ) {
- pool_sync.wait(pool_mutex);
- }
- }
- if (!frame_pool.empty()) {
- frame = frame_pool.front();
- frame_pool.pop_front();
- }
-
- return frame;
-}
-
-void AbstractBufferedPipeline::recycle_frame(uvc_frame_t *frame) {
- ENTER();
-
- if (LIKELY(frame)) {
- Mutex::Autolock lock(pool_mutex);
- if (LIKELY(frame_pool.size() < max_buffer_num)) {
- frame_pool.push_back(frame);
- frame = NULL;
- }
- if (UNLIKELY(frame)) {
- // if pool overflowed
- total_frame_num--;
- uvc_free_frame(frame);
- }
- pool_sync.signal();
- }
-
- EXIT();
-}
-
-void AbstractBufferedPipeline::init_pool(const size_t &data_bytes) {
- ENTER();
-
- uvc_frame_t *frame = NULL;
-
- clear_pool();
- pool_mutex.lock();
- {
-
- size_t frame_sz = data_bytes / 4; // expects 25%, this will be able to much lower
- if (!frame_sz) {
- frame_sz = DEFAULT_FRAME_SZ;
- }
- for (uint32_t i = 0; i < init_pool_num; i++) {
- frame = uvc_allocate_frame(frame_sz);
- if (LIKELY(frame)) {
- frame_pool.push_back(frame);
- total_frame_num++;
- } else {
- LOGW("failed to allocate new frame:%d", total_frame_num);
- break;
- }
- }
- }
- pool_mutex.unlock();
-
- EXIT();
-}
-
-void AbstractBufferedPipeline::clear_pool() {
- ENTER();
-
- Mutex::Autolock lock(pool_mutex);
-
- for (auto iter = frame_pool.begin(); iter != frame_pool.end(); iter++) {
- total_frame_num--;
- uvc_free_frame(*iter);
- }
- frame_pool.clear();
- EXIT();
-}
-
-//********************************************************************************
-//
-//********************************************************************************
-
-void AbstractBufferedPipeline::clear_frames() {
- Mutex::Autolock lock(buffer_mutex);
-
- for (auto iter = frame_buffers.begin(); iter != frame_buffers.end(); iter++) {
- recycle_frame(*iter);
- }
- frame_buffers.clear();
-}
-
-int AbstractBufferedPipeline::add_frame(uvc_frame_t *frame) {
- ENTER();
-
- buffer_mutex.lock();
- {
- // FIXME as current implementation, transferring frame data on my device is slower than that coming from UVC camera... just drop them now
- if (frame_buffers.size() > max_buffer_num) {
- // erase old frames
- int cnt = 0;
- for (auto iter = frame_buffers.begin();
- (iter != frame_buffers.end()) && (cnt < 5); iter++, cnt++) {
- recycle_frame(*iter);
- iter = frame_buffers.erase(iter);
- }
- LOGW("droped frame data");
- }
- if (isRunning() && (frame_buffers.size() < max_buffer_num)) {
- frame_buffers.push_back(frame);
- frame = NULL;
- }
- buffer_sync.signal();
- }
- buffer_mutex.unlock();
- if (frame) {
- recycle_frame(frame);
- }
-
- RETURN(0, int);
-}
-
-uvc_frame_t *AbstractBufferedPipeline::wait_frame() {
- uvc_frame_t *frame = NULL;
-
- Mutex::Autolock lock(buffer_mutex);
-
- if (!frame_buffers.size()) {
- buffer_sync.wait(buffer_mutex);
- }
- if (LIKELY(isRunning() && frame_buffers.size() > 0)) {
- frame = frame_buffers.front();
- frame_buffers.pop_front();
- }
- return frame;
-}
-
-uint32_t AbstractBufferedPipeline::get_frame_count() {
- ENTER();
-
- Mutex::Autolock lock(buffer_mutex);
- uint32_t result = frame_buffers.size();
-
- RETURN(result, uint32_t);
-}
-
-//********************************************************************************
-//
-//********************************************************************************
-void *AbstractBufferedPipeline::handler_thread_func(void *vptr_args) {
-
- ENTER();
- AbstractBufferedPipeline *pipeline = reinterpret_cast(vptr_args);
- if (LIKELY(pipeline)) {
- pipeline->do_loop();
- }
- PRE_EXIT();
- pthread_exit(NULL);
-}
-
-void AbstractBufferedPipeline::do_loop() {
- ENTER();
-
- clear_frames();
- init_pool(default_frame_size);
-
- on_start();
- setState(PIPELINE_STATE_RUNNING);
- for ( ; LIKELY(isRunning()) ; ) {
- uvc_frame_t *frame = wait_frame();
- if ((LIKELY(frame))) {
- try {
- if (!handle_frame(frame)) {
- chain_frame(frame);
- }
- } catch (...) {
- LOGE("exception");
- }
- recycle_frame(frame);
- }
- }
- setState(PIPELINE_STATE_STOPPING);
- mIsRunning = false;
- on_stop();
- setState(PIPELINE_STATE_INITIALIZED);
-
- EXIT();
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/AbstractBufferedPipeline.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/AbstractBufferedPipeline.h
deleted file mode 100755
index 99998ba290..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/AbstractBufferedPipeline.h
+++ /dev/null
@@ -1,70 +0,0 @@
-//
-// Created by saki on 15/11/05.
-//
-
-#ifndef PUPILMOBILE_ABSTRACTBUFFEREDPIPELINE_H
-#define PUPILMOBILE_ABSTRACTBUFFEREDPIPELINE_H
-
-#include
-#include
-#include
-#include "Mutex.h"
-#include "Condition.h"
-
-#include "libUVCCamera.h"
-#include "IPipeline.h"
-
-#pragma interface
-
-#define DEFAULT_INIT_FRAME_POOL_SZ 2
-#define DEFAULT_MAX_FRAME_NUM 8
-
-using namespace android;
-
-class AbstractBufferedPipeline;
-
-class AbstractBufferedPipeline : virtual public IPipeline {
-private:
- const uint32_t max_buffer_num;
- const uint32_t init_pool_num;
- const bool drop_frames;
- volatile uint32_t total_frame_num;
-
-// frame buffer pool to improve performance by reducing memory allocation
- mutable Mutex pool_mutex;
- Condition pool_sync;
- std::list frame_pool;
-// frame buffers
- pthread_t handler_thread;
- mutable Mutex buffer_mutex;
- Condition buffer_sync;
- std::list frame_buffers;
- static void *handler_thread_func(void *vptr_args);
-
-protected:
-// frame buffer pool
- uvc_frame_t *get_frame(const size_t &data_bytes);
- void recycle_frame(uvc_frame_t *frame);
- void init_pool(const size_t &data_bytes);
- void clear_pool();
-// frame buffers
- void clear_frames();
- int add_frame(uvc_frame_t *frame);
- uvc_frame_t *wait_frame();
- uint32_t get_frame_count();
- virtual void do_loop();
- virtual void on_start() = 0;
- virtual void on_stop() = 0;
- virtual int handle_frame(uvc_frame_t *frame) = 0;
-public:
- AbstractBufferedPipeline(const int &_max_buffer_num = DEFAULT_MAX_FRAME_NUM, const int &init_pool_num = DEFAULT_INIT_FRAME_POOL_SZ,
- const size_t &default_frame_size = DEFAULT_FRAME_SZ, const bool &drop_frames_when_buffer_empty = true);
- virtual ~AbstractBufferedPipeline();
- virtual int release();
- virtual int start();
- virtual int stop();
- virtual int queueFrame(uvc_frame_t *frame);
-};
-
-
-#endif //PUPILMOBILE_ABSTRACTBUFFEREDPIPELINE_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CallbackPipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CallbackPipeline.cpp
deleted file mode 100755
index 551f0b6d24..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CallbackPipeline.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-//
-// Created by saki on 15/11/07.
-//
-
-#if 1 // set 1 if you don't need debug message
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // ignore LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG // depends on definition in Android.mk and Application.mk
-#endif
-
-#include "utilbase.h"
-#include "common_utils.h"
-#include "libUVCCamera.h"
-
-#include "pipeline_helper.h"
-#include "IPipeline.h"
-#include "CallbackPipeline.h"
-
-#define INIT_FRAME_POOL_SZ 2
-#define MAX_FRAME_NUM 8
-
-CallbackPipeline::CallbackPipeline(const size_t &_data_bytes)
-: CaptureBasePipeline(MAX_FRAME_NUM, INIT_FRAME_POOL_SZ, _data_bytes),
- mFrameCallbackFunc(NULL),
- callbackPixelBytes(0)
-{
- ENTER();
-
- setState(PIPELINE_STATE_INITIALIZED);
-
- EXIT();
-}
-
-CallbackPipeline::~CallbackPipeline() {
- ENTER();
-
- EXIT();
-}
-
-int CallbackPipeline::setFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format) {
-
- ENTER();
- Mutex::Autolock lock(capture_mutex);
-
- if (isRunning() && isCapturing()) {
- mIsCapturing = false;
- if (mFrameCallbackObj) {
- capture_sync.signal();
- capture_sync.wait(capture_mutex); // wait finishing capturing
- }
- }
- if (!env->IsSameObject(mFrameCallbackObj, frame_callback_obj)) {
- iframecallback_fields.onFrame = NULL;
- if (mFrameCallbackObj) {
- env->DeleteGlobalRef(mFrameCallbackObj);
- }
- mFrameCallbackObj = frame_callback_obj;
- if (frame_callback_obj) {
- // get method IDs of Java object for callback
- jclass clazz = env->GetObjectClass(frame_callback_obj);
- if (LIKELY(clazz)) {
- iframecallback_fields.onFrame = env->GetMethodID(clazz,
- "onFrame", "(Ljava/nio/ByteBuffer;)V");
- } else {
- LOGW("failed to get object class");
- }
- env->ExceptionClear();
- if (!iframecallback_fields.onFrame) {
- LOGE("Can't find IFrameCallback#onFrame");
- env->DeleteGlobalRef(frame_callback_obj);
- mFrameCallbackObj = frame_callback_obj = NULL;
- }
- }
- }
- if (frame_callback_obj) {
- mPixelFormat = pixel_format;
- }
- RETURN(0, int);
-}
-
-void CallbackPipeline::callbackPixelFormatChanged(const uint32_t &width, const uint32_t &height) {
- mFrameCallbackFunc = NULL;
- const size_t sz = width * height;
- switch (mPixelFormat) {
- case PIXEL_FORMAT_RAW:
- LOGI("PIXEL_FORMAT_RAW:");
- callbackPixelBytes = sz * 2;
- break;
- case PIXEL_FORMAT_YUV:
- LOGI("PIXEL_FORMAT_YUV:");
- mFrameCallbackFunc = uvc_any2yuyv;
- callbackPixelBytes = sz * 2;
- break;
- case PIXEL_FORMAT_RGB565:
- LOGI("PIXEL_FORMAT_RGB565:");
- mFrameCallbackFunc = uvc_any2rgb565;
- callbackPixelBytes = sz * 2;
- break;
- case PIXEL_FORMAT_RGBX:
- LOGI("PIXEL_FORMAT_RGBX:");
- mFrameCallbackFunc = uvc_any2rgbx;
- callbackPixelBytes = sz * 4;
- break;
- case PIXEL_FORMAT_YUV20SP:
- LOGI("PIXEL_FORMAT_YUV20SP:");
- mFrameCallbackFunc = uvc_any2yuv420SP;
- callbackPixelBytes = (sz * 3) / 2;
- break;
- case PIXEL_FORMAT_NV21:
- LOGI("PIXEL_FORMAT_NV21:");
- mFrameCallbackFunc = uvc_any2iyuv420SP;
- callbackPixelBytes = (sz * 3) / 2;
- break;
- }
-}
-
-void CallbackPipeline::do_capture(JNIEnv *env) {
- ENTER();
-
- uvc_frame_t *frame;
- uvc_frame_t *temp = get_frame(default_frame_size);
- uvc_frame_t *callback_frame;
- uint32_t width = 0, height = 0;
- size_t sz = default_frame_size;
-
- if (LIKELY(temp)) {
- for (; isRunning() && isCapturing();) {
- frame = waitCaptureFrame();
- if ((LIKELY(frame))) {
- if (UNLIKELY((width != frame->width) || (height != frame->height))) {
- width = frame->width;
- height = frame->height;
- callbackPixelFormatChanged(width, height);
- uvc_ensure_frame_size(temp, callbackPixelBytes);
- sz = callbackPixelBytes;
- }
- if (mFrameCallbackObj) {
- callback_frame = frame;
- sz = frame->actual_bytes;
- if (mFrameCallbackFunc) {
- callback_frame = temp;
- sz = callbackPixelBytes;
- int b = mFrameCallbackFunc(frame, temp);
- if (UNLIKELY(b)) {
- LOGW("failed to convert to callback frame");
- goto SKIP;
- }
- }
- jobject buf = env->NewDirectByteBuffer(callback_frame->data, callbackPixelBytes);
- env->CallVoidMethod(mFrameCallbackObj, iframecallback_fields.onFrame, buf);
- env->ExceptionClear();
- env->DeleteLocalRef(buf);
- }
-SKIP:
- recycle_frame(frame);
- }
- }
- recycle_frame(temp);
- }
-
- EXIT();
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static ID_TYPE nativeCreate(JNIEnv *env, jobject thiz) {
-
- ENTER();
- CallbackPipeline *pipeline = new CallbackPipeline();
- setField_long(env, thiz, "mNativePtr", reinterpret_cast(pipeline));
- RETURN(reinterpret_cast(pipeline), ID_TYPE);
-}
-
-static void nativeDestroy(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- setField_long(env, thiz, "mNativePtr", 0);
- CallbackPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- pipeline->release();
- SAFE_DELETE(pipeline);
- }
- EXIT();
-}
-
-static jint nativeGetState(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- jint result = 0;
- CallbackPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- result = pipeline->getState();
- }
- RETURN(result, jint);
-}
-
-static jint nativeSetPipeline(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject pipeline_obj) {
-
- ENTER();
- jint result = JNI_ERR;
- CallbackPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- IPipeline *pipeline = getPipeline(env, pipeline_obj);
- result = pipeline->setPipeline(pipeline);
- }
-
- RETURN(result, jint);
-}
-
-static jint nativeStart(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
-
- int result = JNI_ERR;
- CallbackPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->start();
- }
- RETURN(result, jint);
-}
-
-static jint nativeStop(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- jint result = JNI_ERR;
- ENTER();
- CallbackPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->stop();
- }
- RETURN(result, jint);
-}
-
-static jint nativeSetFrameCallback(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject jIFrameCallback, jint pixel_format) {
-
- jint result = JNI_ERR;
- ENTER();
- CallbackPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- jobject frame_callback_obj = env->NewGlobalRef(jIFrameCallback);
- result = pipeline->setFrameCallback(env, frame_callback_obj, pixel_format);
- }
- RETURN(result, jint);
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static JNINativeMethod methods[] = {
- { "nativeCreate", "()J", (void *) nativeCreate },
- { "nativeDestroy", "(J)V", (void *) nativeDestroy },
-
- { "nativeGetState", "(J)I", (void *) nativeGetState },
- { "nativeSetPipeline", "(JLcom/serenegiant/usb/IPipeline;)I", (void *) nativeSetPipeline },
-
- { "nativeStart", "(J)I", (void *) nativeStart },
- { "nativeStop", "(J)I", (void *) nativeStop },
-
- { "nativeSetFrameCallback", "(JLcom/serenegiant/usb/IFrameCallback;I)I", (void *) nativeSetFrameCallback },
-};
-
-int register_callback_pipeline(JNIEnv *env) {
- LOGV("register_callback_pipeline:");
- if (registerNativeMethods(env,
- "com/serenegiant/usb/FrameCallbackPipeline",
- methods, NUM_ARRAY_ELEMENTS(methods)) < 0) {
- return -1;
- }
- return 0;
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CallbackPipeline.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CallbackPipeline.h
deleted file mode 100755
index 59c288c3c9..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CallbackPipeline.h
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// Created by saki on 15/11/07.
-//
-
-#ifndef PUPILMOBILE_CALLBACKPIPELINE_H
-#define PUPILMOBILE_CALLBACKPIPELINE_H
-
-#include "libUVCCamera.h"
-#include "CaptureBasePipeline.h"
-
-class CallbackPipeline : virtual public CaptureBasePipeline {
-private:
- jobject mFrameCallbackObj;
- convFunc_t mFrameCallbackFunc;
- Fields_iframecallback iframecallback_fields;
- int mPixelFormat;
- size_t callbackPixelBytes;
- void callbackPixelFormatChanged(const uint32_t &width, const uint32_t &height);
-protected:
- virtual void do_capture(JNIEnv *env);
-public:
- CallbackPipeline(const size_t &_data_bytes = DEFAULT_FRAME_SZ);
- virtual ~CallbackPipeline();
- int setFrameCallback(JNIEnv *env, jobject frame_callback_obj, int pixel_format);
-};
-
-#endif //PUPILMOBILE_CALLBACKPIPELINE_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CaptureBasePipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CaptureBasePipeline.cpp
deleted file mode 100755
index 5d6cea454c..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CaptureBasePipeline.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-//
-// Created by saki on 15/11/07.
-//
-
-#if 1 // set 1 if you don't need debug message
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // ignore LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG // depends on definition in Android.mk and Application.mk
-#endif
-
-#include "utilbase.h"
-#include "common_utils.h"
-
-#include "CaptureBasePipeline.h"
-
-#define INIT_FRAME_POOL_SZ 2
-#define MAX_FRAME_NUM 8
-
-CaptureBasePipeline::CaptureBasePipeline(const size_t &_data_bytes)
-: AbstractBufferedPipeline(MAX_FRAME_NUM, INIT_FRAME_POOL_SZ, _data_bytes),
- mIsCapturing(false),
- captureQueue(NULL),
- frameWidth(0),
- frameHeight(0)
-{
- ENTER();
-
- EXIT();
-}
-
-CaptureBasePipeline::CaptureBasePipeline(const int &_max_buffer_num, const int &init_pool_num, const size_t &default_frame_size)
-: AbstractBufferedPipeline(_max_buffer_num, init_pool_num, default_frame_size),
- mIsCapturing(false),
- captureQueue(NULL),
- frameWidth(0),
- frameHeight(0)
-{
- ENTER();
-
- EXIT();
-}
-
-
-CaptureBasePipeline::~CaptureBasePipeline() {
- ENTER();
-
- clearCaptureFrame();
-
- EXIT();
-}
-
-//********************************************************************************
-//
-//********************************************************************************
-const bool CaptureBasePipeline::isCapturing() const { return mIsCapturing; }
-
-/**
- * clear frame data for capturing
- */
-void CaptureBasePipeline::clearCaptureFrame() {
- Mutex::Autolock lock(capture_mutex);
-
- if (captureQueue)
- recycle_frame(captureQueue);
- captureQueue = NULL;
-}
-
-void CaptureBasePipeline::addCaptureFrame(uvc_frame_t *frame) {
-// ENTER();
-
- Mutex::Autolock lock(capture_mutex);
-
- // keep only latest one
- if (captureQueue) {
- recycle_frame(captureQueue);
- captureQueue = NULL;
- }
- if (LIKELY(isRunning())) {
- captureQueue = frame;
- capture_sync.signal();
- } else {
- recycle_frame(frame);
- }
-
-// EXIT();
-}
-
-/**
- * get frame data for capturing, if not exist, block and wait
- */
-uvc_frame_t *CaptureBasePipeline::waitCaptureFrame() {
- uvc_frame_t *frame = NULL;
- Mutex::Autolock lock(capture_mutex);
-
- if (!captureQueue) {
- capture_sync.wait(capture_mutex);
- }
- if (LIKELY(isRunning() && captureQueue)) {
- frame = captureQueue;
- captureQueue = NULL;
- }
- return frame;
-}
-
-/* override protected */
-void CaptureBasePipeline::on_start() {
- ENTER();
-
- mIsCapturing = true;
- pthread_create(&capture_thread, NULL, capture_thread_func, (void *)this);
-
- EXIT();
-}
-
-/* override protected */
-void CaptureBasePipeline::on_stop() {
- ENTER();
-
- mIsCapturing = false;
- capture_sync.broadcast();
- if (pthread_join(capture_thread, NULL) != EXIT_SUCCESS) {
- LOGW("UVCCameraControl::terminate capture thread: pthread_join failed");
- }
- clearCaptureFrame();
-
- EXIT();
-}
-
-/* override protected */
-int CaptureBasePipeline::handle_frame(uvc_frame_t *frame) {
-// ENTER();
-
- if (LIKELY(frame)) {
- // get empty frame from frame pool
- uvc_frame_t *copy = get_frame(frame->data_bytes);
- if (LIKELY(copy)) {
- // duplicate frame buffer and pass copy
- uvc_error_t ret = uvc_duplicate_frame(frame, copy);
- if (LIKELY(!ret)) {
- addCaptureFrame(copy);
- } else {
- LOGW("uvc_duplicate_frame failed:%d", ret);
- recycle_frame(copy);
- }
- } else {
- LOGW("buffer pool is empty and exceeds the limit, drop frame");
- }
- }
-
- return 0; // RETURN(0, int);
-}
-
-/*
- * thread function
- * @param vptr_args pointer to UVCCameraControl instance
- */
-// static
-void *CaptureBasePipeline::capture_thread_func(void *vptr_args) {
-
- ENTER();
-
- CaptureBasePipeline *pipeline = reinterpret_cast(vptr_args);
- if (LIKELY(pipeline)) {
- JavaVM *vm = getVM();
- JNIEnv *env;
- // attach to JavaVM
- vm->AttachCurrentThread(&env, NULL);
- pipeline->internal_do_capture(env); // never return until finish streaming
- // detach from JavaVM
- vm->DetachCurrentThread();
- MARK("DetachCurrentThread");
- }
-
- PRE_EXIT();
- pthread_exit(NULL);
-}
-
-/**
- * the actual function for capturing
- */
-void CaptureBasePipeline::internal_do_capture(JNIEnv *env) {
-
- ENTER();
-
- clearCaptureFrame();
- for (; isRunning() ;) {
- mIsCapturing = true;
- do_capture(env);
- capture_sync.broadcast();
- } // end of for (; isRunning() ;)
-
- EXIT();
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CaptureBasePipeline.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CaptureBasePipeline.h
deleted file mode 100755
index 6607c3d76f..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/CaptureBasePipeline.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Created by saki on 15/11/07.
-//
-
-#ifndef PUPILMOBILE_CAPTUREBASEPIPELINE_H
-#define PUPILMOBILE_CAPTUREBASEPIPELINE_H
-
-#include "Mutex.h"
-#include "Condition.h"
-
-#include "libUVCCamera.h"
-#include "AbstractBufferedPipeline.h"
-
-using namespace android;
-
-class CaptureBasePipeline : virtual public AbstractBufferedPipeline {
-private:
- static void *capture_thread_func(void *vptr_args);
- void internal_do_capture(JNIEnv *env);
-protected:
- volatile bool mIsCapturing;
- mutable Mutex capture_mutex;
- Condition capture_sync;
- pthread_t capture_thread;
- uvc_frame_t *captureQueue; // keep latest one frame only
- uint32_t frameWidth;
- uint32_t frameHeight;
- void clearCaptureFrame();
- void addCaptureFrame(uvc_frame_t *frame);
- uvc_frame_t *waitCaptureFrame();
-
- virtual void on_start();
- virtual void on_stop();
- virtual int handle_frame(uvc_frame_t *frame);
- virtual void do_capture(JNIEnv *env) = 0;
-public:
- CaptureBasePipeline(const size_t &_data_bytes = DEFAULT_FRAME_SZ);
- CaptureBasePipeline(const int &_max_buffer_num = DEFAULT_MAX_FRAME_NUM, const int &init_pool_num = DEFAULT_INIT_FRAME_POOL_SZ, const size_t &default_frame_size = DEFAULT_FRAME_SZ);
- virtual ~CaptureBasePipeline();
- const bool isCapturing() const;
-};
-
-
-#endif //PUPILMOBILE_CAPTUREBASEPIPELINE_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/ConvertPipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/ConvertPipeline.cpp
deleted file mode 100755
index c5a8d15c93..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/ConvertPipeline.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-//
-// Created by saki on 15/11/05.
-//
-
-#if 1 // set 1 if you don't need debug message
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // ignore LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG // depends on definition in Android.mk and Application.mk
-#endif
-
-#include "utilbase.h"
-#include "common_utils.h"
-
-#include "libUVCCamera.h"
-#include "pipeline_helper.h"
-#include "IPipeline.h"
-#include "ConvertPipeline.h"
-
-#define INIT_FRAME_POOL_SZ 2
-#define MAX_FRAME_NUM 8
-
-/* public */
-ConvertPipeline::ConvertPipeline(const size_t &_data_bytes, const int &_target_pixel_format)
-: AbstractBufferedPipeline(MAX_FRAME_NUM, INIT_FRAME_POOL_SZ, _data_bytes),
- target_pixel_format(_target_pixel_format),
- mFrameConvFunc(NULL)
-{
- ENTER();
-
- updateConvFunc();
- setState(PIPELINE_STATE_INITIALIZED);
-
- EXIT();
-}
-
-/* public */
-ConvertPipeline::~ConvertPipeline() {
- ENTER();
-
- EXIT();
-}
-
-void ConvertPipeline::updateConvFunc() {
- ENTER();
-
- Mutex::Autolock lock(pipeline_mutex);
- mFrameConvFunc = NULL;
- switch (target_pixel_format) {
- case PIXEL_FORMAT_RAW:
- LOGI("PIXEL_FORMAT_RAW:");
- break;
- case PIXEL_FORMAT_YUV:
- LOGI("PIXEL_FORMAT_YUV:");
- mFrameConvFunc = uvc_any2yuyv;
- break;
- case PIXEL_FORMAT_RGB565:
- LOGI("PIXEL_FORMAT_RGB565:");
- mFrameConvFunc = uvc_any2rgb565;
- break;
- case PIXEL_FORMAT_RGBX:
- LOGI("PIXEL_FORMAT_RGBX:");
- mFrameConvFunc = uvc_any2rgbx;
- break;
- case PIXEL_FORMAT_YUV20SP:
- LOGI("PIXEL_FORMAT_YUV20SP:");
- mFrameConvFunc = uvc_any2yuv420SP;
- break;
- case PIXEL_FORMAT_NV21:
- LOGI("PIXEL_FORMAT_NV21:");
- mFrameConvFunc = uvc_any2iyuv420SP;
- break;
- }
-
- EXIT();
-};
-
-void ConvertPipeline::on_start() {
- ENTER();
-
- updateConvFunc();
-
- EXIT();
-}
-
-void ConvertPipeline::on_stop() {
- ENTER();
-
- EXIT();
-}
-
-int ConvertPipeline::handle_frame(uvc_frame_t *frame) {
- ENTER();
-
- Mutex::Autolock lock(pipeline_mutex);
-
- if (next_pipeline) {
- uvc_frame_t *copy = frame;
- if (mFrameConvFunc) {
- copy = get_frame(frame->actual_bytes);
- if (LIKELY(copy)) {
- const uvc_error_t r = mFrameConvFunc(frame, copy);
- if (UNLIKELY(r)) {
- LOGW("failed to convert:%d", r);
- recycle_frame(copy);
- copy = frame;
- }
- }
- }
- next_pipeline->queueFrame(copy);
- }
-
- RETURN(1, int);
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static ID_TYPE nativeCreate(JNIEnv *env, jobject thiz, jint pixel_format) {
-
- ENTER();
- ConvertPipeline *pipeline = new ConvertPipeline(DEFAULT_FRAME_SZ, pixel_format);
- setField_long(env, thiz, "mNativePtr", reinterpret_cast(pipeline));
- RETURN(reinterpret_cast(pipeline), ID_TYPE);
-}
-
-static void nativeDestroy(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- setField_long(env, thiz, "mNativePtr", 0);
- ConvertPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- pipeline->release();
- SAFE_DELETE(pipeline);
- }
- EXIT();
-}
-
-static jint nativeGetState(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- jint result = 0;
- ConvertPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- result = pipeline->getState();
- }
- RETURN(result, jint);
-}
-
-static jint nativeSetPipeline(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject pipeline_obj) {
-
- ENTER();
- jint result = JNI_ERR;
- ConvertPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- IPipeline *target_pipeline = getPipeline(env, pipeline_obj);
- result = pipeline->setPipeline(target_pipeline);
- }
-
- RETURN(result, jint);
-}
-
-static jint nativeStart(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
-
- int result = JNI_ERR;
- ConvertPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->start();
- }
- RETURN(result, jint);
-}
-
-static jint nativeStop(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- jint result = JNI_ERR;
- ENTER();
- ConvertPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->stop();
- }
- RETURN(result, jint);
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static JNINativeMethod methods[] = {
- { "nativeCreate", "(I)J", (void *) nativeCreate },
- { "nativeDestroy", "(J)V", (void *) nativeDestroy },
-
- { "nativeGetState", "(J)I", (void *) nativeGetState },
- { "nativeSetPipeline", "(JLcom/serenegiant/usb/IPipeline;)I", (void *) nativeSetPipeline },
-
- { "nativeStart", "(J)I", (void *) nativeStart },
- { "nativeStop", "(J)I", (void *) nativeStop },
-
-};
-
-int register_convert_pipeline(JNIEnv *env) {
- LOGV("register_convert_pipeline:");
- if (registerNativeMethods(env,
- "com/serenegiant/usb/ConvertPipeline",
- methods, NUM_ARRAY_ELEMENTS(methods)) < 0) {
- return -1;
- }
- return 0;
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/ConvertPipeline.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/ConvertPipeline.h
deleted file mode 100755
index 150fe2a2da..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/ConvertPipeline.h
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// Created by saki on 15/11/05.
-//
-
-#ifndef PUPILMOBILE_CONVERTPIPELINE_H
-#define PUPILMOBILE_CONVERTPIPELINE_H
-
-#include "libUVCCamera.h"
-#include "AbstractBufferedPipeline.h"
-
-class ConvertPipeline : virtual public AbstractBufferedPipeline {
-private:
- const int target_pixel_format;
- convFunc_t mFrameConvFunc;
- void updateConvFunc();
-protected:
- virtual void on_start();
- virtual void on_stop();
- virtual int handle_frame(uvc_frame_t *frame);
-public:
- ConvertPipeline(const size_t &_data_bytes, const int &target_pixel_format = PIXEL_FORMAT_RAW);
- virtual ~ConvertPipeline();
-};
-
-
-#endif //PUPILMOBILE_CONVERTPIPELINE_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/DistributePipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/DistributePipeline.cpp
deleted file mode 100755
index 26cd8d34b2..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/DistributePipeline.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-//
-// Created by saki on 15/11/25.
-//
-
-#include "utilbase.h"
-#include "common_utils.h"
-#include "libUVCCamera.h"
-
-#include "pipeline_helper.h"
-#include "IPipeline.h"
-#include "DistributePipeline.h"
-
-DistributePipeline::DistributePipeline(const int &_max_buffer_num, const int &init_pool_num,
- const size_t &default_frame_size, const bool &drop_frames_when_buffer_empty)
-: AbstractBufferedPipeline(_max_buffer_num, init_pool_num, default_frame_size, drop_frames_when_buffer_empty)
-{
- ENTER();
-
- setState(PIPELINE_STATE_INITIALIZED);
-
- EXIT();
-}
-
-DistributePipeline::~DistributePipeline() {
- ENTER();
-
- Mutex::Autolock lock(pipeline_mutex);
-
- pipelines.clear();
-
- EXIT();
-}
-
-void DistributePipeline::on_start() {
- ENTER();
- EXIT();
-}
-
-void DistributePipeline::on_stop() {
- ENTER();
- EXIT();
-}
-
-int DistributePipeline::handle_frame(uvc_frame_t *frame) {
- ENTER();
-
- Mutex::Autolock lock(pipeline_mutex);
-
- for (auto iter = pipelines.begin(); iter != pipelines.end(); iter++) {
- (*iter)->queueFrame(frame);
- }
-
- RETURN(0, int);
-}
-
-int DistributePipeline::addPipeline(IPipeline *pipeline) {
- ENTER();
-
- if (pipeline) {
- Mutex::Autolock lock(pipeline_mutex);
- pipelines.push_back(pipeline);
- }
-
- RETURN(0, int);
-}
-
-int DistributePipeline::removePipeline(IPipeline *pipeline) {
- ENTER();
-
- if (pipeline) {
- Mutex::Autolock lock(pipeline_mutex);
-
- for (auto iter = pipelines.begin(); iter != pipelines.end(); ) {
- if (*iter == pipeline) {
- iter = pipelines.erase(iter);
- } else {
- iter++;
- }
- }
- }
-
- RETURN(0, int);
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static ID_TYPE nativeCreate(JNIEnv *env, jobject thiz) {
-
- ENTER();
- DistributePipeline *pipeline = new DistributePipeline();
- setField_long(env, thiz, "mNativePtr", reinterpret_cast(pipeline));
- RETURN(reinterpret_cast(pipeline), ID_TYPE);
-}
-
-static void nativeDestroy(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- setField_long(env, thiz, "mNativePtr", 0);
- DistributePipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- pipeline->release();
- SAFE_DELETE(pipeline);
- }
- EXIT();
-}
-
-static jint nativeGetState(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- jint result = 0;
- DistributePipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- result = pipeline->getState();
- }
- RETURN(result, jint);
-}
-
-static jint nativeSetPipeline(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject pipeline_obj) {
-
- ENTER();
- jint result = JNI_ERR;
- DistributePipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- IPipeline *target_pipeline = getPipeline(env, pipeline_obj);
- result = pipeline->setPipeline(target_pipeline);
- }
-
- RETURN(result, jint);
-}
-
-static jint nativeAddPipeline(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject pipeline_obj) {
-
- ENTER();
- jint result = JNI_ERR;
- DistributePipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- IPipeline *target_pipeline = getPipeline(env, pipeline_obj);
- result = pipeline->addPipeline(target_pipeline);
- }
-
- RETURN(result, jint);
-}
-
-static jint nativeRemovePipeline(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject pipeline_obj) {
-
- ENTER();
- jint result = JNI_ERR;
- DistributePipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- IPipeline *target_pipeline = getPipeline(env, pipeline_obj);
- result = pipeline->removePipeline(target_pipeline);
- }
-
- RETURN(result, jint);
-}
-
-static jint nativeStart(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
-
- int result = JNI_ERR;
- DistributePipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->start();
- }
- RETURN(result, jint);
-}
-
-static jint nativeStop(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- jint result = JNI_ERR;
- ENTER();
- DistributePipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->stop();
- }
- RETURN(result, jint);
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static JNINativeMethod methods[] = {
- { "nativeCreate", "()J", (void *) nativeCreate },
- { "nativeDestroy", "(J)V", (void *) nativeDestroy },
-
- { "nativeGetState", "(J)I", (void *) nativeGetState },
- { "nativeSetPipeline", "(JLcom/serenegiant/usb/IPipeline;)I", (void *) nativeSetPipeline },
- { "nativeAddPipeline", "(JLcom/serenegiant/usb/IPipeline;)I", (void *) nativeAddPipeline },
- { "nativeRemovePipeline", "(JLcom/serenegiant/usb/IPipeline;)I", (void *) nativeRemovePipeline },
-
- { "nativeStart", "(J)I", (void *) nativeStart },
- { "nativeStop", "(J)I", (void *) nativeStop },
-};
-
-int register_distribute_pipeline(JNIEnv *env) {
- LOGV("register_distribute_pipeline:");
- if (registerNativeMethods(env,
- "com/serenegiant/usb/DistributePipeline",
- methods, NUM_ARRAY_ELEMENTS(methods)) < 0) {
- return -1;
- }
- return 0;
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/DistributePipeline.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/DistributePipeline.h
deleted file mode 100755
index b3f6d1d203..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/DistributePipeline.h
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// Created by saki on 15/11/25.
-//
-
-#ifndef PUPILMOBILE_DISTRIBUTEPIPELINE_H
-#define PUPILMOBILE_DISTRIBUTEPIPELINE_H
-
-#include "AbstractBufferedPipeline.h"
-
-#pragma interface
-
-class DistributePipeline : virtual public AbstractBufferedPipeline {
-private:
- std::list pipelines;
-protected:
- virtual void on_start();
- virtual void on_stop();
- virtual int handle_frame(uvc_frame_t *frame);
-public:
- DistributePipeline(const int &_max_buffer_num = DEFAULT_MAX_FRAME_NUM, const int &init_pool_num = DEFAULT_INIT_FRAME_POOL_SZ,
- const size_t &default_frame_size = DEFAULT_FRAME_SZ, const bool &drop_frames_when_buffer_empty = true);
- virtual ~DistributePipeline();
- virtual int addPipeline(IPipeline *pipeline);
- virtual int removePipeline(IPipeline *pipeline);
-};
-
-#endif //PUPILMOBILE_DISTRIBUTEPIPELINE_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/IPipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/IPipeline.cpp
deleted file mode 100755
index 823caf6601..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/IPipeline.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-// Created by saki on 15/11/05.
-//
-
-#if 1 // set 1 if you don't need debug message
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // ignore LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG // depends on definition in Android.mk and Application.mk
-#endif
-
-#include
-
-#include "utilbase.h"
-#include "common_utils.h"
-
-#include "libUVCCamera.h"
-#include "pipeline_helper.h"
-#include "IPipeline.h"
-
-/*public*/
-IPipeline::IPipeline(const size_t &_default_frame_size)
-: state(PIPELINE_STATE_UNINITIALIZED),
- mIsRunning(false),
- default_frame_size(_default_frame_size),
- next_pipeline(NULL)
-{
- ENTER();
-
- EXIT();
-}
-
-/*public*/
-IPipeline::~IPipeline() {
- ENTER();
-
- EXIT();
-}
-
-/*public*/
-const bool IPipeline::isRunning() const { return (mIsRunning); };
-
-/*public*/
-const pipeline_state_t IPipeline::getState() const { return (state); };
-
-/*protected*/
-void IPipeline::setState(const pipeline_state_t &new_state) { state = new_state; }
-
-/*public*/
-int IPipeline::setPipeline(IPipeline *pipeline) {
- ENTER();
-
- Mutex::Autolock lock(pipeline_mutex);
-
- // XXX do I need to delete next_pipeline if it is not NULL?
- next_pipeline = pipeline;
-
- RETURN(0, int);
-}
-
-/**
- * set frame to next_pipeline
- * if you don't need this, override this function
- */
-int IPipeline::chain_frame(uvc_frame_t *frame) {
- ENTER();
-
- int result = -1;
- Mutex::Autolock lock(pipeline_mutex);
-
- if (next_pipeline) {
- next_pipeline->queueFrame(frame);
- result = 0;
- }
-
- RETURN(result, int);
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/IPipeline.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/IPipeline.h
deleted file mode 100755
index 83dfecb85f..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/IPipeline.h
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-// Created by saki on 15/11/05.
-//
-
-#ifndef PUPILMOBILE_IPIPELINE_H
-#define PUPILMOBILE_IPIPELINE_H
-
-#include
-#include
-#include "Mutex.h"
-
-#include "libUVCCamera.h"
-
-#pragma interface
-
-using namespace android;
-
-#define DEFAULT_FRAME_SZ 1024
-
-typedef enum pipeline_type {
- PIPELINE_TYPE_SIMPLE_BUFFERED = 0,
- PIPELINE_TYPE_SQLITE_BUFFERED = 10,
- PIPELINE_TYPE_UVC_CONTROL = 100,
- PIPELINE_TYPE_CALLBACK = 200,
- PIPELINE_TYPE_CONVERT = 300,
- PIPELINE_TYPE_PREVIEW = 400,
- PIPELINE_TYPE_PUBLISHER = 500,
- PIPELINE_TYPE_DISTRIBUTE = 600,
-} pipeline_type_t;
-
-typedef enum _pipeline_state {
- PIPELINE_STATE_UNINITIALIZED = 0,
- PIPELINE_STATE_RELEASING = 10,
- PIPELINE_STATE_INITIALIZED = 20,
- PIPELINE_STATE_STARTING = 30,
- PIPELINE_STATE_RUNNING = 40,
- PIPELINE_STATE_STOPPING = 50,
-} pipeline_state_t;
-
-class IPipeline;
-
-class IPipeline {
-private:
- volatile pipeline_state_t state;
- // force inhibiting copy/assignment
- IPipeline(const IPipeline &src);
- void operator =(const IPipeline &src);
-protected:
- volatile bool mIsRunning;
- const size_t default_frame_size;
- mutable Mutex pipeline_mutex;
- IPipeline *next_pipeline;
- void setState(const pipeline_state_t &new_state);
- /**
- * if handle_frame return 0, handler_thread call this function
- * set frame to next pipeline
- * this may block caller thread while the pipeline is full
- * @return 0: success queueing, other: failed
- */
- virtual int chain_frame(uvc_frame_t *frame);
-public:
- IPipeline(const size_t &default_frame_size = DEFAULT_FRAME_SZ);
- virtual ~IPipeline();
- const pipeline_state_t getState() const;
- const bool isRunning() const;
- virtual int setPipeline(IPipeline *pipeline);
- virtual int release() { return 0; };
- virtual int start() { return 0; };
- virtual int stop() { return 0; };
- virtual int queueFrame(uvc_frame_t *frame) = 0;
-};
-
-
-#endif //PUPILMOBILE_IPIPELINE_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PreviewPipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PreviewPipeline.cpp
deleted file mode 100755
index f67e3334d3..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PreviewPipeline.cpp
+++ /dev/null
@@ -1,326 +0,0 @@
-//
-// Created by saki on 15/11/06.
-//
-
-#if 1 // set 1 if you don't need debug message
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // ignore LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG // depends on definition in Android.mk and Application.mk
-#endif
-
-#include
-
-#include "utilbase.h"
-#include "common_utils.h"
-
-#include "libUVCCamera.h"
-#include "pipeline_helper.h"
-#include "IPipeline.h"
-#include "PreviewPipeline.h"
-
-#define INIT_FRAME_POOL_SZ 2
-#define MAX_FRAME_NUM 32 // max approx. 1sec
-
-#define CAPTURE_PIXEL_BYTES 2 // RGB565
-
-PreviewPipeline::PreviewPipeline(const size_t &_data_bytes)
-: CaptureBasePipeline(MAX_FRAME_NUM, INIT_FRAME_POOL_SZ, _data_bytes),
- mCaptureWindow(NULL)
-{
- ENTER();
-
- setState(PIPELINE_STATE_INITIALIZED);
-
- EXIT();
-}
-
-PreviewPipeline::~PreviewPipeline() {
- ENTER();
-
- if (mCaptureWindow) {
- ANativeWindow_release(mCaptureWindow);
- }
- mCaptureWindow = NULL;
- clearCaptureFrame();
-
- EXIT();
-}
-
-//********************************************************************************
-//
-//********************************************************************************
-static void copyFrame(const uint8_t *src, uint8_t *dest, const int width, int height, const int stride_src, const int stride_dest) {
- const int h8 = height % 8;
- for (int i = 0; i < h8; i++) {
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- }
- for (int i = 0; i < height; i += 8) {
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- memcpy(dest, src, width);
- dest += stride_dest; src += stride_src;
- }
-}
-
-
-// transfer specific frame data to the Surface(ANativeWindow)
-static int copyToSurface(uvc_frame_t *frame, ANativeWindow **window) {
-// ENTER();
- int result = 0;
- if (LIKELY(*window)) {
- ANativeWindow_Buffer buffer;
- if (LIKELY(ANativeWindow_lock(*window, &buffer, NULL) == 0)) {
- // source = frame data
- const uint8_t *src = (uint8_t *)frame->data;
- const int src_w = frame->width * CAPTURE_PIXEL_BYTES;
- const int src_step = frame->width * CAPTURE_PIXEL_BYTES;
- // destination = Surface(ANativeWindow)
- uint8_t *dest = (uint8_t *)buffer.bits;
- const int dest_w = buffer.width * CAPTURE_PIXEL_BYTES;
- const int dest_step = buffer.stride * CAPTURE_PIXEL_BYTES;
- // use lower transfer bytes
- const int w = src_w < dest_w ? src_w : dest_w;
- // use lower height
- const int h = frame->height < buffer.height ? frame->height : buffer.height;
- // transfer from frame data to the Surface
- copyFrame(src, dest, w, h, src_step, dest_step);
- ANativeWindow_unlockAndPost(*window);
- } else {
- result = -1;
- }
- } else {
- result = -1;
- }
- return result; //RETURN(result, int);
-}
-
-//********************************************************************************
-//
-//********************************************************************************
-int PreviewPipeline::setCaptureDisplay(ANativeWindow *capture_window) {
- ENTER();
- LOGI("setCaptureDisplay:%p", capture_window);
-
- Mutex::Autolock lock(capture_mutex);
-
- if (isRunning() && isCapturing()) {
- mIsCapturing = false;
- if (mCaptureWindow) {
- LOGD("wait for finishing capture loop");
- capture_sync.broadcast();
- capture_sync.wait(capture_mutex); // wait finishing capturing
- }
- }
- if (mCaptureWindow != capture_window) {
- // release current Surface if already assigned.
- if (UNLIKELY(mCaptureWindow)) {
- LOGD("ANativeWindow_release");
- ANativeWindow_release(mCaptureWindow);
- }
- mCaptureWindow = capture_window;
- }
-
- RETURN(0, int);
-}
-
-/**
- * the actual function for capturing
- */
-void PreviewPipeline::do_capture(JNIEnv *env) {
-
-// ENTER();
-
- uvc_frame_t *frame = NULL;
- uvc_frame_t *rgb565 = get_frame(default_frame_size);
-
- if (LIKELY(rgb565)) {
- for (; isRunning() && isCapturing() ;) {
- frame = waitCaptureFrame();
- if (LIKELY(frame)) {
- if (LIKELY(isCapturing())) {
- const bool need_update_geometry = (frame->width != frameWidth) || (frame->height != frameHeight);
- capture_mutex.lock();
- {
- ANativeWindow *window = mCaptureWindow; // local cache
- if (LIKELY(window)) {
- if (UNLIKELY(need_update_geometry)) {
- frameWidth = frame->width;
- frameHeight = frame->height;
- LOGD("ANativeWindow_setBuffersGeometry:(%dx%d)", frameWidth, frameHeight);
- ANativeWindow_setBuffersGeometry(window,
- frameWidth, frameHeight, WINDOW_FORMAT_RGB_565);
- // if you use Surface came from MediaCodec#createInputSurface
- // you could not change window format at least when you use
- // ANativeWindow_lock / ANativeWindow_unlockAndPost
- // to write frame data to the Surface...you should use RGBX8888 instead
- // So we need check here.
- int32_t window_format = ANativeWindow_getFormat(window);
- if (window_format != WINDOW_FORMAT_RGB_565) {
- LOGE("window format mismatch, cancelled movie capturing.");
- ANativeWindow_release(window);
- window = mCaptureWindow = NULL;
- frameWidth = frameHeight = 0;
- }
- }
- if (LIKELY(window)) {
- int b = uvc_any2rgb565(frame, rgb565);
- if (LIKELY(!b)) {
- copyToSurface(rgb565, &window);
- } else {
- LOGE("failed to convert frame: err=%d", b);
- }
- }
- }
- }
- capture_mutex.unlock();
- }
- recycle_frame(frame);
- }
- }
- }
- if (rgb565) {
- recycle_frame(rgb565);
- }
- capture_mutex.lock();
- {
- if (mCaptureWindow) {
- ANativeWindow_release(mCaptureWindow);
- mCaptureWindow = NULL;
- }
- }
- capture_mutex.unlock();
-// EXIT();
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static ID_TYPE nativeCreate(JNIEnv *env, jobject thiz) {
-
- ENTER();
- PreviewPipeline *pipeline = new PreviewPipeline();
- setField_long(env, thiz, "mNativePtr", reinterpret_cast(pipeline));
- RETURN(reinterpret_cast(pipeline), ID_TYPE);
-}
-
-static void nativeDestroy(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- setField_long(env, thiz, "mNativePtr", 0);
- PreviewPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- pipeline->release();
- SAFE_DELETE(pipeline);
- }
- EXIT();
-}
-
-static jint nativeGetState(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
- jint result = 0;
- PreviewPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- result = pipeline->getState();
- }
- RETURN(result, jint);
-}
-
-static jint nativeSetPipeline(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject pipeline_obj) {
-
- ENTER();
- jint result = JNI_ERR;
- PreviewPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (pipeline) {
- IPipeline *target_pipeline = getPipeline(env, pipeline_obj);
- result = pipeline->setPipeline(target_pipeline);
- }
-
- RETURN(result, jint);
-}
-
-static jint nativeStart(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- ENTER();
-
- int result = JNI_ERR;
- PreviewPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->start();
- }
- RETURN(result, jint);
-}
-
-static jint nativeStop(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline) {
-
- jint result = JNI_ERR;
- ENTER();
- PreviewPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- result = pipeline->stop();
- }
- RETURN(result, jint);
-}
-
-static jint nativeSetCaptureDisplay(JNIEnv *env, jobject thiz,
- ID_TYPE id_pipeline, jobject jSurface) {
-
- jint result = JNI_ERR;
- ENTER();
- PreviewPipeline *pipeline = reinterpret_cast(id_pipeline);
- if (LIKELY(pipeline)) {
- ANativeWindow *capture_window = jSurface ? ANativeWindow_fromSurface(env, jSurface) : NULL;
- result = pipeline->setCaptureDisplay(capture_window);
- }
- RETURN(result, jint);
-}
-
-//**********************************************************************
-//
-//**********************************************************************
-static JNINativeMethod methods[] = {
- { "nativeCreate", "()J", (void *) nativeCreate },
- { "nativeDestroy", "(J)V", (void *) nativeDestroy },
-
- { "nativeGetState", "(J)I", (void *) nativeGetState },
- { "nativeSetPipeline", "(JLcom/serenegiant/usb/IPipeline;)I", (void *) nativeSetPipeline },
-
- { "nativeStart", "(J)I", (void *) nativeStart },
- { "nativeStop", "(J)I", (void *) nativeStop },
-
- { "nativeSetCaptureDisplay", "(JLandroid/view/Surface;)I", (void *) nativeSetCaptureDisplay },
-
-};
-
-int register_preview_pipeline(JNIEnv *env) {
- LOGV("register_preview_pipeline:");
- if (registerNativeMethods(env,
- "com/serenegiant/usb/PreviewPipeline",
- methods, NUM_ARRAY_ELEMENTS(methods)) < 0) {
- return -1;
- }
- return 0;
-}
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PreviewPipeline.h b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PreviewPipeline.h
deleted file mode 100755
index 34beb96d0f..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PreviewPipeline.h
+++ /dev/null
@@ -1,25 +0,0 @@
-//
-// Created by saki on 15/11/06.
-//
-
-#ifndef PUPILMOBILE_PREVIEWPIPELINE_H
-#define PUPILMOBILE_PREVIEWPIPELINE_H
-
-#include
-
-#include "libUVCCamera.h"
-#include "CaptureBasePipeline.h"
-
-class PreviewPipeline : virtual public CaptureBasePipeline {
-private:
- ANativeWindow *mCaptureWindow;
-protected:
- virtual void do_capture(JNIEnv *env);
-public:
- PreviewPipeline(const size_t &_data_bytes = DEFAULT_FRAME_SZ);
- virtual ~PreviewPipeline();
- int setCaptureDisplay(ANativeWindow *capture_window);
-};
-
-
-#endif //PUPILMOBILE_PREVIEWPIPELINE_H
diff --git a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PublisherPipeline.cpp b/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PublisherPipeline.cpp
deleted file mode 100755
index 5b374fdf94..0000000000
--- a/dConnectDevicePlugin/dConnectDeviceUVC/libuvccamera/src/main/jni/UVCCamera/pipeline/PublisherPipeline.cpp
+++ /dev/null
@@ -1,330 +0,0 @@
-//
-// Created by saki on 15/10/06.
-//
-
-#define MEAS_TIME 0
-
-#if 1 // set 1 if you don't need debug message
- #ifndef LOG_NDEBUG
- #define LOG_NDEBUG // ignore LOGV/LOGD/MARK
- #endif
- #undef USE_LOGALL
-#else
- #define USE_LOGALL
- #undef LOG_NDEBUG
- #undef NDEBUG // depends on definition in Android.mk and Application.mk
-#endif
-
-#if MEAS_TIME
-#define MEAS_TIME_INIT nsecs_t _meas_time_ = 0; int _meas_count_ = 0;
-#define MEAS_TIME_START const nsecs_t _meas_t_ = systemTime();
-#define MEAS_TIME_STOP \
- _meas_time_ += (systemTime() - _meas_t_); \
- if UNLIKELY((++_meas_count_ % 100) == 0) { \
- const float d = _meas_time_ / (1000000.f * _meas_count_); \
- LOGI("meas time=%5.2f[msec]", d); \
- }
-#else
-#define MEAS_TIME_INIT
-#define MEAS_TIME_START
-#define MEAS_TIME_STOP
-#endif
-
-#include
-#include
-#include