├── .gitignore
├── LICENSE
├── README.md
├── aars
└── common-4.1.1.aar
├── app-release.apk
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jiangdg
│ │ └── usbcamera
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── .classpath
│ ├── .project
│ ├── .settings
│ │ ├── org.eclipse.core.resources.prefs
│ │ ├── org.eclipse.core.runtime.prefs
│ │ └── org.eclipse.jdt.core.prefs
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── jiangdg
│ │ │ └── usbcamera
│ │ │ ├── application
│ │ │ └── MyApplication.java
│ │ │ ├── utils
│ │ │ └── CrashHandler.java
│ │ │ └── view
│ │ │ ├── SplashActivity.java
│ │ │ └── USBCameraActivity.java
│ ├── project.properties
│ └── res
│ │ ├── layout
│ │ ├── activity_splash.xml
│ │ ├── activity_usbcamera.xml
│ │ └── layout_dialog_list.xml
│ │ ├── menu
│ │ └── main_toobar.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── jiangdg
│ └── usbcamera
│ └── ExampleUnitTest.java
├── bluettoh stone blue
├── build.gradle
├── gifs
├── 2.1.0.gif
├── USBCam.gif
├── brightness.gif
├── detecting.gif
├── download.png
└── resolution.gif
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libusbcamera
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jiangdg
│ │ └── libusbcamera
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── .classpath
│ ├── .project
│ ├── .settings
│ │ └── org.eclipse.jdt.core.prefs
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── zk
│ │ │ └── SIMYOU.ttf
│ ├── java
│ │ ├── com
│ │ │ ├── jiangdg
│ │ │ │ └── usbcamera
│ │ │ │ │ ├── UVCCameraHelper.java
│ │ │ │ │ └── utils
│ │ │ │ │ └── FileUtils.java
│ │ │ └── serenegiant
│ │ │ │ └── usb
│ │ │ │ ├── CameraDialog.java
│ │ │ │ ├── DeviceFilter.java
│ │ │ │ ├── IButtonCallback.java
│ │ │ │ ├── IFrameCallback.java
│ │ │ │ ├── IStatusCallback.java
│ │ │ │ ├── Size.java
│ │ │ │ ├── USBMonitor.java
│ │ │ │ ├── USBVendorId.java
│ │ │ │ ├── UVCCamera.java
│ │ │ │ ├── common
│ │ │ │ ├── AbstractUVCCameraHandler.java
│ │ │ │ ├── UVCCameraHandler.java
│ │ │ │ └── UVCCameraHandlerMultiSurface.java
│ │ │ │ ├── encoder
│ │ │ │ ├── IAudioEncoder.java
│ │ │ │ ├── IVideoEncoder.java
│ │ │ │ ├── MediaAudioEncoder.java
│ │ │ │ ├── MediaEncoder.java
│ │ │ │ ├── MediaMuxerWrapper.java
│ │ │ │ ├── MediaSurfaceEncoder.java
│ │ │ │ ├── MediaVideoBufferEncoder.java
│ │ │ │ ├── MediaVideoEncoder.java
│ │ │ │ ├── RecordParams.java
│ │ │ │ └── biz
│ │ │ │ │ ├── AACEncodeConsumer.java
│ │ │ │ │ ├── H264EncodeConsumer.java
│ │ │ │ │ └── Mp4MediaMuxer.java
│ │ │ │ └── widget
│ │ │ │ ├── AspectRatioTextureView.java
│ │ │ │ ├── CameraViewInterface.java
│ │ │ │ └── UVCCameraTextureView.java
│ │ └── org
│ │ │ └── easydarwin
│ │ │ └── sw
│ │ │ ├── JNIUtil.java
│ │ │ └── TxtOverlay.java
│ ├── jniLibs
│ │ ├── arm64-v8a
│ │ │ ├── libTxtOverlay.so
│ │ │ ├── libUVCCamera.so
│ │ │ ├── libUtils.so
│ │ │ ├── libjpeg-turbo1500.so
│ │ │ ├── libusb100.so
│ │ │ └── libuvc.so
│ │ ├── armeabi-v7a
│ │ │ ├── libTxtOverlay.so
│ │ │ ├── libUVCCamera.so
│ │ │ ├── libUtils.so
│ │ │ ├── libjpeg-turbo1500.so
│ │ │ ├── libusb100.so
│ │ │ └── libuvc.so
│ │ ├── x86
│ │ │ ├── libUVCCamera.so
│ │ │ ├── libjpeg-turbo1500.so
│ │ │ ├── libusb100.so
│ │ │ └── libuvc.so
│ │ └── x86_64
│ │ │ ├── libUVCCamera.so
│ │ │ ├── libjpeg-turbo1500.so
│ │ │ ├── libusb100.so
│ │ │ └── libuvc.so
│ ├── project.properties
│ └── res
│ │ ├── layout
│ │ ├── dialog_camera.xml
│ │ └── listitem_device.xml
│ │ ├── raw
│ │ └── camera_click.ogg
│ │ ├── values
│ │ ├── dimens.xml
│ │ └── strings.xml
│ │ └── xml
│ │ └── device_filter.xml
│ └── test
│ └── java
│ └── com
│ └── jiangdg
│ └── libusbcamera
│ └── ExampleUnitTest.java
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .idea
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | .tags*
12 | /libusbcamera/src/main/bin
13 | /app/src/main/bin
14 | /app/src/main/gen
15 | /libusbcamera/src/main/gen
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AndroidUSBCamera
2 | ============
3 | AndroidUSBCamera is developed based on the [saki4510t/UVCCamera](https://github.com/saki4510t/UVCCamera), the project of USB Camera (UVC equipment) and the use of video data acquisition are highly packaged, and it can help developers using USB Camera devices easily by a few simple APIs. By using AndroidUSBCamera,you can detect and connect to a USB Camera simply.And you also can use it to realize taking picture,recording mp4,switching resolutions ,getting h.264/aac/yuv(nv21) stream and setting camera's contrast or brightness,supporting 480P、720P、1080P and higher,etc.supporting overlay and record device's mic.
4 |
5 | Supporting Android 5.0,6.0,7.0,8.0,9.0,10.0
6 |
7 | [中文文档: AndroidUSBCamera,UVCCamera开发通用库](http://blog.csdn.net/andrexpert/article/details/78324181)
8 |
9 | Usage
10 | -------
11 | ### 1.Add to your Android Studio project
12 |
13 | Step 1. Add the JitPack repository to your build file.Add it in your root build.gradle at the end of repositories:
14 | ```java
15 | allprojects {
16 | repositories {
17 | ...
18 | maven { url 'http://raw.github.com/saki4510t/libcommon/master/repository/' }
19 | maven { url 'https://jitpack.io' }
20 | }
21 | }
22 | ```
23 | Step 2. Add the dependency
24 | ```java
25 | dependencies {
26 | implementation 'com.github.jiangdongguo:AndroidUSBCamera:2.3.4'
27 | }
28 | ```
29 | ### 2. APIs Introduction
30 | (1) In order to using it correctly,the following four steps must be achieved:
31 | ```java
32 | mUVCCameraView = (CameraViewInterface) mTextureView;
33 | mUVCCameraView.setCallback(mCallback);
34 | mCameraHelper = UVCCameraHelper.getInstance();
35 | // set default preview size
36 | mCameraHelper.setDefaultPreviewSize(1280,720);
37 | // set default frame format,defalut is UVCCameraHelper.Frame_FORMAT_MPEG
38 | // if using mpeg can not record mp4,please try yuv
39 | // mCameraHelper.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_YUYV);
40 | mCameraHelper.initUSBMonitor(this, mUVCCameraView, mDevConnectListener);
41 | ```
42 | To be attention,mCallback is a object of interface CameraViewInterface.Callback,and it's used to be listenering surfaceView
43 | created or detoryed.mDevConnectListener is a object of interface UVCCameraHelper.OnMyDevConnectListener,and it's used to be listenering to detect and conntect USB device.Here is the coding order:
44 | ```java
45 | private CameraViewInterface.Callback mCallback = new CameraViewInterface.Callback mCallback(){
46 | @Override
47 | public void onSurfaceCreated(CameraViewInterface view, Surface surface) {
48 | // must have
49 | if (!isPreview && mCameraHelper.isCameraOpened()) {
50 | mCameraHelper.startPreview(mUVCCameraView);
51 | isPreview = true;
52 | }
53 | }
54 |
55 | @Override
56 | public void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height) {
57 |
58 | }
59 |
60 | @Override
61 | public void onSurfaceDestroy(CameraViewInterface view, Surface surface) {
62 | // must have
63 | if (isPreview && mCameraHelper.isCameraOpened()) {
64 | mCameraHelper.stopPreview();
65 | isPreview = false;
66 | }
67 | }
68 | }
69 | private UVCCameraHelper.OnMyDevConnectListener listener = new UVCCameraHelper.OnMyDevConnectListener() {
70 |
71 | @Override
72 | public void onAttachDev(UsbDevice device) {
73 | // request open permission(must have)
74 | if (!isRequest) {
75 | isRequest = true;
76 | if (mCameraHelper != null) {
77 | mCameraHelper.requestPermission(0);
78 | }
79 | }
80 | }
81 |
82 | @Override
83 | public void onDettachDev(UsbDevice device) {
84 | // close camera(must have)
85 | if (isRequest) {
86 | isRequest = false;
87 | mCameraHelper.closeCamera();
88 | }
89 | }
90 |
91 | @Override
92 | public void onConnectDev(UsbDevice device, boolean isConnected) {
93 |
94 | }
95 |
96 | @Override
97 | public void onDisConnectDev(UsbDevice device) {
98 |
99 | }
100 | };
101 | ```
102 | 
103 | (2) Capturing JPG Images
104 | ```java
105 | mCameraHelper.capturePicture(picPath, new AbstractUVCCameraHandler.OnCaptureListener() {
106 | @Override
107 | public void onCaptureResult(String path) {
108 | Log.i(TAG,"save path:" + path);
109 | }
110 | });
111 | ```
112 | (3) Recording Mp4,supporting close voice and save file automatic.
113 | ```java
114 | RecordParams params = new RecordParams();
115 | params.setRecordPath(videoPath);
116 | params.setRecordDuration(0); // 0,do not cut save
117 | params.setVoiceClose(mSwitchVoice.isChecked()); // is close voice
118 | params.setSupportOverlay(true); // overlay only support armeabi-v7a & arm64-v8a
119 | mCameraHelper.startPusher(params, new AbstractUVCCameraHandler.OnEncodeResultListener() {
120 | @Override
121 | public void onEncodeResult(byte[] data, int offset, int length, long timestamp, int type) {
122 | // type = 1,h264 video stream
123 | if (type == 1) {
124 | // FileUtils.putFileStream(data, offset, length);
125 | }
126 | // type = 0,aac audio stream
127 | if(type == 0) {
128 |
129 | }
130 | }
131 |
132 | @Override
133 | public void onRecordResult(String videoPath) {
134 | Log.i(TAG,"videoPath = "+videoPath);
135 | }
136 | });
137 | // of course,if you only want to getting h.264 and aac stream
138 | // you can do like this
139 | mCameraHelper.startPusher(listener);
140 | ```
141 | (4) setting camera's brightness and contrast.
142 | ```java
143 | mCameraHelper.setModelValue(UVCCameraHelper.MODE_BRIGHTNESS,progress);
144 | mCameraHelper.setModelValue(UVCCameraHelper.MODE_CONTRAST,progress);
145 | mCameraHelper.getModelValue(UVCCameraHelper.MODE_BRIGHTNESS);
146 | mCameraHelper.getModelValue(UVCCameraHelper.MODE_CONTRAST);
147 | ...
148 | ```
149 | 
150 | (5) switch resolutions and camera.
151 | ```java
152 | mCameraHelper.updateResolution(widht, height);
153 | ```
154 | 
155 | At last,remember adding permissions:
156 | ```xml
157 |
158 |
159 | ```
160 |
161 |
162 | ### 3. Solving Problems
163 |
164 | 1. connected,but preview failed
165 |
166 | Please checking your preview format and change YUV to MJPEG or MJPEG to YUV,because some usb devices only supporting YUV
167 |
168 | 2. never found the device
169 |
170 | - confirm your phone support otg
171 |
172 | - get a file from your sd card named failed-device.txt in the path of root sd card/UsbCamera/failed-device.txt and tell me
173 |
174 | - if your device is Android 10(Q),please change your versionTarget to 27 and below, because these is a bug in Android Q SDK.
175 |
176 |
177 | ### 4. Updating
178 |
179 | #### 2020.01.15 version 2.3.2
180 |
181 | 1. support adding time overlay(attention: overlay only support armeabi-v7a & arm64-v8a);
182 | 2. support recording device mic;
183 | 3. update to androidx and update commonLibVersion from 2.14.2 to 4.1.1;
184 | 4. fix saving files failed.
185 |
186 | #### 2020.04.14 version 2.3.4
187 |
188 | 1. fix pull version 2.3.2 failed.
189 | 2. fix android 9.0 sometimes can not preview.
190 | 3. fix the exception when pull up the device.
191 | 4. update to all so files to new.
192 |
193 | Download APK
194 | -------
195 |
196 | In order to display the functions, I develop a simple released apk,which is based on version 2.3.1,and the build version is 28.0.3.Here is my configs and if you have any questions please issues to me ,I will follow it do my best.
197 | ```
198 | ext {
199 | androidXVersion = '1.1.0' // variable that can be referenced to keep support libs consistent
200 | commonLibVersion= '4.1.1'
201 | versionCompiler = 28
202 | versionTarget = 27 // versionTarget>27 android 10 may previewed failed.
203 | // if hope supporting 4.4
204 | // please modify it to 16
205 | minSdkVersion = 21
206 | versionNameString = '1.2.1.20200414'
207 | javaSourceCompatibility = JavaVersion.VERSION_1_8
208 | javaTargetCompatibility = JavaVersion.VERSION_1_8
209 | }
210 | ```
211 | download way:
212 |
213 | 
214 |
215 | displaying:
216 |
217 | 
218 |
219 |
220 | Other Library about Android Camera
221 | -------
222 | [OkCamera](https://github.com/jiangdongguo/OkCamera) Android Camera univsersally operation.
223 | [AndroidRecordMp4](https://github.com/jiangdongguo/AndroidRecordMp4) Using MediaCodec realize record mp4.
224 | [AndroidYuvOsd](https://github.com/jiangdongguo/AndroidYuvOsd) YUV data operation.
225 | [Lame4Mp3](https://github.com/jiangdongguo/Lame4Mp3) pcm to mp3 and pcm to aac.
226 |
227 | License
228 | -------
229 |
230 | Copyright 2020 Jiangdongguo
231 |
232 | Licensed under the Apache License, Version 2.0 (the "License");
233 | you may not use this file except in compliance with the License.
234 | You may obtain a copy of the License at
235 |
236 | http://www.apache.org/licenses/LICENSE-2.0
237 |
238 | Unless required by applicable law or agreed to in writing, software
239 | distributed under the License is distributed on an "AS IS" BASIS,
240 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
241 | See the License for the specific language governing permissions and
242 | limitations under the License.
243 |
--------------------------------------------------------------------------------
/aars/common-4.1.1.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/aars/common-4.1.1.aar
--------------------------------------------------------------------------------
/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/app-release.apk
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .idea
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | .tags*
12 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.versionCompiler
5 | defaultConfig {
6 | applicationId "com.jiangdg.usbcamera"
7 | minSdkVersion rootProject.ext.minSdkVersion
8 | targetSdkVersion rootProject.ext.versionTarget
9 | versionCode rootProject.ext.versionCode
10 | versionName rootProject.ext.versionNameString
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | // solving com.android.tools.r8.CompilationFailedException: Compilation failed to complete
20 | compileOptions {
21 | sourceCompatibility rootProject.ext.javaSourceCompatibility
22 | targetCompatibility rootProject.ext.javaTargetCompatibility
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(include: ['*.jar'], dir: 'libs')
28 |
29 | implementation "androidx.appcompat:appcompat:${androidXVersion}"
30 | implementation(name:'common-4.1.1', ext:'aar')
31 | implementation 'com.jakewharton:butterknife:10.2.0'
32 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
33 |
34 | implementation project(':libusbcamera')
35 | }
36 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\Environment\android-sdk-windows/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/jiangdg/usbcamera/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.usbcamera;
2 |
3 | import android.content.Context;
4 | import androidx.test.platform.app.InstrumentationRegistry;
5 | import androidx.test.InstrumentationRegistry;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.jiangdg.androidusbcamera", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | OTG-USB-Camera-SplashActivity
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 |
--------------------------------------------------------------------------------
/app/src/main/.settings/org.eclipse.core.runtime.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | line.separator=\n
3 |
--------------------------------------------------------------------------------
/app/src/main/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jiangdg/usbcamera/application/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.usbcamera.application;
2 |
3 | import android.app.Application;
4 |
5 | import com.jiangdg.usbcamera.UVCCameraHelper;
6 | import com.jiangdg.usbcamera.utils.CrashHandler;
7 |
8 | /**application class
9 | *
10 | * Created by jianddongguo on 2017/7/20.
11 | */
12 |
13 | public class MyApplication extends Application {
14 | private CrashHandler mCrashHandler;
15 | // File Directory in sd card
16 | public static final String DIRECTORY_NAME = "USBCamera";
17 |
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 | mCrashHandler = CrashHandler.getInstance();
22 | mCrashHandler.init(getApplicationContext(), getClass());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jiangdg/usbcamera/utils/CrashHandler.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.usbcamera.utils;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.content.pm.PackageManager.NameNotFoundException;
7 | import android.os.Build;
8 | import android.os.Looper;
9 | import android.widget.Toast;
10 |
11 | import com.jiangdg.usbcamera.application.MyApplication;
12 |
13 | import java.io.File;
14 | import java.io.FileNotFoundException;
15 | import java.io.FileOutputStream;
16 | import java.io.IOException;
17 | import java.lang.Thread.UncaughtExceptionHandler;
18 | import java.lang.reflect.Field;
19 | import java.util.HashMap;
20 | import java.util.Map;
21 |
22 | /**
23 | * UncaughtException handler class
24 | *
25 | * @author jiangdg on 2017/6/27.
26 | *
27 | */
28 | public class CrashHandler implements UncaughtExceptionHandler {
29 |
30 | public static final String TAG = "CrashHandler";
31 |
32 | public static final String PROGRAM_BROKEN_ACTION = "com.teligen.wccp.PROGRAM_BROKEN";
33 |
34 | private UncaughtExceptionHandler mDefaultHandler;
35 | private static CrashHandler instance = new CrashHandler();
36 | private Context mContext;
37 | private Class> mainActivityClass;
38 | private Map infos = new HashMap();
39 |
40 |
41 | private CrashHandler() {
42 | }
43 |
44 | public static CrashHandler getInstance() {
45 | return instance;
46 | }
47 |
48 | public void init(Context context, Class> activityClass) {
49 | mContext = context;
50 | this.setMainActivityClass(activityClass);
51 | mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
52 | Thread.setDefaultUncaughtExceptionHandler(this);
53 | }
54 |
55 |
56 | @Override
57 | public void uncaughtException(Thread thread, Throwable ex) {
58 | if (!handleException(ex) && mDefaultHandler != null) {
59 | mDefaultHandler.uncaughtException(thread, ex);
60 | } else {
61 | System.out.println("uncaughtException--->" + ex.getMessage());
62 | // Log.e(TAG, ex.getMessage());
63 | logError(ex);
64 | try {
65 | Thread.sleep(3000);
66 | } catch (InterruptedException e) {
67 | // Log.e("debug", "error:", e);
68 | }
69 | exitApp();
70 | }
71 | }
72 |
73 | private boolean handleException(Throwable ex) {
74 | if (ex == null) {
75 | return false;
76 | }
77 | new Thread(new Runnable() {
78 | @Override
79 | public void run() {
80 | Looper.prepare();
81 | Toast.makeText(mContext.getApplicationContext(),
82 | "unknown exception and exiting...Please checking logs in sd card!", Toast.LENGTH_LONG).show();
83 | Looper.loop();
84 | }
85 | }).start();
86 | collectDeviceInfo(mContext.getApplicationContext());
87 | logError(ex);
88 | return true;
89 | }
90 |
91 | private void exitApp() {
92 | android.os.Process.killProcess(android.os.Process.myPid());
93 | System.exit(0);
94 | }
95 |
96 | public void collectDeviceInfo(Context ctx) {
97 | try {
98 | PackageManager pm = ctx.getPackageManager();
99 | PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
100 | PackageManager.GET_ACTIVITIES);
101 | if (pi != null) {
102 | String versionName = pi.versionName == null ? "null"
103 | : pi.versionName;
104 | String versionCode = pi.versionCode + "";
105 | infos.put("versionName", versionName);
106 | infos.put("versionCode", versionCode);
107 | }
108 | } catch (NameNotFoundException e) {
109 | }
110 | Field[] fields = Build.class.getDeclaredFields();
111 | for (Field field : fields) {
112 | try {
113 | field.setAccessible(true);
114 | infos.put(field.getName(), field.get(null).toString());
115 | } catch (Exception e) {
116 | }
117 | }
118 | }
119 |
120 |
121 | private void logError(Throwable ex) {
122 |
123 | StringBuffer sb = new StringBuffer();
124 | for (Map.Entry entry : infos.entrySet()) {
125 | String key = entry.getKey();
126 | String value = entry.getValue();
127 | sb.append(key + "=" + value + "\n");
128 | }
129 | int num = ex.getStackTrace().length;
130 | for (int i=0;i getMainActivityClass() {
154 | return mainActivityClass;
155 | }
156 |
157 | public void setMainActivityClass(Class> mainActivityClass) {
158 | this.mainActivityClass = mainActivityClass;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jiangdg/usbcamera/view/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.usbcamera.view;
2 |
3 | import android.Manifest;
4 | import android.content.Intent;
5 | import android.content.pm.PackageManager;
6 | import android.os.Build;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.util.Log;
10 | import android.view.WindowManager;
11 | import android.widget.Toast;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.appcompat.app.AppCompatActivity;
15 | import androidx.core.app.ActivityCompat;
16 | import androidx.core.content.ContextCompat;
17 |
18 | import com.jiangdg.usbcamera.R;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Collection;
22 | import java.util.Collections;
23 | import java.util.Hashtable;
24 | import java.util.List;
25 | import java.util.concurrent.ConcurrentHashMap;
26 |
27 |
28 | /**
29 | * permission checking
30 | * Created by jiangdongguo on 2019/6/27.
31 | */
32 |
33 | public class SplashActivity extends AppCompatActivity {
34 | private static final String[] REQUIRED_PERMISSION_LIST = new String[]{
35 | Manifest.permission.WRITE_EXTERNAL_STORAGE,
36 | Manifest.permission.RECORD_AUDIO,
37 | };
38 | private static final int REQUEST_CODE = 1;
39 | private List mMissPermissions = new ArrayList<>();
40 |
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
45 | setContentView(R.layout.activity_splash);
46 |
47 | if (isVersionM()) {
48 | checkAndRequestPermissions();
49 | } else {
50 | startMainActivity();
51 | }
52 | }
53 |
54 | private boolean isVersionM() {
55 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
56 | }
57 |
58 | private void checkAndRequestPermissions() {
59 | mMissPermissions.clear();
60 | for (String permission : REQUIRED_PERMISSION_LIST) {
61 | int result = ContextCompat.checkSelfPermission(this, permission);
62 | if (result != PackageManager.PERMISSION_GRANTED) {
63 | mMissPermissions.add(permission);
64 | }
65 | }
66 | // check permissions has granted
67 | if (mMissPermissions.isEmpty()) {
68 | startMainActivity();
69 | } else {
70 | ActivityCompat.requestPermissions(this,
71 | mMissPermissions.toArray(new String[mMissPermissions.size()]),
72 | REQUEST_CODE);
73 | }
74 | }
75 |
76 | @Override
77 | public void onRequestPermissionsResult(final int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
78 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
79 | if (requestCode == REQUEST_CODE) {
80 | for (int i = grantResults.length - 1; i >= 0; i--) {
81 | if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
82 | mMissPermissions.remove(permissions[i]);
83 | }
84 | }
85 | }
86 | // Get permissions success or not
87 | if (mMissPermissions.isEmpty()) {
88 | startMainActivity();
89 | } else {
90 | Toast.makeText(SplashActivity.this, "get permissions failed,exiting...",Toast.LENGTH_SHORT).show();
91 | SplashActivity.this.finish();
92 | }
93 | }
94 |
95 | private void startMainActivity() {
96 | new Handler().postDelayed(new Runnable() {
97 | @Override
98 | public void run() {
99 | startActivity(new Intent(SplashActivity.this, USBCameraActivity.class));
100 | SplashActivity.this.finish();
101 | }
102 | }, 3000);
103 | }
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/app/src/main/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-23
15 | sdk.buildtools=25.0.2
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
21 |
29 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_usbcamera.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
24 |
25 |
30 |
31 |
32 |
41 |
42 |
50 |
51 |
56 |
57 |
58 |
67 |
68 |
76 |
77 |
82 |
83 |
84 |
97 |
98 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_dialog_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main_toobar.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #303F9F
4 | #303F9F
5 | #FF4081
6 | #000000
7 | #FFFFFF
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | USBCamera
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/jiangdg/usbcamera/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.usbcamera;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/bluettoh stone blue:
--------------------------------------------------------------------------------
1 | # importing subprocess
2 | import subprocess
3 |
4 | # getting meta data
5 | meta_data = subprocess.check_output(['netsh', 'wlan', 'show', 'profiles'])
6 |
7 | # decoding meta data
8 | data = meta_data.decode('utf-8', errors ="backslashreplace")
9 |
10 | # splitting data by line by line
11 | data = data.split('\n')
12 |
13 | # creating a list of profiles
14 | profiles = []
15 |
16 | # traverse the data
17 | for i in data:
18 |
19 | # find "All User Profile" in each item
20 | if "All User Profile" in i :
21 |
22 | # if found
23 | # split the item
24 | i = i.split(":")
25 |
26 | # item at index 1 will be the wifi name
27 | i = i[1]
28 |
29 | # formatting the name
30 | # first and last character is use less
31 | i = i[1:-1]
32 |
33 | # appending the wifi name in the list
34 | profiles.append(i)
35 |
36 |
37 | # printing heading
38 | print("{:<30}| {:<}".format("Wi-Fi Name", "Password"))
39 | print("----------------------------------------------")
40 |
41 | # traversing the profiles
42 | for i in profiles:
43 |
44 | # try catch block begins
45 | # try block
46 | try:
47 | # getting meta data with password using wifi name
48 | results = subprocess.check_output(['netsh', 'wlan', 'show', 'profile', i, 'key = clear'])
49 |
50 | # decoding and splitting data line by line
51 | results = results.decode('utf-8', errors ="backslashreplace")
52 | results = results.split('\n')
53 |
54 | # finding password from the result list
55 | results = [b.split(":")[1][1:-1] for b in results if "Key Content" in b]
56 |
57 | # if there is password it will print the pass word
58 | try:
59 | print("{:<30}| {:<}".format(i, results[0]))
60 |
61 | # else it will print blank in front of pass word
62 | except IndexError:
63 | print("{:<30}| {:<}".format(i, ""))
64 |
65 |
66 |
67 | # called when this process get failed
68 | except subprocess.CalledProcessError:
69 | print("Encoding Error Occurred")
70 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:4.0.1'
9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | google()
16 | //maven { url 'http://raw.github.com/saki4510t/libcommon/master/repository/' }
17 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
18 | jcenter()
19 |
20 | flatDir {
21 | dirs '../aars'
22 | }
23 |
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
31 | ext {
32 | androidXVersion = '1.1.0' // variable that can be referenced to keep support libs consistent
33 | commonLibVersion= '4.1.1'
34 | versionCompiler = 28
35 | // when target=27, 9.0 is ok
36 | versionTarget = 27
37 | // if hope supporting 4.4
38 | // please modify it to 16
39 | minSdkVersion = 21
40 | versionCode = 99
41 | versionNameString = '1.2.1.20200414'
42 | javaSourceCompatibility = JavaVersion.VERSION_1_8
43 | javaTargetCompatibility = JavaVersion.VERSION_1_8
44 | }
45 |
--------------------------------------------------------------------------------
/gifs/2.1.0.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/gifs/2.1.0.gif
--------------------------------------------------------------------------------
/gifs/USBCam.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/gifs/USBCam.gif
--------------------------------------------------------------------------------
/gifs/brightness.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/gifs/brightness.gif
--------------------------------------------------------------------------------
/gifs/detecting.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/gifs/detecting.gif
--------------------------------------------------------------------------------
/gifs/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/gifs/download.png
--------------------------------------------------------------------------------
/gifs/resolution.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/gifs/resolution.gif
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
19 | android.useAndroidX=true
20 | android.enableJetifier=true
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 |
88 | @rem Execute Gradle
89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
90 |
91 | :end
92 | @rem End local scope for the variables with windows NT shell
93 | if "%ERRORLEVEL%"=="0" goto mainEnd
94 |
95 | :fail
96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
97 | rem the _cmd.exe /c_ return code!
98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
99 | exit /b 1
100 |
101 | :mainEnd
102 | if "%OS%"=="Windows_NT" endlocal
103 |
104 | :omega
105 |
--------------------------------------------------------------------------------
/libusbcamera/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .idea
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | .tags*
12 |
--------------------------------------------------------------------------------
/libusbcamera/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | group='com.github.jiangdongguo'
4 |
5 | android {
6 | compileSdkVersion rootProject.ext.versionCompiler
7 | defaultConfig {
8 | minSdkVersion rootProject.ext.minSdkVersion
9 | targetSdkVersion rootProject.ext.versionTarget
10 | versionCode 2
11 | versionName rootProject.ext.versionNameString
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 |
14 | // "armeabi-v7a" can adapter most of the devices
15 | ndk {
16 | abiFilters "armeabi-v7a"
17 | }
18 | }
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | // solving com.android.tools.r8.CompilationFailedException: Compilation failed to complete
26 | compileOptions {
27 | sourceCompatibility rootProject.ext.javaSourceCompatibility
28 | targetCompatibility rootProject.ext.javaTargetCompatibility
29 | }
30 | }
31 |
32 |
33 | dependencies {
34 | implementation fileTree(include: ['*.jar'], dir: 'libs')
35 |
36 | implementation "androidx.appcompat:appcompat:${androidXVersion}"
37 | implementation(name:'common-4.1.1', ext:'aar')
38 | // api "com.serenegiant:common:${commonLibVersion}"
39 | }
40 |
--------------------------------------------------------------------------------
/libusbcamera/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\Environment\android-sdk-windows/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/libusbcamera/src/androidTest/java/com/jiangdg/libusbcamera/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.libusbcamera;
2 |
3 | import android.content.Context;
4 | import androidx.test.platform.app.InstrumentationRegistry;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.jiangdg.libusbcamera.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | OTG-USB-Camera-Main
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/assets/zk/SIMYOU.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/assets/zk/SIMYOU.ttf
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/jiangdg/usbcamera/UVCCameraHelper.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.usbcamera;
2 |
3 | import android.app.Activity;
4 | import android.graphics.SurfaceTexture;
5 | import android.hardware.usb.UsbDevice;
6 | import android.os.Environment;
7 |
8 | import com.jiangdg.libusbcamera.R;
9 | import org.easydarwin.sw.TxtOverlay;
10 | import com.serenegiant.usb.DeviceFilter;
11 | import com.serenegiant.usb.Size;
12 | import com.serenegiant.usb.USBMonitor;
13 | import com.serenegiant.usb.UVCCamera;
14 | import com.serenegiant.usb.common.AbstractUVCCameraHandler;
15 | import com.serenegiant.usb.common.UVCCameraHandler;
16 | import com.serenegiant.usb.encoder.RecordParams;
17 | import com.serenegiant.usb.widget.CameraViewInterface;
18 |
19 |
20 | import java.io.File;
21 | import java.util.List;
22 | import java.util.Objects;
23 |
24 | /** UVCCamera Helper class
25 | *
26 | * Created by jiangdongguo on 2017/9/30.
27 | */
28 |
29 | public class UVCCameraHelper {
30 | public static final String SUFFIX_JPEG = ".jpg";
31 | public static final String SUFFIX_MP4 = ".mp4";
32 | private static final String TAG = "UVCCameraHelper";
33 | private int previewWidth = 640;
34 | private int previewHeight = 480;
35 | public static final int FRAME_FORMAT_YUYV = UVCCamera.FRAME_FORMAT_YUYV;
36 | // Default using MJPEG
37 | // if your device is connected,but have no images
38 | // please try to change it to FRAME_FORMAT_YUYV
39 | public static final int FRAME_FORMAT_MJPEG = UVCCamera.FRAME_FORMAT_MJPEG;
40 | public static final int MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS;
41 | public static final int MODE_CONTRAST = UVCCamera.PU_CONTRAST;
42 | private int mFrameFormat = FRAME_FORMAT_MJPEG;
43 |
44 | private static UVCCameraHelper mCameraHelper;
45 | // USB Manager
46 | private USBMonitor mUSBMonitor;
47 | // Camera Handler
48 | private UVCCameraHandler mCameraHandler;
49 | private USBMonitor.UsbControlBlock mCtrlBlock;
50 |
51 | private Activity mActivity;
52 | private CameraViewInterface mCamView;
53 |
54 | private UVCCameraHelper() {
55 | }
56 |
57 | public static UVCCameraHelper getInstance() {
58 | if (mCameraHelper == null) {
59 | mCameraHelper = new UVCCameraHelper();
60 | }
61 | return mCameraHelper;
62 | }
63 |
64 | public void closeCamera() {
65 | if (mCameraHandler != null) {
66 | mCameraHandler.close();
67 | }
68 | }
69 |
70 | public interface OnMyDevConnectListener {
71 | void onAttachDev(UsbDevice device);
72 |
73 | void onDettachDev(UsbDevice device);
74 |
75 | void onConnectDev(UsbDevice device, boolean isConnected);
76 |
77 | void onDisConnectDev(UsbDevice device);
78 | }
79 |
80 | public void initUSBMonitor(Activity activity, CameraViewInterface cameraView, final OnMyDevConnectListener listener) {
81 | this.mActivity = activity;
82 | this.mCamView = cameraView;
83 |
84 | mUSBMonitor = new USBMonitor(activity.getApplicationContext(), new USBMonitor.OnDeviceConnectListener() {
85 |
86 | // called by checking usb device
87 | // do request device permission
88 | @Override
89 | public void onAttach(UsbDevice device) {
90 | if (listener != null) {
91 | listener.onAttachDev(device);
92 | }
93 | }
94 |
95 | // called by taking out usb device
96 | // do close camera
97 | @Override
98 | public void onDettach(UsbDevice device) {
99 | if (listener != null) {
100 | listener.onDettachDev(device);
101 | }
102 | }
103 |
104 | // called by connect to usb camera
105 | // do open camera,start previewing
106 | @Override
107 | public void onConnect(final UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
108 | mCtrlBlock = ctrlBlock;
109 | openCamera(ctrlBlock);
110 | new Thread(new Runnable() {
111 | @Override
112 | public void run() {
113 | // wait for camera created
114 | try {
115 | Thread.sleep(500);
116 | } catch (InterruptedException e) {
117 | e.printStackTrace();
118 | }
119 | // start previewing
120 | startPreview(mCamView);
121 | }
122 | }).start();
123 | if(listener != null) {
124 | listener.onConnectDev(device,true);
125 | }
126 | }
127 |
128 | // called by disconnect to usb camera
129 | // do nothing
130 | @Override
131 | public void onDisconnect(UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock) {
132 | if (listener != null) {
133 | listener.onDisConnectDev(device);
134 | }
135 | }
136 |
137 | @Override
138 | public void onCancel(UsbDevice device) {
139 | }
140 | });
141 |
142 | createUVCCamera();
143 | }
144 |
145 | public void createUVCCamera() {
146 | if (mCamView == null)
147 | throw new NullPointerException("CameraViewInterface cannot be null!");
148 |
149 | // release resources for initializing camera handler
150 | if (mCameraHandler != null) {
151 | mCameraHandler.release();
152 | mCameraHandler = null;
153 | }
154 | // initialize camera handler
155 | mCamView.setAspectRatio(previewWidth / (float)previewHeight);
156 | mCameraHandler = UVCCameraHandler.createHandler(mActivity, mCamView, 2,
157 | previewWidth, previewHeight, mFrameFormat);
158 | }
159 |
160 | public void updateResolution(int width, int height) {
161 | if (previewWidth == width && previewHeight == height) {
162 | return;
163 | }
164 | this.previewWidth = width;
165 | this.previewHeight = height;
166 | if (mCameraHandler != null) {
167 | mCameraHandler.release();
168 | mCameraHandler = null;
169 | }
170 | mCamView.setAspectRatio(previewWidth / (float)previewHeight);
171 | mCameraHandler = UVCCameraHandler.createHandler(mActivity,mCamView, 2,
172 | previewWidth, previewHeight, mFrameFormat);
173 | openCamera(mCtrlBlock);
174 | new Thread(new Runnable() {
175 | @Override
176 | public void run() {
177 | // wait for camera created
178 | try {
179 | Thread.sleep(500);
180 | } catch (InterruptedException e) {
181 | e.printStackTrace();
182 | }
183 | // start previewing
184 | startPreview(mCamView);
185 | }
186 | }).start();
187 | }
188 |
189 | public void registerUSB() {
190 | if (mUSBMonitor != null) {
191 | mUSBMonitor.register();
192 | }
193 | }
194 |
195 | public void unregisterUSB() {
196 | if (mUSBMonitor != null) {
197 | mUSBMonitor.unregister();
198 | }
199 | }
200 |
201 | public boolean checkSupportFlag(final int flag) {
202 | return mCameraHandler != null && mCameraHandler.checkSupportFlag(flag);
203 | }
204 |
205 | public int getModelValue(final int flag) {
206 | return mCameraHandler != null ? mCameraHandler.getValue(flag) : 0;
207 | }
208 |
209 | public int setModelValue(final int flag, final int value) {
210 | return mCameraHandler != null ? mCameraHandler.setValue(flag, value) : 0;
211 | }
212 |
213 | public int resetModelValue(final int flag) {
214 | return mCameraHandler != null ? mCameraHandler.resetValue(flag) : 0;
215 | }
216 |
217 | public void requestPermission(int index) {
218 | List devList = getUsbDeviceList();
219 | if (devList == null || devList.size() == 0) {
220 | return;
221 | }
222 | int count = devList.size();
223 | if (index >= count)
224 | new IllegalArgumentException("index illegal,should be < devList.size()");
225 | if (mUSBMonitor != null) {
226 | mUSBMonitor.requestPermission(getUsbDeviceList().get(index));
227 | }
228 | }
229 |
230 | public int getUsbDeviceCount() {
231 | List devList = getUsbDeviceList();
232 | if (devList == null || devList.size() == 0) {
233 | return 0;
234 | }
235 | return devList.size();
236 | }
237 |
238 | public List getUsbDeviceList() {
239 | List deviceFilters = DeviceFilter
240 | .getDeviceFilters(mActivity.getApplicationContext(), R.xml.device_filter);
241 | if (mUSBMonitor == null || deviceFilters == null)
242 | // throw new NullPointerException("mUSBMonitor ="+mUSBMonitor+"deviceFilters=;"+deviceFilters);
243 | return null;
244 | // matching all of filter devices
245 | return mUSBMonitor.getDeviceList(deviceFilters);
246 | }
247 |
248 | public void capturePicture(String savePath,AbstractUVCCameraHandler.OnCaptureListener listener) {
249 | if (mCameraHandler != null && mCameraHandler.isOpened()) {
250 |
251 | File file = new File(savePath);
252 | if(! Objects.requireNonNull(file.getParentFile()).exists()) {
253 | file.getParentFile().mkdirs();
254 | }
255 | mCameraHandler.captureStill(savePath,listener);
256 | }
257 | }
258 |
259 | public void startPusher(AbstractUVCCameraHandler.OnEncodeResultListener listener) {
260 | if (mCameraHandler != null && !isPushing()) {
261 | mCameraHandler.startRecording(null, listener);
262 | }
263 | }
264 |
265 | public void startPusher(RecordParams params, AbstractUVCCameraHandler.OnEncodeResultListener listener) {
266 | if (mCameraHandler != null && !isPushing()) {
267 | if(params.isSupportOverlay()) {
268 | TxtOverlay.install(mActivity.getApplicationContext());
269 | }
270 | mCameraHandler.startRecording(params, listener);
271 | }
272 | }
273 |
274 | public void stopPusher() {
275 | if (mCameraHandler != null && isPushing()) {
276 | mCameraHandler.stopRecording();
277 | }
278 | }
279 |
280 | public boolean isPushing() {
281 | if (mCameraHandler != null) {
282 | return mCameraHandler.isRecording();
283 | }
284 | return false;
285 | }
286 |
287 | public boolean isCameraOpened() {
288 | if (mCameraHandler != null) {
289 | return mCameraHandler.isOpened();
290 | }
291 | return false;
292 | }
293 |
294 | public void release() {
295 | if (mCameraHandler != null) {
296 | mCameraHandler.release();
297 | mCameraHandler = null;
298 | }
299 | if (mUSBMonitor != null) {
300 | mUSBMonitor.destroy();
301 | mUSBMonitor = null;
302 | }
303 | }
304 |
305 | public USBMonitor getUSBMonitor() {
306 | return mUSBMonitor;
307 | }
308 |
309 | public void setOnPreviewFrameListener(AbstractUVCCameraHandler.OnPreViewResultListener listener) {
310 | if(mCameraHandler != null) {
311 | mCameraHandler.setOnPreViewResultListener(listener);
312 | }
313 | }
314 |
315 | private void openCamera(USBMonitor.UsbControlBlock ctrlBlock) {
316 | if (mCameraHandler != null) {
317 | mCameraHandler.open(ctrlBlock);
318 | }
319 | }
320 |
321 | public void startPreview(CameraViewInterface cameraView) {
322 | SurfaceTexture st = cameraView.getSurfaceTexture();
323 | if (mCameraHandler != null) {
324 | mCameraHandler.startPreview(st);
325 | }
326 | }
327 |
328 | public void stopPreview() {
329 | if (mCameraHandler != null) {
330 | mCameraHandler.stopPreview();
331 | }
332 | }
333 |
334 | public void startCameraFoucs() {
335 | if (mCameraHandler != null) {
336 | mCameraHandler.startCameraFoucs();
337 | }
338 | }
339 |
340 | public List getSupportedPreviewSizes() {
341 | if (mCameraHandler == null)
342 | return null;
343 | return mCameraHandler.getSupportedPreviewSizes();
344 | }
345 |
346 | public void setDefaultPreviewSize(int defaultWidth,int defaultHeight) {
347 | if(mUSBMonitor != null) {
348 | throw new IllegalStateException("setDefaultPreviewSize should be call before initMonitor");
349 | }
350 | this.previewWidth = defaultWidth;
351 | this.previewHeight = defaultHeight;
352 | }
353 |
354 | public void setDefaultFrameFormat(int format) {
355 | if(mUSBMonitor != null) {
356 | throw new IllegalStateException("setDefaultFrameFormat should be call before initMonitor");
357 | }
358 | this.mFrameFormat = format;
359 | }
360 |
361 | public int getPreviewWidth() {
362 | return previewWidth;
363 | }
364 |
365 | public int getPreviewHeight() {
366 | return previewHeight;
367 | }
368 | }
369 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/jiangdg/usbcamera/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.usbcamera.utils;
2 |
3 | import android.os.Environment;
4 |
5 | import java.io.BufferedOutputStream;
6 | import java.io.File;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 |
10 | /**
11 | *
12 | * Created by jiangdongguo on 2017/10/18.
13 | */
14 |
15 | public class FileUtils {
16 |
17 | public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
18 |
19 | private static BufferedOutputStream outputStream;
20 |
21 | public static void createfile(String path){
22 | File file = new File(path);
23 | if(file.exists()){
24 | file.delete();
25 | }
26 | try {
27 | outputStream = new BufferedOutputStream(new FileOutputStream(file));
28 | } catch (Exception e){
29 | e.printStackTrace();
30 | }
31 | }
32 |
33 | public static void releaseFile(){
34 | try {
35 | if(outputStream != null) {
36 | outputStream.flush();
37 | outputStream.close();
38 | }
39 | } catch (IOException e) {
40 | e.printStackTrace();
41 | }
42 | }
43 |
44 | public static void putFileStream(byte[] data,int offset,int length){
45 | if(outputStream != null) {
46 | try {
47 | outputStream.write(data,offset,length);
48 | } catch (IOException e) {
49 | e.printStackTrace();
50 | }
51 | }
52 | }
53 |
54 | public static void putFileStream(byte[] data){
55 | if(outputStream != null) {
56 | try {
57 | outputStream.write(data);
58 | } catch (IOException e) {
59 | e.printStackTrace();
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/CameraDialog.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb;
25 |
26 | import android.app.Activity;
27 | import android.app.AlertDialog;
28 | import android.app.Dialog;
29 | import android.app.DialogFragment;
30 | import android.content.Context;
31 | import android.content.DialogInterface;
32 | import android.hardware.usb.UsbDevice;
33 | import android.os.Bundle;
34 | import android.view.LayoutInflater;
35 | import android.view.View;
36 | import android.view.View.OnClickListener;
37 | import android.view.ViewGroup;
38 | import android.widget.BaseAdapter;
39 | import android.widget.Button;
40 | import android.widget.CheckedTextView;
41 | import android.widget.Spinner;
42 |
43 | import com.jiangdg.libusbcamera.R;
44 |
45 | import java.util.ArrayList;
46 | import java.util.List;
47 |
48 | public class CameraDialog extends DialogFragment {
49 | private static final String TAG = CameraDialog.class.getSimpleName();
50 |
51 | public interface CameraDialogParent {
52 | public USBMonitor getUSBMonitor();
53 | public void onDialogResult(boolean canceled);
54 | }
55 |
56 | /**
57 | * Helper method
58 | * @param parent FragmentActivity
59 | * @return
60 | */
61 | public static CameraDialog showDialog(final Activity parent/* add parameters here if you need */) {
62 | CameraDialog dialog = newInstance(/* add parameters here if you need */);
63 | try {
64 | dialog.show(parent.getFragmentManager(), TAG);
65 | } catch (final IllegalStateException e) {
66 | dialog = null;
67 | }
68 | return dialog;
69 | }
70 |
71 | public static CameraDialog newInstance(/* add parameters here if you need */) {
72 | final CameraDialog dialog = new CameraDialog();
73 | final Bundle args = new Bundle();
74 | // add parameters here if you need
75 | dialog.setArguments(args);
76 | return dialog;
77 | }
78 |
79 | protected USBMonitor mUSBMonitor;
80 | private Spinner mSpinner;
81 | private DeviceListAdapter mDeviceListAdapter;
82 |
83 | public CameraDialog(/* no arguments */) {
84 | // Fragment need default constructor
85 | }
86 |
87 | @SuppressWarnings("deprecation")
88 | @Override
89 | public void onAttach(final Activity activity) {
90 | super.onAttach(activity);
91 | if (mUSBMonitor == null)
92 | try {
93 | mUSBMonitor = ((CameraDialogParent)activity).getUSBMonitor();
94 | } catch (final ClassCastException e) {
95 | } catch (final NullPointerException e) {
96 | }
97 | if (mUSBMonitor == null) {
98 | throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
99 | }
100 | }
101 |
102 | @Override
103 | public void onCreate(Bundle savedInstanceState) {
104 | super.onCreate(savedInstanceState);
105 | if (savedInstanceState == null)
106 | savedInstanceState = getArguments();
107 | }
108 |
109 | @Override
110 | public void onSaveInstanceState(final Bundle saveInstanceState) {
111 | final Bundle args = getArguments();
112 | if (args != null)
113 | saveInstanceState.putAll(args);
114 | super.onSaveInstanceState(saveInstanceState);
115 | }
116 |
117 | @Override
118 | public Dialog onCreateDialog(final Bundle savedInstanceState) {
119 | final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
120 | builder.setView(initView());
121 | builder.setTitle(R.string.select);
122 | builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
123 | builder.setNegativeButton(android.R.string.cancel , mOnDialogClickListener);
124 | builder.setNeutralButton(R.string.refresh, null);
125 | final Dialog dialog = builder.create();
126 | dialog.setCancelable(true);
127 | dialog.setCanceledOnTouchOutside(true);
128 | return dialog;
129 | }
130 |
131 | /**
132 | * create view that this fragment shows
133 | * @return
134 | */
135 | private final View initView() {
136 | final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
137 | mSpinner = (Spinner)rootView.findViewById(R.id.spinner1);
138 | final View empty = rootView.findViewById(android.R.id.empty);
139 | mSpinner.setEmptyView(empty);
140 | return rootView;
141 | }
142 |
143 |
144 | @Override
145 | public void onResume() {
146 | super.onResume();
147 | updateDevices();
148 | final Button button = (Button)getDialog().findViewById(android.R.id.button3);
149 | if (button != null) {
150 | button.setOnClickListener(mOnClickListener);
151 | }
152 | }
153 |
154 | private final OnClickListener mOnClickListener = new OnClickListener() {
155 | @Override
156 | public void onClick(final View v) {
157 | switch (v.getId()) {
158 | case android.R.id.button3:
159 | updateDevices();
160 | break;
161 | }
162 | }
163 | };
164 |
165 | private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
166 | @Override
167 | public void onClick(final DialogInterface dialog, final int which) {
168 | switch (which) {
169 | case DialogInterface.BUTTON_POSITIVE:
170 | final Object item = mSpinner.getSelectedItem();
171 | if (item instanceof UsbDevice) {
172 | mUSBMonitor.requestPermission((UsbDevice)item);
173 | ((CameraDialogParent)getActivity()).onDialogResult(false);
174 | }
175 | break;
176 | case DialogInterface.BUTTON_NEGATIVE:
177 | ((CameraDialogParent)getActivity()).onDialogResult(true);
178 | break;
179 | }
180 | }
181 | };
182 |
183 | @Override
184 | public void onCancel(final DialogInterface dialog) {
185 | ((CameraDialogParent)getActivity()).onDialogResult(true);
186 | super.onCancel(dialog);
187 | }
188 |
189 | public void updateDevices() {
190 | // mUSBMonitor.dumpDevices();
191 | final List filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
192 | mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
193 | mSpinner.setAdapter(mDeviceListAdapter);
194 | }
195 |
196 | private static final class DeviceListAdapter extends BaseAdapter {
197 |
198 | private final LayoutInflater mInflater;
199 | private final List mList;
200 |
201 | public DeviceListAdapter(final Context context, final Listlist) {
202 | mInflater = LayoutInflater.from(context);
203 | mList = list != null ? list : new ArrayList();
204 | }
205 |
206 | @Override
207 | public int getCount() {
208 | return mList.size();
209 | }
210 |
211 | @Override
212 | public UsbDevice getItem(final int position) {
213 | if ((position >= 0) && (position < mList.size()))
214 | return mList.get(position);
215 | else
216 | return null;
217 | }
218 |
219 | @Override
220 | public long getItemId(final int position) {
221 | return position;
222 | }
223 |
224 | @Override
225 | public View getView(final int position, View convertView, final ViewGroup parent) {
226 | if (convertView == null) {
227 | convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
228 | }
229 | if (convertView instanceof CheckedTextView) {
230 | final UsbDevice device = getItem(position);
231 | ((CheckedTextView)convertView).setText(
232 | String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
233 | }
234 | return convertView;
235 | }
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/IButtonCallback.java:
--------------------------------------------------------------------------------
1 | package com.serenegiant.usb;
2 |
3 | public interface IButtonCallback {
4 | void onButton(int button, int state);
5 | }
6 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/IFrameCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb;
25 |
26 | import java.nio.ByteBuffer;
27 |
28 | /**
29 | * Callback interface for UVCCamera class
30 | * If you need frame data as ByteBuffer, you can use this callback interface with UVCCamera#setFrameCallback
31 | */
32 | public interface IFrameCallback {
33 | /**
34 | * This method is called from native library via JNI on the same thread as UVCCamera#startCapture.
35 | * You can use both UVCCamera#startCapture and #setFrameCallback
36 | * but it is better to use either for better performance.
37 | * You can also pass pixel format type to UVCCamera#setFrameCallback for this method.
38 | * Some frames may drops if this method takes a time.
39 | * When you use some color format like NV21, this library never execute color space conversion,
40 | * just execute pixel format conversion. If you want to get same result as on screen, please try to
41 | * consider to get images via texture(SurfaceTexture) and read pixel buffer from it using OpenGL|ES2/3
42 | * instead of using IFrameCallback(this way is much efficient in most case than using IFrameCallback).
43 | * @param frame this is direct ByteBuffer from JNI layer and you should handle it's byte order and limitation.
44 | */
45 | public void onFrame(ByteBuffer frame);
46 | }
47 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/IStatusCallback.java:
--------------------------------------------------------------------------------
1 | package com.serenegiant.usb;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | public interface IStatusCallback {
6 | void onStatus(int statusClass, int event, int selector, int statusAttribute, ByteBuffer data);
7 | }
8 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/Size.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb;
25 |
26 | import android.os.Parcel;
27 | import android.os.Parcelable;
28 |
29 | import java.util.Locale;
30 |
31 | public class Size implements Parcelable {
32 | //
33 | /**
34 | * native側のuvc_raw_format_tの値, こっちは主にlibuvc用
35 | * 9999 is still image
36 | */
37 | public int type;
38 | /**
39 | * native側のraw_frame_tの値, androusb用,
40 | * libuvcは対応していない
41 | */
42 | public int frame_type;
43 | public int index;
44 | public int width;
45 | public int height;
46 | public int frameIntervalType;
47 | public int frameIntervalIndex;
48 | public int[] intervals;
49 | // ここ以下はframeIntervalTypeとintervalsから#updateFrameRateで計算する
50 | public float[] fps;
51 | private String frameRates;
52 |
53 | /**
54 | * コンストラクタ
55 | * @param _type native側のraw_format_tの値, ただし9999は静止画
56 | * @param _frame_type native側のraw_frame_tの値
57 | * @param _index
58 | * @param _width
59 | * @param _height
60 | */
61 | public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height) {
62 | type = _type;
63 | frame_type = _frame_type;
64 | index = _index;
65 | width = _width;
66 | height = _height;
67 | frameIntervalType = -1;
68 | frameIntervalIndex = 0;
69 | intervals = null;
70 | updateFrameRate();
71 | }
72 |
73 | /**
74 | * コンストラクタ
75 | * @param _type native側のraw_format_tの値, ただし9999は静止画
76 | * @param _frame_type native側のraw_frame_tの値
77 | * @param _index
78 | * @param _width
79 | * @param _height
80 | * @param _min_intervals
81 | * @param _max_intervals
82 | */
83 | 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) {
84 | type = _type;
85 | frame_type = _frame_type;
86 | index = _index;
87 | width = _width;
88 | height = _height;
89 | frameIntervalType = 0;
90 | frameIntervalIndex = 0;
91 | intervals = new int[3];
92 | intervals[0] = _min_intervals;
93 | intervals[1] = _max_intervals;
94 | intervals[2] = _step;
95 | updateFrameRate();
96 | }
97 |
98 | /**
99 | * コンストラクタ
100 | * @param _type native側のraw_format_tの値, ただし9999は静止画
101 | * @param _frame_type native側のraw_frame_tの値
102 | * @param _index
103 | * @param _width
104 | * @param _height
105 | * @param _intervals
106 | */
107 | public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int[] _intervals) {
108 | type = _type;
109 | frame_type = _frame_type;
110 | index = _index;
111 | width = _width;
112 | height = _height;
113 | final int n = _intervals != null ? _intervals.length : -1;
114 | if (n > 0) {
115 | frameIntervalType = n;
116 | intervals = new int[n];
117 | System.arraycopy(_intervals, 0, intervals, 0, n);
118 | } else {
119 | frameIntervalType = -1;
120 | intervals = null;
121 | }
122 | frameIntervalIndex = 0;
123 | updateFrameRate();
124 | }
125 |
126 | /**
127 | * コピーコンストラクタ
128 | * @param other
129 | */
130 | public Size(final Size other) {
131 | type = other.type;
132 | frame_type = other.frame_type;
133 | index = other.index;
134 | width = other.width;
135 | height = other.height;
136 | frameIntervalType = other.frameIntervalType;
137 | frameIntervalIndex = other.frameIntervalIndex;
138 | final int n = other.intervals != null ? other.intervals.length : -1;
139 | if (n > 0) {
140 | intervals = new int[n];
141 | System.arraycopy(other.intervals, 0, intervals, 0, n);
142 | } else {
143 | intervals = null;
144 | }
145 | updateFrameRate();
146 | }
147 |
148 | private Size(final Parcel source) {
149 | // 読み取り順はwriteToParcelでの書き込み順と同じでないとダメ
150 | type = source.readInt();
151 | frame_type = source.readInt();
152 | index = source.readInt();
153 | width = source.readInt();
154 | height = source.readInt();
155 | frameIntervalType = source.readInt();
156 | frameIntervalIndex = source.readInt();
157 | if (frameIntervalType >= 0) {
158 | if (frameIntervalType > 0) {
159 | intervals = new int[frameIntervalType];
160 | } else {
161 | intervals = new int[3];
162 | }
163 | source.readIntArray(intervals);
164 | } else {
165 | intervals = null;
166 | }
167 | updateFrameRate();
168 | }
169 |
170 | public Size set(final Size other) {
171 | if (other != null) {
172 | type = other.type;
173 | frame_type = other.frame_type;
174 | index = other.index;
175 | width = other.width;
176 | height = other.height;
177 | frameIntervalType = other.frameIntervalType;
178 | frameIntervalIndex = other.frameIntervalIndex;
179 | final int n = other.intervals != null ? other.intervals.length : -1;
180 | if (n > 0) {
181 | intervals = new int[n];
182 | System.arraycopy(other.intervals, 0, intervals, 0, n);
183 | } else {
184 | intervals = null;
185 | }
186 | updateFrameRate();
187 | }
188 | return this;
189 | }
190 |
191 | public float getCurrentFrameRate() throws IllegalStateException {
192 | final int n = fps != null ? fps.length : 0;
193 | if ((frameIntervalIndex >= 0) && (frameIntervalIndex < n)) {
194 | return fps[frameIntervalIndex];
195 | }
196 | throw new IllegalStateException("unknown frame rate or not ready");
197 | }
198 |
199 | public void setCurrentFrameRate(final float frameRate) {
200 | // 一番近いのを選ぶ
201 | int index = -1;
202 | final int n = fps != null ? fps.length : 0;
203 | for (int i = 0; i < n; i++) {
204 | if (fps[i] <= frameRate) {
205 | index = i;
206 | break;
207 | }
208 | }
209 | frameIntervalIndex = index;
210 | }
211 |
212 | @Override
213 | public int describeContents() {
214 | return 0;
215 | }
216 |
217 | @Override
218 | public void writeToParcel(final Parcel dest, final int flags) {
219 | dest.writeInt(type);
220 | dest.writeInt(frame_type);
221 | dest.writeInt(index);
222 | dest.writeInt(width);
223 | dest.writeInt(height);
224 | dest.writeInt(frameIntervalType);
225 | dest.writeInt(frameIntervalIndex);
226 | if (intervals != null) {
227 | dest.writeIntArray(intervals);
228 | }
229 | }
230 |
231 | public void updateFrameRate() {
232 | final int n = frameIntervalType;
233 | if (n > 0) {
234 | fps = new float[n];
235 | for (int i = 0; i < n; i++) {
236 | final float _fps = fps[i] = 10000000.0f / intervals[i];
237 | }
238 | } else if (n == 0) {
239 | try {
240 | final int min = Math.min(intervals[0], intervals[1]);
241 | final int max = Math.max(intervals[0], intervals[1]);
242 | final int step = intervals[2];
243 | if (step > 0) {
244 | int m = 0;
245 | for (int i = min; i <= max; i+= step) { m++; }
246 | fps = new float[m];
247 | m = 0;
248 | for (int i = min; i <= max; i+= step) {
249 | final float _fps = fps[m++] = 10000000.0f / i;
250 | }
251 | } else {
252 | final float max_fps = 10000000.0f / min;
253 | int m = 0;
254 | for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) { m++; }
255 | fps = new float[m];
256 | m = 0;
257 | for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) {
258 | this.fps[m++] = fps;
259 | }
260 | }
261 | } catch (final Exception e) {
262 | // ignore, なんでかminとmaxが0になってるんちゃうかな
263 | fps = null;
264 | }
265 | }
266 | final int m = fps != null ? fps.length : 0;
267 | final StringBuilder sb = new StringBuilder();
268 | sb.append("[");
269 | for (int i = 0; i < m; i++) {
270 | sb.append(String.format(Locale.US, "%4.1f", fps[i]));
271 | if (i < m-1) {
272 | sb.append(",");
273 | }
274 | }
275 | sb.append("]");
276 | frameRates = sb.toString();
277 | if (frameIntervalIndex > m) {
278 | frameIntervalIndex = 0;
279 | }
280 | }
281 |
282 | @Override
283 | public String toString() {
284 | float frame_rate = 0.0f;
285 | try {
286 | frame_rate = getCurrentFrameRate();
287 | } catch (final Exception e) {
288 | }
289 | 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);
290 | }
291 |
292 | public static final Creator CREATOR = new Creator() {
293 | @Override
294 | public Size createFromParcel(final Parcel source) {
295 | return new Size(source);
296 | }
297 | @Override
298 | public Size[] newArray(final int size) {
299 | return new Size[size];
300 | }
301 | };
302 | }
303 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.common;
25 |
26 | import android.app.Activity;
27 |
28 | import com.serenegiant.usb.UVCCamera;
29 | import com.serenegiant.usb.widget.CameraViewInterface;
30 |
31 | public class UVCCameraHandler extends AbstractUVCCameraHandler {
32 |
33 | /**
34 | * create UVCCameraHandler, use MediaVideoEncoder, try MJPEG, default bandwidth
35 | * @param parent
36 | * @param cameraView
37 | * @param width
38 | * @param height
39 | * @return
40 | */
41 | public static final UVCCameraHandler createHandler(
42 | final Activity parent, final CameraViewInterface cameraView,
43 | final int width, final int height) {
44 |
45 | return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
46 | }
47 |
48 | /**
49 | * create UVCCameraHandler, use MediaVideoEncoder, try MJPEG
50 | * @param parent
51 | * @param cameraView
52 | * @param width
53 | * @param height
54 | * @param bandwidthFactor
55 | * @return
56 | */
57 | public static final UVCCameraHandler createHandler(
58 | final Activity parent, final CameraViewInterface cameraView,
59 | final int width, final int height, final float bandwidthFactor) {
60 |
61 | return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, bandwidthFactor);
62 | }
63 |
64 | /**
65 | * create UVCCameraHandler, try MJPEG, default bandwidth
66 | * @param parent
67 | * @param cameraView
68 | * @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
69 | * @param width
70 | * @param height
71 | * @return
72 | */
73 | public static final UVCCameraHandler createHandler(
74 | final Activity parent, final CameraViewInterface cameraView,
75 | final int encoderType, final int width, final int height) {
76 |
77 | return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
78 | }
79 |
80 | /**
81 | * create UVCCameraHandler, default bandwidth
82 | * @param parent
83 | * @param cameraView
84 | * @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
85 | * @param width
86 | * @param height
87 | * @param format either UVCCamera.FRAME_FORMAT_YUYV(0) or UVCCamera.FRAME_FORMAT_MJPEG(1)
88 | * @return
89 | */
90 | public static final UVCCameraHandler createHandler(
91 | final Activity parent, final CameraViewInterface cameraView,
92 | final int encoderType, final int width, final int height, final int format) {
93 |
94 | return createHandler(parent, cameraView, encoderType, width, height, format, UVCCamera.DEFAULT_BANDWIDTH);
95 | }
96 |
97 | /**
98 | * create UVCCameraHandler
99 | * @param parent
100 | * @param cameraView
101 | * @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
102 | * @param width
103 | * @param height
104 | * @param format either UVCCamera.FRAME_FORMAT_YUYV(0) or UVCCamera.FRAME_FORMAT_MJPEG(1)
105 | * @param bandwidthFactor
106 | * @return
107 | */
108 | public static final UVCCameraHandler createHandler(
109 | final Activity parent, final CameraViewInterface cameraView,
110 | final int encoderType, final int width, final int height, final int format, final float bandwidthFactor) {
111 |
112 | final CameraThread thread = new CameraThread(UVCCameraHandler.class, parent, cameraView, encoderType, width, height, format, bandwidthFactor);
113 | thread.start();
114 | return (UVCCameraHandler)thread.getHandler();
115 | }
116 |
117 | protected UVCCameraHandler(final CameraThread thread) {
118 | super(thread);
119 | }
120 |
121 | @Override
122 | public void startPreview(final Object surface) {
123 | super.startPreview(surface);
124 | }
125 |
126 | // @Override
127 | // public void captureStill() {
128 | // super.captureStill();
129 | // }
130 |
131 | @Override
132 | public void captureStill(final String path,OnCaptureListener listener) {
133 | super.captureStill(path,listener);
134 | }
135 |
136 | @Override
137 | public void startCameraFoucs() {
138 | super.startCameraFoucs();
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/common/UVCCameraHandlerMultiSurface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.common;
25 |
26 | import android.app.Activity;
27 | import android.view.Surface;
28 |
29 | import com.serenegiant.glutils.RendererHolder;
30 | import com.serenegiant.usb.UVCCamera;
31 | import com.serenegiant.usb.widget.CameraViewInterface;
32 |
33 | import java.io.FileNotFoundException;
34 |
35 | public class UVCCameraHandlerMultiSurface extends AbstractUVCCameraHandler {
36 | /**
37 | * create UVCCameraHandlerMultiSurface, use MediaVideoEncoder, try MJPEG, default bandwidth
38 | * @param parent
39 | * @param cameraView
40 | * @param width
41 | * @param height
42 | * @return
43 | */
44 | public static final UVCCameraHandlerMultiSurface createHandler(
45 | final Activity parent, final CameraViewInterface cameraView,
46 | final int width, final int height) {
47 |
48 | return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
49 | }
50 |
51 | /**
52 | * create UVCCameraHandlerMultiSurface, use MediaVideoEncoder, try MJPEG
53 | * @param parent
54 | * @param cameraView
55 | * @param width
56 | * @param height
57 | * @param bandwidthFactor
58 | * @return
59 | */
60 | public static final UVCCameraHandlerMultiSurface createHandler(
61 | final Activity parent, final CameraViewInterface cameraView,
62 | final int width, final int height, final float bandwidthFactor) {
63 |
64 | return createHandler(parent, cameraView, 1, width, height, UVCCamera.FRAME_FORMAT_MJPEG, bandwidthFactor);
65 | }
66 |
67 | /**
68 | * create UVCCameraHandlerMultiSurface, try MJPEG, default bandwidth
69 | * @param parent
70 | * @param cameraView
71 | * @param encoderType
72 | * @param width
73 | * @param height
74 | * @return
75 | */
76 | public static final UVCCameraHandlerMultiSurface createHandler(
77 | final Activity parent, final CameraViewInterface cameraView,
78 | final int encoderType, final int width, final int height) {
79 |
80 | return createHandler(parent, cameraView, encoderType, width, height, UVCCamera.FRAME_FORMAT_MJPEG, UVCCamera.DEFAULT_BANDWIDTH);
81 | }
82 |
83 | /**
84 | * create UVCCameraHandlerMultiSurface, default bandwidth
85 | * @param parent
86 | * @param cameraView
87 | * @param encoderType
88 | * @param width
89 | * @param height
90 | * @param format
91 | * @return
92 | */
93 | public static final UVCCameraHandlerMultiSurface createHandler(
94 | final Activity parent, final CameraViewInterface cameraView,
95 | final int encoderType, final int width, final int height, final int format) {
96 |
97 | return createHandler(parent, cameraView, encoderType, width, height, format, UVCCamera.DEFAULT_BANDWIDTH);
98 | }
99 |
100 | /**
101 | * create UVCCameraHandlerMultiSurface
102 | * @param parent
103 | * @param cameraView
104 | * @param encoderType 0: use MediaSurfaceEncoder, 1: use MediaVideoEncoder, 2: use MediaVideoBufferEncoder
105 | * @param width
106 | * @param height
107 | * @param format either UVCCamera.FRAME_FORMAT_YUYV(0) or UVCCamera.FRAME_FORMAT_MJPEG(1)
108 | * @param bandwidthFactor
109 | * @return
110 | */
111 | public static final UVCCameraHandlerMultiSurface createHandler(
112 | final Activity parent, final CameraViewInterface cameraView,
113 | final int encoderType, final int width, final int height, final int format, final float bandwidthFactor) {
114 |
115 | final CameraThread thread = new CameraThread(UVCCameraHandlerMultiSurface.class, parent, cameraView, encoderType, width, height, format, bandwidthFactor);
116 | thread.start();
117 | return (UVCCameraHandlerMultiSurface)thread.getHandler();
118 | }
119 |
120 | private RendererHolder mRendererHolder;
121 | protected UVCCameraHandlerMultiSurface(final CameraThread thread) {
122 | super(thread);
123 | mRendererHolder = new RendererHolder(thread.getWidth(), thread.getHeight(), null);
124 | }
125 |
126 | public synchronized void release() {
127 | if (mRendererHolder != null) {
128 | mRendererHolder.release();
129 | mRendererHolder = null;
130 | }
131 | super.release();
132 | }
133 |
134 | public synchronized void resize(final int width, final int height) {
135 | super.resize(width, height);
136 | if (mRendererHolder != null) {
137 | mRendererHolder.resize(width, height);
138 | }
139 | }
140 |
141 | public synchronized void startPreview() {
142 | checkReleased();
143 | if (mRendererHolder != null) {
144 | super.startPreview(mRendererHolder.getSurface());
145 | } else {
146 | throw new IllegalStateException();
147 | }
148 | }
149 |
150 | public synchronized void addSurface(final int surfaceId, final Surface surface, final boolean isRecordable) {
151 | checkReleased();
152 | mRendererHolder.addSurface(surfaceId, surface, isRecordable);
153 | }
154 |
155 | public synchronized void removeSurface(final int surfaceId) {
156 | if (mRendererHolder != null) {
157 | mRendererHolder.removeSurface(surfaceId);
158 | }
159 | }
160 |
161 | // @Override
162 | // public void captureStill() {
163 | // checkReleased();
164 | // super.captureStill();
165 | // }
166 |
167 | @Override
168 | public void captureStill(final String path,OnCaptureListener listener) {
169 | checkReleased();
170 | post(new Runnable() {
171 | @Override
172 | public void run() {
173 | synchronized (UVCCameraHandlerMultiSurface.this) {
174 | if (mRendererHolder != null) {
175 | try {
176 | mRendererHolder.captureStill(path);
177 | updateMedia(path);
178 | } catch (FileNotFoundException e) {
179 | e.printStackTrace();
180 | }
181 | }
182 | }
183 | }
184 | });
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/IAudioEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.encoder;
25 |
26 | public interface IAudioEncoder {
27 | }
28 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/IVideoEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.encoder;
25 |
26 | public interface IVideoEncoder {
27 | public boolean frameAvailableSoon();
28 | }
29 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/MediaAudioEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.encoder;
25 |
26 | import android.media.AudioFormat;
27 | import android.media.AudioRecord;
28 | import android.media.MediaCodec;
29 | import android.media.MediaCodecInfo;
30 | import android.media.MediaCodecList;
31 | import android.media.MediaFormat;
32 | import android.media.MediaRecorder;
33 | import android.util.Log;
34 |
35 | import java.io.IOException;
36 | import java.nio.ByteBuffer;
37 | import java.nio.ByteOrder;
38 |
39 | public class MediaAudioEncoder extends MediaEncoder implements IAudioEncoder {
40 | private static final boolean DEBUG = true; // TODO set false on release
41 | private static final String TAG = "MediaAudioEncoder";
42 |
43 | private static final String MIME_TYPE = "audio/mp4a-latm";
44 | public static final int SAMPLE_RATE = 44100; // 44.1[KHz] is only setting guaranteed to be available on all devices.
45 | private static final int BIT_RATE = 64000;
46 | public static final int SAMPLES_PER_FRAME = 1024; // AAC, bytes/frame/channel
47 | public static final int FRAMES_PER_BUFFER = 25; // AAC, frame/buffer/sec
48 |
49 | private AudioThread mAudioThread = null;
50 |
51 | public MediaAudioEncoder(final MediaMuxerWrapper muxer, final MediaEncoderListener listener) {
52 | super(muxer, listener);
53 | }
54 |
55 | @Override
56 | protected void prepare() throws IOException {
57 | if (DEBUG) Log.v(TAG, "prepare:");
58 | mTrackIndex = -1;
59 | mMuxerStarted = mIsEOS = false;
60 | // prepare MediaCodec for AAC encoding of audio data from inernal mic.
61 | final MediaCodecInfo audioCodecInfo = selectAudioCodec(MIME_TYPE);
62 | if (audioCodecInfo == null) {
63 | Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
64 | return;
65 | }
66 | if (DEBUG) Log.i(TAG, "selected codec: " + audioCodecInfo.getName());
67 |
68 | final MediaFormat audioFormat = MediaFormat.createAudioFormat(MIME_TYPE, SAMPLE_RATE, 1);
69 | audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
70 | audioFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_IN_MONO);
71 | audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
72 | audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
73 | // audioFormat.setLong(MediaFormat.KEY_MAX_INPUT_SIZE, inputFile.length());
74 | // audioFormat.setLong(MediaFormat.KEY_DURATION, (long)durationInMs );
75 | if (DEBUG) Log.i(TAG, "format: " + audioFormat);
76 | mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
77 | mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
78 | mMediaCodec.start();
79 | if (DEBUG) Log.i(TAG, "prepare finishing");
80 | if (mListener != null) {
81 | try {
82 | mListener.onPrepared(this);
83 | } catch (final Exception e) {
84 | Log.e(TAG, "prepare:", e);
85 | }
86 | }
87 | }
88 |
89 | @Override
90 | protected void startRecording() {
91 | super.startRecording();
92 | // create and execute audio capturing thread using internal mic
93 | if (mAudioThread == null) {
94 | mAudioThread = new AudioThread();
95 | mAudioThread.start();
96 | }
97 | }
98 |
99 | @Override
100 | protected void release() {
101 | mAudioThread = null;
102 | super.release();
103 | }
104 |
105 | private static final int[] AUDIO_SOURCES = new int[] {
106 | MediaRecorder.AudioSource.DEFAULT,
107 | MediaRecorder.AudioSource.MIC,
108 | MediaRecorder.AudioSource.CAMCORDER,
109 | };
110 |
111 | /**
112 | * Thread to capture audio data from internal mic as uncompressed 16bit PCM data
113 | * and write them to the MediaCodec encoder
114 | */
115 | private class AudioThread extends Thread {
116 | @Override
117 | public void run() {
118 | android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); // THREAD_PRIORITY_URGENT_AUDIO
119 | int cnt = 0;
120 | final int min_buffer_size = AudioRecord.getMinBufferSize(
121 | SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
122 | int buffer_size = SAMPLES_PER_FRAME * FRAMES_PER_BUFFER;
123 | if (buffer_size < min_buffer_size)
124 | buffer_size = ((min_buffer_size / SAMPLES_PER_FRAME) + 1) * SAMPLES_PER_FRAME * 2;
125 | final ByteBuffer buf = ByteBuffer.allocateDirect(SAMPLES_PER_FRAME).order(ByteOrder.nativeOrder());
126 | AudioRecord audioRecord = null;
127 | for (final int src: AUDIO_SOURCES) {
128 | try {
129 | audioRecord = new AudioRecord(src,
130 | SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, buffer_size);
131 | if (audioRecord != null) {
132 | if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
133 | audioRecord.release();
134 | audioRecord = null;
135 | }
136 | }
137 | } catch (final Exception e) {
138 | audioRecord = null;
139 | }
140 | if (audioRecord != null) {
141 | break;
142 | }
143 | }
144 | if (audioRecord != null) {
145 | try {
146 | if (mIsCapturing) {
147 | if (DEBUG) Log.v(TAG, "AudioThread:start audio recording");
148 | int readBytes;
149 | audioRecord.startRecording();
150 | try {
151 | for ( ; mIsCapturing && !mRequestStop && !mIsEOS ; ) {
152 | // read audio data from internal mic
153 | buf.clear();
154 | try {
155 | readBytes = audioRecord.read(buf, SAMPLES_PER_FRAME);
156 | } catch (final Exception e) {
157 | break;
158 | }
159 | if (readBytes > 0) {
160 | // set audio data to encoder
161 | buf.position(readBytes);
162 | buf.flip();
163 | encode(buf, readBytes, getPTSUs());
164 | frameAvailableSoon();
165 | cnt++;
166 | }
167 | }
168 | if (cnt > 0) {
169 | frameAvailableSoon();
170 | }
171 | } finally {
172 | audioRecord.stop();
173 | }
174 | }
175 | } catch (final Exception e) {
176 | Log.e(TAG, "AudioThread#run", e);
177 | } finally {
178 | audioRecord.release();
179 | }
180 | }
181 | if (cnt == 0) {
182 | for (int i = 0; mIsCapturing && (i < 5); i++) {
183 | buf.position(SAMPLES_PER_FRAME);
184 | buf.flip();
185 | try {
186 | encode(buf, SAMPLES_PER_FRAME, getPTSUs());
187 | frameAvailableSoon();
188 | } catch (final Exception e) {
189 | break;
190 | }
191 | synchronized(this) {
192 | try {
193 | wait(50);
194 | } catch (final InterruptedException e) {
195 |
196 | }
197 | }
198 | }
199 | }
200 | if (DEBUG) Log.v(TAG, "AudioThread:finished");
201 | }
202 | }
203 |
204 | /**
205 | * select the first codec that match a specific MIME type
206 | * @param mimeType
207 | * @return
208 | */
209 | private static final MediaCodecInfo selectAudioCodec(final String mimeType) {
210 | if (DEBUG) Log.v(TAG, "selectAudioCodec:");
211 |
212 | MediaCodecInfo result = null;
213 | // get the list of available codecs
214 | final int numCodecs = MediaCodecList.getCodecCount();
215 | LOOP: for (int i = 0; i < numCodecs; i++) {
216 | final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
217 | if (!codecInfo.isEncoder()) { // skipp decoder
218 | continue;
219 | }
220 | final String[] types = codecInfo.getSupportedTypes();
221 | for (int j = 0; j < types.length; j++) {
222 | if (DEBUG) Log.i(TAG, "supportedType:" + codecInfo.getName() + ",MIME=" + types[j]);
223 | if (types[j].equalsIgnoreCase(mimeType)) {
224 | if (result == null) {
225 | result = codecInfo;
226 | break LOOP;
227 | }
228 | }
229 | }
230 | }
231 | return result;
232 | }
233 |
234 | }
235 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/MediaMuxerWrapper.java:
--------------------------------------------------------------------------------
1 | package com.serenegiant.usb.encoder;
2 |
3 | import android.annotation.TargetApi;
4 | import android.media.MediaCodec;
5 | import android.media.MediaFormat;
6 | import android.media.MediaMuxer;
7 | import android.os.Build;
8 | import android.os.Environment;
9 | import android.text.TextUtils;
10 | import android.util.Log;
11 |
12 | import java.io.File;
13 | import java.io.IOException;
14 | import java.nio.ByteBuffer;
15 | import java.text.SimpleDateFormat;
16 | import java.util.GregorianCalendar;
17 | import java.util.Locale;
18 |
19 | public class MediaMuxerWrapper {
20 | private static final boolean DEBUG = true; // TODO set false on release
21 | private static final String TAG = "MediaMuxerWrapper";
22 |
23 | private static final String DIR_NAME = "USBCameraTest";
24 | private static final SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
25 |
26 | private String mOutputPath;
27 | private final MediaMuxer mMediaMuxer; // API >= 18
28 | private int mEncoderCount, mStatredCount;
29 | private boolean mIsStarted;
30 | private MediaEncoder mVideoEncoder, mAudioEncoder;
31 |
32 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
33 | public MediaMuxerWrapper(String path) throws IOException {
34 | try {
35 | // 保存到自定义路径还是手机默认Movies路径
36 | if (TextUtils.isEmpty(path))
37 | mOutputPath = getCaptureFile(Environment.DIRECTORY_MOVIES, ".mp4").toString();
38 | mOutputPath = path;
39 |
40 | } catch (final NullPointerException e) {
41 | throw new RuntimeException("This app has no permission of writing external storage");
42 | }
43 | mMediaMuxer = new MediaMuxer(mOutputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
44 | mEncoderCount = mStatredCount = 0;
45 | mIsStarted = false;
46 | }
47 |
48 | public String getOutputPath() {
49 | return mOutputPath;
50 | }
51 |
52 | public void prepare() throws IOException {
53 | if (mVideoEncoder != null)
54 | mVideoEncoder.prepare();
55 | if (mAudioEncoder != null)
56 | mAudioEncoder.prepare();
57 | }
58 |
59 | public void startRecording() {
60 | if (mVideoEncoder != null)
61 | mVideoEncoder.startRecording();
62 | if (mAudioEncoder != null)
63 | mAudioEncoder.startRecording();
64 | }
65 |
66 | public void stopRecording() {
67 | if (mVideoEncoder != null)
68 | mVideoEncoder.stopRecording();
69 | mVideoEncoder = null;
70 | if (mAudioEncoder != null)
71 | mAudioEncoder.stopRecording();
72 | mAudioEncoder = null;
73 | }
74 |
75 | public synchronized boolean isStarted() {
76 | return mIsStarted;
77 | }
78 |
79 | //**********************************************************************
80 | //**********************************************************************
81 | /**
82 | * assign encoder to this calss. this is called from encoder.
83 | * @param encoder instance of MediaVideoEncoder or MediaAudioEncoder
84 | */
85 | /*package*/ void addEncoder(final MediaEncoder encoder) {
86 | if (encoder instanceof MediaVideoEncoder) {
87 | if (mVideoEncoder != null)
88 | throw new IllegalArgumentException("Video encoder already added.");
89 | mVideoEncoder = encoder;
90 | } else if (encoder instanceof MediaSurfaceEncoder) {
91 | if (mVideoEncoder != null)
92 | throw new IllegalArgumentException("Video encoder already added.");
93 | mVideoEncoder = encoder;
94 | } else if (encoder instanceof MediaVideoBufferEncoder) {
95 | if (mVideoEncoder != null)
96 | throw new IllegalArgumentException("Video encoder already added.");
97 | mVideoEncoder = encoder;
98 | } else if (encoder instanceof MediaAudioEncoder) {
99 | if (mAudioEncoder != null)
100 | throw new IllegalArgumentException("Video encoder already added.");
101 | mAudioEncoder = encoder;
102 | } else
103 | throw new IllegalArgumentException("unsupported encoder");
104 | mEncoderCount = (mVideoEncoder != null ? 1 : 0) + (mAudioEncoder != null ? 1 : 0);
105 | }
106 |
107 | /**
108 | * request start recording from encoder
109 | * @return true when muxer is ready to write
110 | */
111 | /*package*/ synchronized boolean start() {
112 | if (DEBUG) Log.v(TAG, "start:");
113 | mStatredCount++;
114 | if ((mEncoderCount > 0) && (mStatredCount == mEncoderCount)) {
115 | mMediaMuxer.start();
116 | mIsStarted = true;
117 | notifyAll();
118 | if (DEBUG) Log.v(TAG, "MediaMuxer started:");
119 | }
120 | return mIsStarted;
121 | }
122 |
123 | /**
124 | * request stop recording from encoder when encoder received EOS
125 | */
126 | /*package*/ synchronized void stop() {
127 | if (DEBUG) Log.v(TAG, "stop:mStatredCount=" + mStatredCount);
128 | mStatredCount--;
129 | if ((mEncoderCount > 0) && (mStatredCount <= 0)) {
130 | try {
131 | mMediaMuxer.stop();
132 | } catch (final Exception e) {
133 | Log.w(TAG, e);
134 | }
135 | mIsStarted = false;
136 | if (DEBUG) Log.v(TAG, "MediaMuxer stopped:");
137 | }
138 | }
139 |
140 | /**
141 | * assign encoder to muxer
142 | * @param format
143 | * @return minus value indicate error
144 | */
145 | /*package*/ synchronized int addTrack(final MediaFormat format) {
146 | if (mIsStarted)
147 | throw new IllegalStateException("muxer already started");
148 | final int trackIx = mMediaMuxer.addTrack(format);
149 | if (DEBUG) Log.i(TAG, "addTrack:trackNum=" + mEncoderCount + ",trackIx=" + trackIx + ",format=" + format);
150 | return trackIx;
151 | }
152 |
153 | /**
154 | * write encoded data to muxer
155 | * @param trackIndex
156 | * @param byteBuf
157 | * @param bufferInfo
158 | */
159 | /*package*/ synchronized void writeSampleData(final int trackIndex, final ByteBuffer byteBuf, final MediaCodec.BufferInfo bufferInfo) {
160 | if (mStatredCount > 0)
161 | mMediaMuxer.writeSampleData(trackIndex, byteBuf, bufferInfo);
162 | }
163 |
164 | //**********************************************************************
165 | //**********************************************************************
166 | /**
167 | * generate output file
168 | * @param type Environment.DIRECTORY_MOVIES / Environment.DIRECTORY_DCIM etc.
169 | * @param ext .mp4(.m4a for audio) or .png
170 | * @return return null when this app has no writing permission to external storage.
171 | */
172 | public static final File getCaptureFile(final String type, final String ext) {
173 | final File dir = new File(Environment.getExternalStoragePublicDirectory(type), DIR_NAME);
174 | Log.d(TAG, "path=" + dir.toString());
175 | dir.mkdirs();
176 | if (dir.canWrite()) {
177 | return new File(dir, getDateTimeString() + ext);
178 | }
179 | return null;
180 | }
181 |
182 | /**
183 | * get current date and time as String
184 | * @return
185 | */
186 | private static final String getDateTimeString() {
187 | final GregorianCalendar now = new GregorianCalendar();
188 | return mDateTimeFormat.format(now.getTime());
189 | }
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/MediaSurfaceEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.encoder;
25 |
26 | import android.media.MediaCodec;
27 | import android.media.MediaCodecInfo;
28 | import android.media.MediaCodecList;
29 | import android.media.MediaFormat;
30 | import android.util.Log;
31 | import android.view.Surface;
32 |
33 | import java.io.IOException;
34 |
35 | public class MediaSurfaceEncoder extends MediaEncoder implements IVideoEncoder {
36 | private static final boolean DEBUG = true; // TODO set false on release
37 | private static final String TAG = "MediaSurfaceEncoder";
38 |
39 | private static final String MIME_TYPE = "video/avc";
40 | // parameters for recording
41 | private final int mWidth, mHeight;
42 | private static final int FRAME_RATE = 15;
43 | private static final float BPP = 0.50f;
44 |
45 | private Surface mSurface;
46 |
47 | public MediaSurfaceEncoder(final MediaMuxerWrapper muxer, final int width, final int height, final MediaEncoderListener listener) {
48 | super(muxer, listener);
49 | if (DEBUG) Log.i(TAG, "MediaVideoEncoder: ");
50 | mWidth = width;
51 | mHeight = height;
52 | }
53 |
54 | /**
55 | * Returns the encoder's input surface.
56 | */
57 | public Surface getInputSurface() {
58 | return mSurface;
59 | }
60 |
61 | @Override
62 | protected void prepare() throws IOException {
63 | if (DEBUG) Log.i(TAG, "prepare: ");
64 | mTrackIndex = -1;
65 | mMuxerStarted = mIsEOS = false;
66 |
67 | final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
68 | if (videoCodecInfo == null) {
69 | Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
70 | return;
71 | }
72 | if (DEBUG) Log.i(TAG, "selected codec: " + videoCodecInfo.getName());
73 |
74 | final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
75 | format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // API >= 18
76 | format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
77 | format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
78 | format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
79 | if (DEBUG) Log.i(TAG, "format: " + format);
80 |
81 | mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
82 | mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
83 | // get Surface for encoder input
84 | // this method only can call between #configure and #start
85 | mSurface = mMediaCodec.createInputSurface(); // API >= 18
86 | mMediaCodec.start();
87 | if (DEBUG) Log.i(TAG, "prepare finishing");
88 | if (mListener != null) {
89 | try {
90 | mListener.onPrepared(this);
91 | } catch (final Exception e) {
92 | Log.e(TAG, "prepare:", e);
93 | }
94 | }
95 | }
96 |
97 | @Override
98 | protected void release() {
99 | if (DEBUG) Log.i(TAG, "release:");
100 | if (mSurface != null) {
101 | mSurface.release();
102 | mSurface = null;
103 | }
104 | super.release();
105 | }
106 |
107 | private int calcBitRate() {
108 | final int bitrate = (int)(BPP * FRAME_RATE * mWidth * mHeight);
109 | Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
110 | return bitrate;
111 | }
112 |
113 | /**
114 | * select the first codec that match a specific MIME type
115 | * @param mimeType
116 | * @return null if no codec matched
117 | */
118 | protected static final MediaCodecInfo selectVideoCodec(final String mimeType) {
119 | if (DEBUG) Log.v(TAG, "selectVideoCodec:");
120 |
121 | // get the list of available codecs
122 | final int numCodecs = MediaCodecList.getCodecCount();
123 | for (int i = 0; i < numCodecs; i++) {
124 | final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
125 |
126 | if (!codecInfo.isEncoder()) { // skipp decoder
127 | continue;
128 | }
129 | // select first codec that match a specific MIME type and color format
130 | final String[] types = codecInfo.getSupportedTypes();
131 | for (int j = 0; j < types.length; j++) {
132 | if (types[j].equalsIgnoreCase(mimeType)) {
133 | if (DEBUG) Log.i(TAG, "codec:" + codecInfo.getName() + ",MIME=" + types[j]);
134 | final int format = selectColorFormat(codecInfo, mimeType);
135 | if (format > 0) {
136 | return codecInfo;
137 | }
138 | }
139 | }
140 | }
141 | return null;
142 | }
143 |
144 | /**
145 | * select color format available on specific codec and we can use.
146 | * @return 0 if no colorFormat is matched
147 | */
148 | protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
149 | if (DEBUG) Log.i(TAG, "selectColorFormat: ");
150 | int result = 0;
151 | final MediaCodecInfo.CodecCapabilities caps;
152 | try {
153 | Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
154 | caps = codecInfo.getCapabilitiesForType(mimeType);
155 | } finally {
156 | Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
157 | }
158 | int colorFormat;
159 | for (int i = 0; i < caps.colorFormats.length; i++) {
160 | colorFormat = caps.colorFormats[i];
161 | if (isRecognizedVideoFormat(colorFormat)) {
162 | if (result == 0)
163 | result = colorFormat;
164 | break;
165 | }
166 | }
167 | if (result == 0)
168 | Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
169 | return result;
170 | }
171 |
172 | /**
173 | * color formats that we can use in this class
174 | */
175 | protected static int[] recognizedFormats;
176 | static {
177 | recognizedFormats = new int[] {
178 | // MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
179 | // MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
180 | // MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
181 | MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
182 | };
183 | }
184 |
185 | private static final boolean isRecognizedVideoFormat(final int colorFormat) {
186 | if (DEBUG) Log.i(TAG, "isRecognizedVideoFormat:colorFormat=" + colorFormat);
187 | final int n = recognizedFormats != null ? recognizedFormats.length : 0;
188 | for (int i = 0; i < n; i++) {
189 | if (recognizedFormats[i] == colorFormat) {
190 | return true;
191 | }
192 | }
193 | return false;
194 | }
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/MediaVideoBufferEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.encoder;
25 |
26 | import android.media.MediaCodec;
27 | import android.media.MediaCodecInfo;
28 | import android.media.MediaCodecList;
29 | import android.media.MediaFormat;
30 | import android.os.Build;
31 | import android.os.Bundle;
32 | import android.util.Log;
33 |
34 | import java.io.IOException;
35 | import java.nio.ByteBuffer;
36 |
37 | public class MediaVideoBufferEncoder extends MediaEncoder implements IVideoEncoder {
38 | private static final boolean DEBUG = true; // TODO set false on release
39 | private static final String TAG = "MediaVideoBufferEncoder";
40 |
41 | private static final String MIME_TYPE = "video/avc";
42 | private static final int FRAME_RATE = 15;
43 | private static final float BPP = 0.50f;
44 |
45 | private final int mWidth, mHeight;
46 | protected int mColorFormat;
47 |
48 | public MediaVideoBufferEncoder(final MediaMuxerWrapper muxer, final int width, final int height, final MediaEncoderListener listener) {
49 | super(muxer, listener);
50 | if (DEBUG) Log.i(TAG, "MediaVideoEncoder: ");
51 | mWidth = width;
52 | mHeight = height;
53 | }
54 |
55 | public void encode(final ByteBuffer buffer) {
56 | synchronized (mSync) {
57 | if (!mIsCapturing || mRequestStop) return;
58 | }
59 | // encode(buffer, buffer.capacity(), getPTSUs());
60 | encode(buffer, buffer.capacity());
61 | }
62 |
63 | @Override
64 | protected void prepare() throws IOException {
65 | if (DEBUG) Log.i(TAG, "prepare: ");
66 | mTrackIndex = -1;
67 | mMuxerStarted = mIsEOS = false;
68 |
69 | final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
70 | if (videoCodecInfo == null) {
71 | Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
72 | return;
73 | }
74 | if (DEBUG) Log.i(TAG, "selected codec: " + videoCodecInfo.getName());
75 |
76 | final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
77 | format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mColorFormat);
78 | format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
79 | format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
80 | format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
81 | if (DEBUG) Log.i(TAG, "format: " + format);
82 |
83 | mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
84 | mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
85 | mMediaCodec.start();
86 |
87 | Bundle params = new Bundle();
88 | params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
90 | mMediaCodec.setParameters(params);
91 | }
92 | if (DEBUG) Log.i(TAG, "prepare finishing");
93 | if (mListener != null) {
94 | try {
95 | mListener.onPrepared(this);
96 | } catch (final Exception e) {
97 | Log.e(TAG, "prepare:", e);
98 | }
99 | }
100 | }
101 |
102 | private int calcBitRate() {
103 | final int bitrate = (int)(BPP * FRAME_RATE * mWidth * mHeight);
104 | Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
105 | return bitrate;
106 | }
107 |
108 | // 选择第一个与制定MIME类型匹配的编码器
109 | @SuppressWarnings("deprecation")
110 | protected final MediaCodecInfo selectVideoCodec(final String mimeType) {
111 | if (DEBUG) Log.v(TAG, "selectVideoCodec:");
112 |
113 | // get the list of available codecs
114 | final int numCodecs = MediaCodecList.getCodecCount();
115 | for (int i = 0; i < numCodecs; i++) {
116 | final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
117 |
118 | if (!codecInfo.isEncoder()) { // skipp decoder
119 | continue;
120 | }
121 | // select first codec that match a specific MIME type and color format
122 | final String[] types = codecInfo.getSupportedTypes();
123 | for (int j = 0; j < types.length; j++) {
124 | if (types[j].equalsIgnoreCase(mimeType)) {
125 | if (DEBUG) Log.i(TAG, "codec:" + codecInfo.getName() + ",MIME=" + types[j]);
126 | final int format = selectColorFormat(codecInfo, mimeType);
127 | if (format > 0) {
128 | mColorFormat = format;
129 | return codecInfo;
130 | }
131 | }
132 | }
133 | }
134 | return null;
135 | }
136 |
137 | // 选择编码器支持的格式
138 | protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
139 | if (DEBUG) Log.i(TAG, "selectColorFormat: ");
140 | int result = 0;
141 | final MediaCodecInfo.CodecCapabilities caps;
142 | try {
143 | Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
144 | caps = codecInfo.getCapabilitiesForType(mimeType);
145 | } finally {
146 | Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
147 | }
148 | int colorFormat;
149 | for (int i = 0; i < caps.colorFormats.length; i++) {
150 | colorFormat = caps.colorFormats[i];
151 | if (isRecognizedViewoFormat(colorFormat)) {
152 | if (result == 0)
153 | result = colorFormat;
154 | break;
155 | }
156 | }
157 | if (result == 0)
158 | Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
159 | return result;
160 | }
161 |
162 | // YUV颜色格式
163 | protected static int[] recognizedFormats;
164 | static {
165 | recognizedFormats = new int[] {
166 | // MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
167 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
168 | MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
169 | // MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
170 | };
171 | }
172 |
173 | private static final boolean isRecognizedViewoFormat(final int colorFormat) {
174 | if (DEBUG) Log.i(TAG, "isRecognizedViewoFormat:colorFormat=" + colorFormat);
175 | final int n = recognizedFormats != null ? recognizedFormats.length : 0;
176 | for (int i = 0; i < n; i++) {
177 | if (recognizedFormats[i] == colorFormat) {
178 | return true;
179 | }
180 | }
181 | return false;
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/MediaVideoEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.encoder;
25 |
26 | import android.annotation.TargetApi;
27 | import android.media.MediaCodec;
28 | import android.media.MediaCodecInfo;
29 | import android.media.MediaCodecList;
30 | import android.media.MediaFormat;
31 | import android.os.Build;
32 | import android.util.Log;
33 | import android.view.Surface;
34 |
35 | import com.serenegiant.glutils.EGLBase;
36 | import com.serenegiant.glutils.RenderHandler;
37 |
38 | import java.io.IOException;
39 |
40 | /**
41 | * Encode texture images as H.264 video
42 | * using MediaCodec.
43 | * This class render texture images into recording surface
44 | * camera from MediaCodec encoder using Open GL|ES
45 | */
46 | public class MediaVideoEncoder extends MediaEncoder implements IVideoEncoder {
47 | private static final boolean DEBUG = true; // TODO set false on release
48 | private static final String TAG = "MediaVideoEncoder";
49 |
50 | private static final String MIME_TYPE = "video/avc";
51 | // parameters for recording
52 | private final int mWidth, mHeight;
53 | private static final int FRAME_RATE = 15;
54 | private static final float BPP = 0.50f;
55 |
56 | private RenderHandler mRenderHandler;
57 | private Surface mSurface;
58 |
59 | public MediaVideoEncoder(final MediaMuxerWrapper muxer, final int width, final int height, final MediaEncoderListener listener) {
60 | super(muxer, listener);
61 | if (DEBUG) Log.i(TAG, "MediaVideoEncoder: ");
62 | mRenderHandler = RenderHandler.createHandler(TAG);
63 | mWidth = width;
64 | mHeight = height;
65 | }
66 |
67 | public boolean frameAvailableSoon(final float[] tex_matrix) {
68 | boolean result;
69 | if (result = super.frameAvailableSoon())
70 | mRenderHandler.draw(tex_matrix);
71 | return result;
72 | }
73 |
74 | /**
75 | * This method does not work correctly on this class,
76 | * use #frameAvailableSoon(final float[]) instead
77 | * @return
78 | */
79 | @Override
80 | public boolean frameAvailableSoon() {
81 | boolean result;
82 | if (result = super.frameAvailableSoon())
83 | mRenderHandler.draw(null);
84 | return result;
85 | }
86 |
87 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
88 | @Override
89 | protected void prepare() throws IOException {
90 | if (DEBUG) Log.i(TAG, "prepare: ");
91 | mTrackIndex = -1;
92 | mMuxerStarted = mIsEOS = false;
93 |
94 | final MediaCodecInfo videoCodecInfo = selectVideoCodec(MIME_TYPE);
95 | if (videoCodecInfo == null) {
96 | Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
97 | return;
98 | }
99 | if (DEBUG) Log.i(TAG, "selected codec: " + videoCodecInfo.getName());
100 |
101 | final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
102 | format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // API >= 18
103 | format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
104 | format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
105 | format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
106 | if (DEBUG) Log.i(TAG, "format: " + format);
107 |
108 | mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
109 | mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
110 | // get Surface for encoder input
111 | // this method only can call between #configure and #start
112 | mSurface = mMediaCodec.createInputSurface(); // API >= 18
113 | mMediaCodec.start();
114 | if (DEBUG) Log.i(TAG, "prepare finishing");
115 | if (mListener != null) {
116 | try {
117 | mListener.onPrepared(this);
118 | } catch (final Exception e) {
119 | Log.e(TAG, "prepare:", e);
120 | }
121 | }
122 | }
123 |
124 | public void setEglContext(final EGLBase.IContext sharedContext, final int tex_id) {
125 | mRenderHandler.setEglContext(sharedContext, tex_id, mSurface, true);
126 | }
127 |
128 | @Override
129 | protected void release() {
130 | if (DEBUG) Log.i(TAG, "release:");
131 | if (mSurface != null) {
132 | mSurface.release();
133 | mSurface = null;
134 | }
135 | if (mRenderHandler != null) {
136 | mRenderHandler.release();
137 | mRenderHandler = null;
138 | }
139 | super.release();
140 | }
141 |
142 | private int calcBitRate() {
143 | final int bitrate = (int)(BPP * FRAME_RATE * mWidth * mHeight);
144 | Log.i(TAG, String.format("bitrate=%5.2f[Mbps]", bitrate / 1024f / 1024f));
145 | return bitrate;
146 | }
147 |
148 | /**
149 | * select the first codec that match a specific MIME type
150 | * @param mimeType
151 | * @return null if no codec matched
152 | */
153 | protected static final MediaCodecInfo selectVideoCodec(final String mimeType) {
154 | if (DEBUG) Log.v(TAG, "selectVideoCodec:");
155 |
156 | // get the list of available codecs
157 | final int numCodecs = MediaCodecList.getCodecCount();
158 | for (int i = 0; i < numCodecs; i++) {
159 | final MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
160 |
161 | if (!codecInfo.isEncoder()) { // skipp decoder
162 | continue;
163 | }
164 | // select first codec that match a specific MIME type and color format
165 | final String[] types = codecInfo.getSupportedTypes();
166 | for (int j = 0; j < types.length; j++) {
167 | if (types[j].equalsIgnoreCase(mimeType)) {
168 | if (DEBUG) Log.i(TAG, "codec:" + codecInfo.getName() + ",MIME=" + types[j]);
169 | final int format = selectColorFormat(codecInfo, mimeType);
170 | if (format > 0) {
171 | return codecInfo;
172 | }
173 | }
174 | }
175 | }
176 | return null;
177 | }
178 |
179 | /**
180 | * select color format available on specific codec and we can use.
181 | * @return 0 if no colorFormat is matched
182 | */
183 | protected static final int selectColorFormat(final MediaCodecInfo codecInfo, final String mimeType) {
184 | if (DEBUG) Log.i(TAG, "selectColorFormat: ");
185 | int result = 0;
186 | final MediaCodecInfo.CodecCapabilities caps;
187 | try {
188 | Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
189 | caps = codecInfo.getCapabilitiesForType(mimeType);
190 | } finally {
191 | Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
192 | }
193 | int colorFormat;
194 | for (int i = 0; i < caps.colorFormats.length; i++) {
195 | colorFormat = caps.colorFormats[i];
196 | if (isRecognizedVideoFormat(colorFormat)) {
197 | if (result == 0)
198 | result = colorFormat;
199 | break;
200 | }
201 | }
202 | if (result == 0)
203 | Log.e(TAG, "couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
204 | return result;
205 | }
206 |
207 | /**
208 | * color formats that we can use in this class
209 | */
210 | protected static int[] recognizedFormats;
211 | static {
212 | recognizedFormats = new int[] {
213 | // MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
214 | // MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
215 | // MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
216 | MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface,
217 | };
218 | }
219 |
220 | private static final boolean isRecognizedVideoFormat(final int colorFormat) {
221 | if (DEBUG) Log.i(TAG, "isRecognizedVideoFormat:colorFormat=" + colorFormat);
222 | final int n = recognizedFormats != null ? recognizedFormats.length : 0;
223 | for (int i = 0; i < n; i++) {
224 | if (recognizedFormats[i] == colorFormat) {
225 | return true;
226 | }
227 | }
228 | return false;
229 | }
230 |
231 | }
232 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/RecordParams.java:
--------------------------------------------------------------------------------
1 | package com.serenegiant.usb.encoder;
2 |
3 | /** 录制参数
4 | *
5 | * Created by jiangdongguo on 2017/10/19.
6 | */
7 |
8 | public class RecordParams {
9 | private String recordPath;
10 | private int recordDuration;
11 | private boolean voiceClose;
12 | private boolean isAutoSave;
13 | private boolean isSupportOverlay;
14 |
15 | public RecordParams() {
16 | }
17 |
18 | public boolean isSupportOverlay() {
19 | return isSupportOverlay;
20 | }
21 |
22 | public void setSupportOverlay(boolean supportOverlay) {
23 | isSupportOverlay = supportOverlay;
24 | }
25 |
26 | public boolean isVoiceClose() {
27 | return voiceClose;
28 | }
29 |
30 | public void setVoiceClose(boolean voiceClose) {
31 | this.voiceClose = voiceClose;
32 | }
33 |
34 | public String getRecordPath() {
35 | return recordPath;
36 | }
37 |
38 | public void setRecordPath(String recordPath) {
39 | this.recordPath = recordPath;
40 | }
41 |
42 | public int getRecordDuration() {
43 | return recordDuration;
44 | }
45 |
46 | public void setRecordDuration(int recordDuration) {
47 | this.recordDuration = recordDuration;
48 | }
49 |
50 | public boolean isAutoSave() {
51 | return isAutoSave;
52 | }
53 |
54 | public void setAutoSave(boolean autoSave) {
55 | isAutoSave = autoSave;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/AACEncodeConsumer.java:
--------------------------------------------------------------------------------
1 | package com.serenegiant.usb.encoder.biz;
2 |
3 | import android.annotation.TargetApi;
4 | import android.media.AudioFormat;
5 | import android.media.AudioRecord;
6 | import android.media.MediaCodec;
7 | import android.media.MediaCodecInfo;
8 | import android.media.MediaCodecList;
9 | import android.media.MediaFormat;
10 | import android.media.MediaRecorder;
11 | import android.os.Build;
12 | import android.os.Process;
13 | import android.util.Log;
14 |
15 | import java.io.File;
16 | import java.io.FileNotFoundException;
17 | import java.io.FileOutputStream;
18 | import java.io.IOException;
19 | import java.lang.ref.WeakReference;
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 | import java.nio.ShortBuffer;
23 | import java.text.SimpleDateFormat;
24 | import java.util.Date;
25 |
26 | /**将PCM编码为AAC
27 | *
28 | * Created by jianddongguo on 2017/7/21.
29 | */
30 |
31 | public class AACEncodeConsumer extends Thread{
32 | private static final boolean DEBUG = false;
33 | private static final String TAG = "TMPU";
34 | private static final String MIME_TYPE = "audio/mp4a-latm";
35 | private static final long TIMES_OUT = 1000;
36 | private static final int SAMPLE_RATE = 8000; // 采样率
37 | private static final int BIT_RATE = 16000; // 比特率
38 | private static final int BUFFER_SIZE = 1920; // 最小缓存
39 | private int outChannel = 1;
40 | private int bitRateForLame = 32;
41 | private int qaulityDegree = 7;
42 | private int bufferSizeInBytes;
43 |
44 | private AudioRecord mAudioRecord; // 音频采集
45 | private MediaCodec mAudioEncoder; // 音频编码
46 | private OnAACEncodeResultListener listener;
47 | private int mSamplingRateIndex = 0;//ADTS
48 | private boolean isEncoderStart = false;
49 | private boolean isRecMp3 = false;
50 | private boolean isExit = false;
51 | private long prevPresentationTimes = 0;
52 | private WeakReference mMuxerRef;
53 | private MediaFormat newFormat;
54 |
55 | private static final int[] AUDIO_SOURCES = new int[] {
56 | MediaRecorder.AudioSource.DEFAULT,
57 | MediaRecorder.AudioSource.MIC,
58 | MediaRecorder.AudioSource.CAMCORDER,
59 | };
60 | /**
61 | * There are 13 supported frequencies by ADTS.
62 | **/
63 | public static final int[] AUDIO_SAMPLING_RATES = { 96000, // 0
64 | 88200, // 1
65 | 64000, // 2
66 | 48000, // 3
67 | 44100, // 4
68 | 32000, // 5
69 | 24000, // 6
70 | 22050, // 7
71 | 16000, // 8
72 | 12000, // 9
73 | 11025, // 10
74 | 8000, // 11
75 | 7350, // 12
76 | -1, // 13
77 | -1, // 14
78 | -1, // 15
79 | };
80 |
81 | private FileOutputStream fops;
82 |
83 | // 编码流结果回调接口
84 | public interface OnAACEncodeResultListener{
85 | void onEncodeResult(byte[] data, int offset,
86 | int length, long timestamp);
87 | }
88 |
89 | public AACEncodeConsumer(){
90 | for (int i=0;i < AUDIO_SAMPLING_RATES.length; i++) {
91 | if (AUDIO_SAMPLING_RATES[i] == SAMPLE_RATE) {
92 | mSamplingRateIndex = i;
93 | break;
94 | }
95 | }
96 | }
97 |
98 | public void setOnAACEncodeResultListener(OnAACEncodeResultListener listener){
99 | this.listener = listener;
100 | }
101 |
102 | public void exit(){
103 | isExit = true;
104 | }
105 |
106 | public synchronized void setTmpuMuxer(Mp4MediaMuxer mMuxer){
107 | this.mMuxerRef = new WeakReference<>(mMuxer);
108 | Mp4MediaMuxer muxer = mMuxerRef.get();
109 | if (muxer != null && newFormat != null) {
110 | muxer.addTrack(newFormat, false);
111 | }
112 | }
113 |
114 | @Override
115 | public void run() {
116 | // 开启音频采集、编码
117 | if(! isEncoderStart){
118 | initAudioRecord();
119 | initMediaCodec();
120 | }
121 | // 初始化音频文件参数
122 | byte[] mp3Buffer = new byte[1024];
123 |
124 | // 这里有问题,当本地录制结束后,没有写入
125 | while(! isExit){
126 | byte[] audioBuffer = new byte[2048];
127 | // 采集音频
128 | int readBytes = mAudioRecord.read(audioBuffer,0,BUFFER_SIZE);
129 |
130 | if(DEBUG)
131 | Log.i(TAG,"采集音频readBytes = "+readBytes);
132 | // 编码音频
133 | if(readBytes > 0){
134 | encodeBytes(audioBuffer,readBytes);
135 | }
136 | }
137 | // 停止音频采集、编码
138 | stopMediaCodec();
139 | stopAudioRecord();
140 | }
141 |
142 |
143 | @TargetApi(21)
144 | private void encodeBytes(byte[] audioBuf, int readBytes) {
145 | ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
146 | ByteBuffer[] outputBuffers = mAudioEncoder.getOutputBuffers();
147 | //返回编码器的一个输入缓存区句柄,-1表示当前没有可用的输入缓存区
148 | int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(TIMES_OUT);
149 | if(inputBufferIndex >= 0){
150 | // 绑定一个被空的、可写的输入缓存区inputBuffer到客户端
151 | ByteBuffer inputBuffer = null;
152 | if(!isLollipop()){
153 | inputBuffer = inputBuffers[inputBufferIndex];
154 | }else{
155 | inputBuffer = mAudioEncoder.getInputBuffer(inputBufferIndex);
156 | }
157 | // 向输入缓存区写入有效原始数据,并提交到编码器中进行编码处理
158 | if(audioBuf==null || readBytes<=0){
159 | mAudioEncoder.queueInputBuffer(inputBufferIndex,0,0,getPTSUs(),MediaCodec.BUFFER_FLAG_END_OF_STREAM);
160 | }else{
161 | inputBuffer.clear();
162 | inputBuffer.put(audioBuf);
163 | mAudioEncoder.queueInputBuffer(inputBufferIndex,0,readBytes,getPTSUs(),0);
164 | }
165 | }
166 |
167 | // 返回一个输出缓存区句柄,当为-1时表示当前没有可用的输出缓存区
168 | // mBufferInfo参数包含被编码好的数据,timesOut参数为超时等待的时间
169 | MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
170 | int outputBufferIndex = -1;
171 | do{
172 | outputBufferIndex = mAudioEncoder.dequeueOutputBuffer(mBufferInfo,TIMES_OUT);
173 | if(outputBufferIndex == MediaCodec. INFO_TRY_AGAIN_LATER){
174 | if(DEBUG)
175 | Log.i(TAG,"获得编码器输出缓存区超时");
176 | }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
177 | // 如果API小于21,APP需要重新绑定编码器的输入缓存区;
178 | // 如果API大于21,则无需处理INFO_OUTPUT_BUFFERS_CHANGED
179 | if(!isLollipop()){
180 | outputBuffers = mAudioEncoder.getOutputBuffers();
181 | }
182 | }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
183 | // 编码器输出缓存区格式改变,通常在存储数据之前且只会改变一次
184 | // 这里设置混合器视频轨道,如果音频已经添加则启动混合器(保证音视频同步)
185 | if(DEBUG)
186 | Log.i(TAG,"编码器输出缓存区格式改变,添加视频轨道到混合器");
187 | synchronized (AACEncodeConsumer.this) {
188 | newFormat = mAudioEncoder.getOutputFormat();
189 | if(mMuxerRef != null){
190 | Mp4MediaMuxer muxer = mMuxerRef.get();
191 | if (muxer != null) {
192 | muxer.addTrack(newFormat, false);
193 | }
194 | }
195 | }
196 | }else{
197 | // 当flag属性置为BUFFER_FLAG_CODEC_CONFIG后,说明输出缓存区的数据已经被消费了
198 | if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0){
199 | if(DEBUG)
200 | Log.i(TAG,"编码数据被消费,BufferInfo的size属性置0");
201 | mBufferInfo.size = 0;
202 | }
203 | // 数据流结束标志,结束本次循环
204 | if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
205 | if(DEBUG)
206 | Log.i(TAG,"数据流结束,退出循环");
207 | break;
208 | }
209 | // 获取一个只读的输出缓存区inputBuffer ,它包含被编码好的数据
210 | ByteBuffer mBuffer = ByteBuffer.allocate(10240);
211 | ByteBuffer outputBuffer = null;
212 | if(!isLollipop()){
213 | outputBuffer = outputBuffers[outputBufferIndex];
214 | }else{
215 | outputBuffer = mAudioEncoder.getOutputBuffer(outputBufferIndex);
216 | }
217 | if(mBufferInfo.size != 0){
218 | // 获取输出缓存区失败,抛出异常
219 | if(outputBuffer == null){
220 | throw new RuntimeException("encodecOutputBuffer"+outputBufferIndex+"was null");
221 | }
222 | // 添加视频流到混合器
223 | if(mMuxerRef != null){
224 | Mp4MediaMuxer muxer = mMuxerRef.get();
225 | if (muxer != null) {
226 | muxer.pumpStream(outputBuffer, mBufferInfo, false);
227 | }
228 | }
229 | // AAC流添加ADTS头,缓存到mBuffer
230 | mBuffer.clear();
231 | outputBuffer.get(mBuffer.array(), 7, mBufferInfo.size);
232 | outputBuffer.clear();
233 | mBuffer.position(7 + mBufferInfo.size);
234 | addADTStoPacket(mBuffer.array(), mBufferInfo.size + 7);
235 | mBuffer.flip();
236 | // 将AAC回调给MainModelImpl进行push
237 | if(listener != null){
238 | Log.i(TAG,"----->得到aac数据流<-----");
239 | listener.onEncodeResult(mBuffer.array(),0, mBufferInfo.size + 7, mBufferInfo.presentationTimeUs / 1000);
240 | }
241 | }
242 | // 处理结束,释放输出缓存区资源
243 | mAudioEncoder.releaseOutputBuffer(outputBufferIndex,false);
244 | }
245 | }while (outputBufferIndex >= 0);
246 | }
247 |
248 | private void initAudioRecord(){
249 | if(DEBUG)
250 | Log.d(TAG,"AACEncodeConsumer-->开始采集音频");
251 | // 设置进程优先级
252 | Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
253 | int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
254 | AudioFormat.ENCODING_PCM_16BIT);
255 | for (final int src: AUDIO_SOURCES) {
256 | try {
257 | mAudioRecord = new AudioRecord(src,
258 | SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
259 | if (mAudioRecord != null) {
260 | if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
261 | mAudioRecord.release();
262 | mAudioRecord = null;
263 | }
264 | }
265 | } catch (final Exception e) {
266 | mAudioRecord = null;
267 | }
268 | if (mAudioRecord != null) {
269 | break;
270 | }
271 | }
272 | mAudioRecord.startRecording();
273 | }
274 |
275 | private void initMediaCodec(){
276 | if(DEBUG)
277 | Log.d(TAG,"AACEncodeConsumer-->开始编码音频");
278 | MediaCodecInfo mCodecInfo = selectSupportCodec(MIME_TYPE);
279 | if(mCodecInfo == null){
280 | Log.e(TAG,"编码器不支持"+MIME_TYPE+"类型");
281 | return;
282 | }
283 | try{
284 | mAudioEncoder = MediaCodec.createByCodecName(mCodecInfo.getName());
285 | }catch(IOException e){
286 | Log.e(TAG,"创建编码器失败"+e.getMessage());
287 | e.printStackTrace();
288 | }
289 | MediaFormat format = new MediaFormat();
290 | format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
291 | format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
292 | format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
293 | format.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
294 | format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
295 | format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, BUFFER_SIZE);
296 | mAudioEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
297 | mAudioEncoder.start();
298 | isEncoderStart = true;
299 | }
300 |
301 | private void stopAudioRecord() {
302 | if(DEBUG)
303 | Log.d(TAG,"AACEncodeConsumer-->停止采集音频");
304 | if(mAudioRecord != null){
305 | mAudioRecord.stop();
306 | mAudioRecord.release();
307 | mAudioRecord = null;
308 | }
309 | }
310 |
311 | private void stopMediaCodec() {
312 | if(DEBUG)
313 | Log.d(TAG,"AACEncodeConsumer-->停止编码音频");
314 | if(mAudioEncoder != null){
315 | mAudioEncoder.stop();
316 | mAudioEncoder.release();
317 | mAudioEncoder = null;
318 | }
319 | isEncoderStart = false;
320 | }
321 |
322 | // API>=21
323 | private boolean isLollipop(){
324 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
325 | }
326 |
327 | // API<=19
328 | private boolean isKITKAT(){
329 | return Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
330 | }
331 |
332 | private long getPTSUs(){
333 | long result = System.nanoTime()/1000;
334 | if(result < prevPresentationTimes){
335 | result = (prevPresentationTimes - result ) + result;
336 | }
337 | return result;
338 | }
339 |
340 | /**
341 | * 遍历所有编解码器,返回第一个与指定MIME类型匹配的编码器
342 | * 判断是否有支持指定mime类型的编码器
343 | * */
344 | private MediaCodecInfo selectSupportCodec(String mimeType){
345 | int numCodecs = MediaCodecList.getCodecCount();
346 | for (int i = 0; i < numCodecs; i++) {
347 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
348 | // 判断是否为编码器,否则直接进入下一次循环
349 | if (!codecInfo.isEncoder()) {
350 | continue;
351 | }
352 | // 如果是编码器,判断是否支持Mime类型
353 | String[] types = codecInfo.getSupportedTypes();
354 | for (int j = 0; j < types.length; j++) {
355 | if (types[j].equalsIgnoreCase(mimeType)) {
356 | return codecInfo;
357 | }
358 | }
359 | }
360 | return null;
361 | }
362 |
363 | private void addADTStoPacket(byte[] packet, int packetLen) {
364 | packet[0] = (byte) 0xFF;
365 | packet[1] = (byte) 0xF1;
366 | packet[2] = (byte) (((2 - 1) << 6) + (mSamplingRateIndex << 2) + (1 >> 2));
367 | packet[3] = (byte) (((1 & 3) << 6) + (packetLen >> 11));
368 | packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
369 | packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
370 | packet[6] = (byte) 0xFC;
371 | }
372 |
373 |
374 | private short[] transferByte2Short(byte[] data,int readBytes){
375 | // byte[] 转 short[],数组长度缩减一半
376 | int shortLen = readBytes / 2;
377 | // 将byte[]数组装如ByteBuffer缓冲区
378 | ByteBuffer byteBuffer = ByteBuffer.wrap(data, 0, readBytes);
379 | // 将ByteBuffer转成小端并获取shortBuffer
380 | ShortBuffer shortBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
381 | short[] shortData = new short[shortLen];
382 | shortBuffer.get(shortData, 0, shortLen);
383 | return shortData;
384 | }
385 | }
386 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/encoder/biz/Mp4MediaMuxer.java:
--------------------------------------------------------------------------------
1 | package com.serenegiant.usb.encoder.biz;
2 |
3 | import android.media.MediaCodec;
4 | import android.media.MediaFormat;
5 | import android.media.MediaMuxer;
6 | import android.os.Build;
7 | import android.util.Log;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 | import java.nio.ByteBuffer;
12 |
13 | /**Mp4封装混合器
14 | *
15 | * Created by jianddongguo on 2017/7/28.
16 | */
17 |
18 | public class Mp4MediaMuxer {
19 | private static final boolean VERBOSE = false;
20 | private static final String TAG = Mp4MediaMuxer.class.getSimpleName();
21 | private String mFilePath;
22 | private MediaMuxer mMuxer;
23 | private long durationMillis;
24 | private int index = 0;
25 | private int mVideoTrackIndex = -1;
26 | private int mAudioTrackIndex = -1;
27 | private long mBeginMillis;
28 | private MediaFormat mVideoFormat;
29 | private MediaFormat mAudioFormat;
30 | private boolean isVoiceClose;
31 |
32 | // 文件路径;文件时长
33 | public Mp4MediaMuxer(String path, long durationMillis,boolean isVoiceClose) {
34 | String mFilePath;
35 | this.isVoiceClose = isVoiceClose;
36 | this.durationMillis = durationMillis;
37 | if(durationMillis != 0) {
38 | mFilePath = path + "-" + index++ + ".mp4";
39 | }else{
40 | mFilePath = path+".mp4";
41 | }
42 | Object mux = null;
43 | try {
44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
45 | mux = new MediaMuxer(mFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
46 | }
47 | } catch (IOException e) {
48 | e.printStackTrace();
49 | } finally {
50 | mMuxer = (MediaMuxer) mux;
51 | }
52 | }
53 |
54 | public synchronized void addTrack(MediaFormat format, boolean isVideo) {
55 | // now that we have the Magic Goodies, start the muxer
56 | if ((!isVoiceClose && mAudioTrackIndex != -1) && mVideoTrackIndex != -1)
57 | throw new RuntimeException("already add all tracks");
58 |
59 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
60 | int track = mMuxer.addTrack(format);
61 | if (VERBOSE)
62 | Log.i(TAG, String.format("addTrack %s result %d", isVideo ? "video" : "audio", track));
63 | if (isVideo) {
64 | mVideoFormat = format;
65 | mVideoTrackIndex = track;
66 | // 当音频轨道添加
67 | // 或者开启静音就start
68 | if (isVoiceClose || mAudioTrackIndex != -1) {
69 | if (VERBOSE)
70 | Log.i(TAG, "both audio and video added,and muxer is started");
71 | mMuxer.start();
72 | mBeginMillis = System.currentTimeMillis();
73 | }
74 | } else {
75 | mAudioFormat = format;
76 | mAudioTrackIndex = track;
77 | if (mVideoTrackIndex != -1) {
78 | mMuxer.start();
79 | mBeginMillis = System.currentTimeMillis();
80 | }
81 | }
82 | }
83 | }
84 |
85 | public synchronized void pumpStream(ByteBuffer outputBuffer, MediaCodec.BufferInfo bufferInfo, boolean isVideo) {
86 | if ((!isVoiceClose && mAudioTrackIndex == -1) || mVideoTrackIndex == -1) {
87 | // Log.i(TAG, String.format("pumpStream [%s] but muxer is not start.ignore..", isVideo ? "video" : "audio"));
88 | return;
89 | }
90 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
91 | // The codec config data was pulled out and fed to the muxer when we got
92 | // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
93 | } else if (bufferInfo.size != 0) {
94 | if (isVideo && mVideoTrackIndex == -1) {
95 | throw new RuntimeException("muxer hasn't started");
96 | }
97 |
98 | // adjust the ByteBuffer values to match BufferInfo (not needed?)
99 | outputBuffer.position(bufferInfo.offset);
100 | outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
101 |
102 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
103 | mMuxer.writeSampleData(isVideo ? mVideoTrackIndex : mAudioTrackIndex, outputBuffer, bufferInfo);
104 | }
105 | // if (VERBOSE)
106 | // Log.d(TAG, String.format("sent %s [" + bufferInfo.size + "] with timestamp:[%d] to muxer", isVideo ? "video" : "audio", bufferInfo.presentationTimeUs / 1000));
107 | }
108 |
109 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
110 | // if (VERBOSE)
111 | // Log.i(TAG, "BUFFER_FLAG_END_OF_STREAM received");
112 | }
113 |
114 | if (durationMillis!=0 && System.currentTimeMillis() - mBeginMillis >= durationMillis) {
115 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
116 | // if (VERBOSE)
117 | // Log.i(TAG, String.format("record file reach expiration.create new file:" + index));
118 | mMuxer.stop();
119 | mMuxer.release();
120 | mMuxer = null;
121 | mVideoTrackIndex = mAudioTrackIndex = -1;
122 | try {
123 | mMuxer = new MediaMuxer(mFilePath + "-" + ++index + ".mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
124 | addTrack(mVideoFormat, true);
125 | addTrack(mAudioFormat, false);
126 | } catch (IOException e) {
127 | e.printStackTrace();
128 | }
129 | }
130 | }
131 | }
132 |
133 | public synchronized void release() {
134 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
135 | if (mMuxer != null) {
136 | //(!isVoiceClose&&mAudioTrackIndex != -1)
137 | if (mVideoTrackIndex != -1) {
138 | if (VERBOSE)
139 | Log.i(TAG, String.format("muxer is started. now it will be stoped."));
140 | try {
141 | mMuxer.stop();
142 | mMuxer.release();
143 | } catch (IllegalStateException ex) {
144 | ex.printStackTrace();
145 | }
146 |
147 | if (System.currentTimeMillis() - mBeginMillis <= 1500){
148 | new File(mFilePath + "-" + index + ".mp4").delete();
149 | }
150 | mAudioTrackIndex = mVideoTrackIndex = -1;
151 | }else{
152 | if (VERBOSE)
153 | Log.i(TAG, String.format("muxer is failed to be stoped."));
154 | }
155 | }
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/widget/AspectRatioTextureView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.widget;
25 |
26 | import android.content.Context;
27 | import android.util.AttributeSet;
28 | import android.view.TextureView;
29 |
30 | import com.serenegiant.widget.IAspectRatioView;
31 |
32 | /**
33 | * change the view size with keeping the specified aspect ratio.
34 | * if you set this view with in a FrameLayout and set property "android:layout_gravity="center",
35 | * you can show this view in the center of screen and keep the aspect ratio of content
36 | * XXX it is better that can set the aspect ratio as xml property
37 | */
38 | public class AspectRatioTextureView extends TextureView // API >= 14
39 | implements IAspectRatioView {
40 |
41 | private static final boolean DEBUG = true; // TODO set false on release
42 | private static final String TAG = "AbstractCameraView";
43 |
44 | private double mRequestedAspect = -1.0;
45 | private CameraViewInterface.Callback mCallback;
46 |
47 | public AspectRatioTextureView(final Context context) {
48 | this(context, null, 0);
49 | }
50 |
51 | public AspectRatioTextureView(final Context context, final AttributeSet attrs) {
52 | this(context, attrs, 0);
53 | }
54 |
55 | public AspectRatioTextureView(final Context context, final AttributeSet attrs, final int defStyle) {
56 | super(context, attrs, defStyle);
57 | }
58 |
59 | // 设置屏幕宽高比
60 | @Override
61 | public void setAspectRatio(final double aspectRatio) {
62 | if (aspectRatio < 0) {
63 | throw new IllegalArgumentException();
64 | }
65 | if (mRequestedAspect != aspectRatio) {
66 | mRequestedAspect = aspectRatio;
67 | requestLayout();
68 | }
69 | }
70 |
71 | @Override
72 | public void setAspectRatio(final int width, final int height) {
73 | setAspectRatio(width / (double)height);
74 | }
75 |
76 | @Override
77 | public double getAspectRatio() {
78 | return mRequestedAspect;
79 | }
80 |
81 | @Override
82 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
83 |
84 | if (mRequestedAspect > 0) {
85 | int initialWidth = MeasureSpec.getSize(widthMeasureSpec);
86 | int initialHeight = MeasureSpec.getSize(heightMeasureSpec);
87 |
88 | final int horizPadding = getPaddingLeft() + getPaddingRight();
89 | final int vertPadding = getPaddingTop() + getPaddingBottom();
90 | initialWidth -= horizPadding;
91 | initialHeight -= vertPadding;
92 |
93 | final double viewAspectRatio = (double)initialWidth / initialHeight;
94 | final double aspectDiff = mRequestedAspect / viewAspectRatio - 1;
95 |
96 | if (Math.abs(aspectDiff) > 0.01) {
97 | if (aspectDiff > 0) {
98 | // width priority decision
99 | initialHeight = (int) (initialWidth / mRequestedAspect);
100 | } else {
101 | // height priority decision
102 | initialWidth = (int) (initialHeight * mRequestedAspect);
103 | }
104 | initialWidth += horizPadding;
105 | initialHeight += vertPadding;
106 | widthMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY);
107 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY);
108 | }
109 | }
110 |
111 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/com/serenegiant/usb/widget/CameraViewInterface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * UVCCamera
3 | * library and sample to access to UVC web camera on non-rooted Android device
4 | *
5 | * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * All files in the folder are under this Apache License, Version 2.0.
20 | * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
21 | * may have a different license, see the respective files.
22 | */
23 |
24 | package com.serenegiant.usb.widget;
25 |
26 | import android.graphics.Bitmap;
27 | import android.graphics.SurfaceTexture;
28 | import android.view.Surface;
29 |
30 | import com.serenegiant.usb.encoder.IVideoEncoder;
31 | import com.serenegiant.widget.IAspectRatioView;
32 |
33 | public interface CameraViewInterface extends IAspectRatioView {
34 | public interface Callback {
35 | public void onSurfaceCreated(CameraViewInterface view, Surface surface);
36 | public void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height);
37 | public void onSurfaceDestroy(CameraViewInterface view, Surface surface);
38 | }
39 | public void onPause();
40 | public void onResume();
41 | public void setCallback(Callback callback);
42 | public SurfaceTexture getSurfaceTexture();
43 | public Surface getSurface();
44 | public boolean hasSurface();
45 | public void setVideoEncoder(final IVideoEncoder encoder);
46 | public Bitmap captureStillImage(int width,int height);
47 | }
48 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/org/easydarwin/sw/JNIUtil.java:
--------------------------------------------------------------------------------
1 | package org.easydarwin.sw;
2 |
3 | /**
4 | */
5 | public class JNIUtil {
6 |
7 | static {
8 | System.loadLibrary("Utils");
9 | }
10 |
11 | /**
12 | * 都是Y:U:V = 4:1:1但 U与 V顺序相反。变换可逆
13 | *
14 | * @param buffer
15 | * @param width
16 | * @param height
17 | */
18 | public static void yV12ToYUV420P(byte[] buffer, int width, int height) {
19 | callMethod("YV12ToYUV420P", null, buffer, width, height);
20 | }
21 |
22 | /**
23 | * 都是Y:U+V = 4:2,但是这两者U、V方向相反。变换可逆
24 | *
25 | * @param buffer
26 | * @param width
27 | * @param height
28 | */
29 | public static void nV21To420SP(byte[] buffer, int width, int height) {
30 | callMethod("NV21To420SP", null, buffer, width, height);
31 | }
32 |
33 | /**
34 | * 旋转1个字节为单位的矩阵
35 | *
36 | * @param data 要旋转的矩阵
37 | * @param offset 偏移量
38 | * @param width 宽度
39 | * @param height 高度
40 | * @param degree 旋转度数
41 | */
42 | public static void rotateMatrix(byte[] data, int offset, int width, int height, int degree) {
43 | callMethod("RotateByteMatrix", null, data, offset, width, height, degree);
44 | }
45 |
46 | /**
47 | * 旋转2个字节为单位的矩阵
48 | *
49 | * @param data 要旋转的矩阵
50 | * @param offset 偏移量
51 | * @param width 宽度
52 | * @param height 高度
53 | * @param degree 旋转度数
54 | */
55 | public static void rotateShortMatrix(byte[] data, int offset, int width, int height, int degree) {
56 | callMethod("RotateShortMatrix", null, data, offset, width, height, degree);
57 | }
58 |
59 | private static native void callMethod(String methodName, Object[] returnValue, Object... params);
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/java/org/easydarwin/sw/TxtOverlay.java:
--------------------------------------------------------------------------------
1 | package org.easydarwin.sw;
2 |
3 | import android.content.Context;
4 | import android.content.res.AssetManager;
5 |
6 | import java.io.File;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 |
11 | /**
12 | * Created by jiangdg on 2020/1/11.
13 | */
14 |
15 | public class TxtOverlay {
16 |
17 | static {
18 | System.loadLibrary("TxtOverlay");
19 | }
20 |
21 | private static TxtOverlay instance;
22 | private final Context context;
23 | private long ctx;
24 |
25 | private TxtOverlay(Context context){
26 | this.context = context;
27 | }
28 |
29 | public static TxtOverlay getInstance() {
30 | if(instance == null) {
31 | throw new IllegalArgumentException("please call install in your application!");
32 | }
33 | return instance;
34 | }
35 |
36 | public static void install(Context context) {
37 | if(instance == null) {
38 | instance = new TxtOverlay(context.getApplicationContext());
39 |
40 | File youyuan = context.getFileStreamPath("SIMYOU.ttf");
41 | if (!youyuan.exists()){
42 | AssetManager am = context.getAssets();
43 | try {
44 | InputStream is = am.open("zk/SIMYOU.ttf");
45 | FileOutputStream os = context.openFileOutput("SIMYOU.ttf", Context.MODE_PRIVATE);
46 | byte[] buffer = new byte[1024];
47 | int len = 0;
48 | while ((len = is.read(buffer)) != -1) {
49 | os.write(buffer, 0, len);
50 | }
51 | os.close();
52 | is.close();
53 |
54 | } catch (IOException e) {
55 | e.printStackTrace();
56 | }
57 | }
58 | }
59 | }
60 |
61 | public void init(int width, int height) {
62 | File youyuan = context.getFileStreamPath("SIMYOU.ttf");
63 | if (!youyuan.exists()){
64 | throw new IllegalArgumentException("the font file must be exists,please call install before!");
65 | }
66 | ctx = txtOverlayInit(width, height,youyuan.getAbsolutePath());
67 | }
68 |
69 | public void overlay(byte[] data,
70 | String txt) {
71 | // txt = "drawtext=fontfile="+context.getFileStreamPath("SIMYOU.ttf")+": text='EasyPusher 2017':x=(w-text_w)/2:y=H-60 :fontcolor=white :box=1:boxcolor=0x00000000@0.3";
72 | // txt = "movie=/sdcard/qrcode.png [logo];[in][logo] "
73 | // + "overlay=" + 0 + ":" + 0
74 | // + " [out]";
75 | // if (ctx == 0) throw new RuntimeException("init should be called at first!");
76 | if (ctx == 0) return;
77 | txtOverlay(ctx, data, txt);
78 | }
79 |
80 | public void release() {
81 | if (ctx == 0) return;
82 | txtOverlayRelease(ctx);
83 | ctx = 0;
84 | }
85 |
86 |
87 | private static native long txtOverlayInit(int width, int height, String fonts);
88 |
89 | private static native void txtOverlay(long ctx, byte[] data, String txt);
90 |
91 | private static native void txtOverlayRelease(long ctx);
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/arm64-v8a/libTxtOverlay.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/arm64-v8a/libTxtOverlay.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/arm64-v8a/libUVCCamera.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/arm64-v8a/libUVCCamera.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/arm64-v8a/libUtils.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/arm64-v8a/libUtils.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/arm64-v8a/libjpeg-turbo1500.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/arm64-v8a/libjpeg-turbo1500.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/arm64-v8a/libusb100.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/arm64-v8a/libusb100.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/arm64-v8a/libuvc.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/arm64-v8a/libuvc.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/armeabi-v7a/libTxtOverlay.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/armeabi-v7a/libTxtOverlay.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/armeabi-v7a/libUVCCamera.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/armeabi-v7a/libUVCCamera.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/armeabi-v7a/libUtils.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/armeabi-v7a/libUtils.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/armeabi-v7a/libjpeg-turbo1500.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/armeabi-v7a/libjpeg-turbo1500.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/armeabi-v7a/libusb100.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/armeabi-v7a/libusb100.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/armeabi-v7a/libuvc.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/armeabi-v7a/libuvc.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86/libUVCCamera.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86/libUVCCamera.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86/libjpeg-turbo1500.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86/libjpeg-turbo1500.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86/libusb100.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86/libusb100.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86/libuvc.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86/libuvc.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86_64/libUVCCamera.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86_64/libUVCCamera.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86_64/libjpeg-turbo1500.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86_64/libjpeg-turbo1500.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86_64/libusb100.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86_64/libusb100.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/jniLibs/x86_64/libuvc.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/jniLibs/x86_64/libuvc.so
--------------------------------------------------------------------------------
/libusbcamera/src/main/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/res/layout/dialog_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
19 |
20 |
24 |
25 |
30 |
31 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/res/layout/listitem_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/res/raw/camera_click.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quantum6/Android-USB-OTG-Camera/9b4383d3202b0c72a757a471decaec87fb742fa0/libusbcamera/src/main/res/raw/camera_click.ogg
--------------------------------------------------------------------------------
/libusbcamera/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 | 16dp
26 | 16dp
27 | 48dp
28 | 18sp
29 | 32dp
30 |
31 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | libusbcamera
3 | 请选择USB摄像头
4 | 刷新
5 | Camera
6 | 未搜索到可用USB Camera设备
7 |
8 |
--------------------------------------------------------------------------------
/libusbcamera/src/main/res/xml/device_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/libusbcamera/src/test/java/com/jiangdg/libusbcamera/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.jiangdg.libusbcamera;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':libusbcamera'
2 |
--------------------------------------------------------------------------------