├── .gitignore
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── you
│ │ └── chen
│ │ └── media
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ ├── ConvertNV.cpp
│ │ ├── ConvertNV.h
│ │ ├── Log.h
│ │ ├── RotateNV_UV.cpp
│ │ ├── RotateNV_UV.h
│ │ ├── include
│ │ │ ├── libyuv.h
│ │ │ └── libyuv
│ │ │ │ ├── basic_types.h
│ │ │ │ ├── compare.h
│ │ │ │ ├── compare_row.h
│ │ │ │ ├── convert.h
│ │ │ │ ├── convert_argb.h
│ │ │ │ ├── convert_from.h
│ │ │ │ ├── convert_from_argb.h
│ │ │ │ ├── cpu_id.h
│ │ │ │ ├── macros_msa.h
│ │ │ │ ├── mjpeg_decoder.h
│ │ │ │ ├── planar_functions.h
│ │ │ │ ├── rotate.h
│ │ │ │ ├── rotate_argb.h
│ │ │ │ ├── rotate_row.h
│ │ │ │ ├── row.h
│ │ │ │ ├── scale.h
│ │ │ │ ├── scale_argb.h
│ │ │ │ ├── scale_row.h
│ │ │ │ ├── version.h
│ │ │ │ └── video_common.h
│ │ ├── jniLibs
│ │ │ ├── arm64-v8a
│ │ │ │ └── libyuv.so
│ │ │ ├── armeabi-v7a
│ │ │ │ └── libyuv.so
│ │ │ ├── x86
│ │ │ │ └── libyuv.so
│ │ │ └── x86_64
│ │ │ │ └── libyuv.so
│ │ └── native-lib.cpp
│ ├── java
│ │ └── you
│ │ │ └── chen
│ │ │ └── media
│ │ │ ├── App.java
│ │ │ ├── YuvTests.java
│ │ │ ├── camera
│ │ │ ├── CameraHelper.java
│ │ │ ├── CameraUtils.java
│ │ │ ├── OrientationHelper.java
│ │ │ ├── SizeFilter.java
│ │ │ └── impl
│ │ │ │ ├── HightSizeFilter.java
│ │ │ │ ├── PictureSizeFilter.java
│ │ │ │ └── VideoSizeFilter.java
│ │ │ ├── core
│ │ │ ├── BytePool.java
│ │ │ ├── Constant.java
│ │ │ ├── MediaEncoder.java
│ │ │ ├── MediaUtils.java
│ │ │ ├── Orientation.java
│ │ │ ├── Transform.java
│ │ │ ├── YuvUtils.java
│ │ │ ├── audio
│ │ │ │ ├── AudioCallback.java
│ │ │ │ ├── AudioMuxerCallback.java
│ │ │ │ ├── AudioPresentationTime.java
│ │ │ │ ├── AudioRecorder.java
│ │ │ │ ├── AudioTaker.java
│ │ │ │ └── AudioTransform.java
│ │ │ ├── h264
│ │ │ │ ├── AvcTransform.java
│ │ │ │ ├── ClipAvcTransform.java
│ │ │ │ ├── H264Callback.java
│ │ │ │ ├── H264MuxerCallback.java
│ │ │ │ └── H264Utils.java
│ │ │ ├── mp4
│ │ │ │ └── Mp4Recorder.java
│ │ │ └── scan
│ │ │ │ ├── DecodeThread.java
│ │ │ │ ├── DecoderHandler.java
│ │ │ │ ├── FormatDecoder.java
│ │ │ │ └── SoundVibratorHelper.java
│ │ │ ├── rx
│ │ │ ├── NothingSubscribe.java
│ │ │ ├── RequestRetry.java
│ │ │ ├── RxUtils.java
│ │ │ ├── ThrowableConsumer.java
│ │ │ ├── perm
│ │ │ │ ├── Permission.java
│ │ │ │ ├── PermissionFragment.java
│ │ │ │ ├── PermissionMustCallback.java
│ │ │ │ └── RxPermissions.java
│ │ │ └── viewbind
│ │ │ │ ├── TextViewChangedOnSubscribe.java
│ │ │ │ └── ViewClickOnSubscribe.java
│ │ │ ├── ui
│ │ │ ├── AacActivity.java
│ │ │ ├── CameraActivity.java
│ │ │ ├── H264Activity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── Mp4Activity.java
│ │ │ ├── ScanActivity.java
│ │ │ ├── TestActivity.java
│ │ │ └── ViewTestActivity.java
│ │ │ ├── utils
│ │ │ ├── BitmapUtils.java
│ │ │ ├── FileUtils.java
│ │ │ ├── LogUtils.java
│ │ │ ├── Utils.java
│ │ │ └── ViewUtils.java
│ │ │ └── widget
│ │ │ ├── CameraView.java
│ │ │ ├── FlashView.java
│ │ │ ├── FocusView.java
│ │ │ ├── ScanFormatView.java
│ │ │ └── ToggleRadioButton.java
│ └── res
│ │ ├── color
│ │ └── flash_camera.xml
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xxhdpi
│ │ ├── flash_camera_s.png
│ │ ├── flash_camera_un.png
│ │ ├── focus_camera.png
│ │ ├── line_scan_camera.png
│ │ ├── qrcode_scan_line.png
│ │ ├── scan_success.png
│ │ └── switch_camera.png
│ │ ├── drawable
│ │ ├── flash_camera.xml
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── act_aac.xml
│ │ ├── act_camera.xml
│ │ ├── act_h264.xml
│ │ ├── act_main.xml
│ │ ├── act_mp4.xml
│ │ ├── act_pcm.xml
│ │ ├── act_scan.xml
│ │ ├── act_test.xml
│ │ ├── act_viewtest.xml
│ │ └── include_flash.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── raw
│ │ └── beep.ogg
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── you
│ └── chen
│ └── media
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.DS_Store
3 | /.gradle
4 | /.idea
5 | /build
6 | /gradle
7 | /gradlew
8 | /gradlew.bat
9 | /local.properties
10 | /captures
11 | .externalNativeBuild
12 | .cxx
13 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.2"
6 | defaultConfig {
7 | applicationId "you.chen.media"
8 | minSdkVersion 21
9 | targetSdkVersion 29
10 | versionCode 100
11 | versionName "1.0.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 |
14 | ndk {
15 | abiFilters "arm64-v8a", "armeabi-v7a"
16 | }
17 |
18 | externalNativeBuild {
19 | cmake {
20 | cppFlags ""
21 | abiFilters "arm64-v8a", "armeabi-v7a"
22 | }
23 | }
24 | }
25 |
26 | sourceSets {
27 | main {
28 | jni.srcDirs = []
29 | jniLibs.srcDirs = ['src/main/cpp/jniLibs']
30 | }
31 | }
32 |
33 | compileOptions {
34 | sourceCompatibility JavaVersion.VERSION_1_8
35 | targetCompatibility JavaVersion.VERSION_1_8
36 | }
37 |
38 | lintOptions { //忽略Manifest警告
39 | disable 'GoogleAppIndexingWarning'
40 | }
41 |
42 | buildTypes {
43 | release {
44 | minifyEnabled false
45 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
46 | }
47 | }
48 |
49 | externalNativeBuild {
50 | cmake {
51 | path "src/main/cpp/CMakeLists.txt"
52 | version "3.10.2"
53 | }
54 | }
55 | }
56 |
57 | dependencies {
58 | implementation fileTree(include: ['*.jar'], dir: 'libs')
59 | implementation 'androidx.appcompat:appcompat:1.0.2'
60 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
61 | testImplementation 'junit:junit:4.12'
62 | androidTestImplementation 'androidx.test.ext:junit:1.1.0'
63 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
64 | implementation 'io.reactivex.rxjava2:rxjava:2.2.16'
65 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
66 | implementation 'com.google.zxing:core:3.3.3'
67 | }
68 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/you/chen/media/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package you.chen.media;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("you.chen.media", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
41 |
42 |
44 |
45 |
47 |
48 |
49 |
50 |
52 |
53 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 |
6 | cmake_minimum_required(VERSION 3.4.1)
7 |
8 | #include头文件目录
9 | include_directories(${PROJECT_SOURCE_DIR}/include)
10 |
11 | # 指定链接库文件目录
12 | link_directories(${PROJECT_SOURCE_DIR}/jniLibs/${ANDROID_ABI})
13 |
14 | #设置所有.cpp文件路径
15 | file(GLOB SRC_CPP_LIST ${PROJECT_SOURCE_DIR}/*.cpp)
16 |
17 | add_library(
18 | yuv-utils
19 | SHARED
20 | ${SRC_CPP_LIST})
21 |
22 | target_link_libraries(
23 | yuv-utils
24 | # libyuv.so
25 | yuv
26 | log)
--------------------------------------------------------------------------------
/app/src/main/cpp/ConvertNV.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by you on 2018-09-06. NV21,NV12之间的UV剪切并旋转操作
3 | * NV21与NV12区别只是UV与VU交差的区别,因此以下方法NV21,NV12可通用
4 | */
5 |
6 | #include "ConvertNV.h"
7 |
8 | /**
9 | * 同时剪切NV21数据并旋转
10 | * @param sample
11 | * @param sample_size
12 | * @param dst_y
13 | * @param dst_stride_y
14 | * @param dst_u
15 | * @param dst_stride_u
16 | * @param dst_v
17 | * @param dst_stride_v
18 | * @param crop_x
19 | * @param crop_y
20 | * @param src_width
21 | * @param src_height
22 | * @param crop_width
23 | * @param crop_height
24 | * @param mode
25 | */
26 | void ConvertNV21(const uint8_t* sample,
27 | uint8_t* dst_y,
28 | int dst_stride_y,
29 | uint8_t* dst_vu,
30 | int dst_stride_vu,
31 | int crop_x,
32 | int crop_y,
33 | int src_width,
34 | int src_height,
35 | int crop_width,
36 | int crop_height,
37 | libyuv::RotationMode mode) {
38 | const uint8_t *src = sample + (src_width * crop_y + crop_x);
39 | const uint8_t* src_vu = sample + (src_width * src_height) +
40 | ((crop_y / 2) * src_width) + ((crop_x / 2) * 2);
41 | NV21Rotate(src, src_width, src_vu, src_width, dst_y, dst_stride_y,
42 | dst_vu, dst_stride_vu, crop_width, crop_height, mode);
43 | }
44 |
45 | /**
46 | * 同时NV21转NV12并旋转
47 | * @param src_y
48 | * @param src_stride_y
49 | * @param src_uv
50 | * @param src_stride_uv
51 | * @param dst_y
52 | * @param dst_stride_y
53 | * @param dst_uv
54 | * @param dst_stride_uv
55 | * @param width
56 | * @param height
57 | * @param mode
58 | */
59 | void NV21ToNV12Rotate(const uint8_t* src_y,
60 | int src_stride_y,
61 | const uint8_t* src_vu,
62 | int src_stride_vu,
63 | uint8_t* dst_y,
64 | int dst_stride_y,
65 | uint8_t* dst_uv,
66 | int dst_stride_uv,
67 | int width,
68 | int height,
69 | libyuv::RotationMode mode) {
70 | if (mode == libyuv::kRotate0) {
71 | libyuv::NV21ToNV12(src_y, src_stride_y, src_vu, src_stride_vu,
72 | dst_y, dst_stride_y, dst_uv, dst_stride_uv, width, height);
73 | return;
74 | }
75 |
76 | int uv16Width = (width + 1) >> 1;
77 | int uv16Height = (height + 1) >> 1;
78 |
79 | const uint16_t *src = reinterpret_cast(src_vu);
80 | int src_stride = (src_stride_vu + 1) >> 1;
81 | uint16_t *dst = reinterpret_cast(dst_uv);
82 | int dst_stride = (dst_stride_uv + 1) >> 1;
83 |
84 | switch (mode) {
85 | case libyuv::kRotate90:
86 | libyuv::RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
87 | RotateNV_UV90_X(src, src_stride, dst, dst_stride, uv16Width, uv16Height);//
88 | break;
89 | case libyuv::kRotate270:
90 | libyuv::RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
91 | RotateNV_UV270_X(src, src_stride, dst, dst_stride, uv16Width, uv16Height);
92 | break;
93 | case libyuv::kRotate180:
94 | libyuv::RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
95 | // RotateNV_UV180_X(src, dst, uv16Width * uv16Height);
96 | RotateNV_UV180_X(src_vu, src_stride_vu, dst_uv, dst_stride_uv, width, uv16Height);
97 | break;
98 | }
99 | }
100 |
101 | /**
102 | * 同时裁剪NV21转NV12并旋转
103 | * @param sample
104 | * @param dst_y
105 | * @param dst_stride_y
106 | * @param dst_uv
107 | * @param dst_stride_uv
108 | * @param crop_x
109 | * @param crop_y
110 | * @param src_width
111 | * @param src_height
112 | * @param crop_width
113 | * @param crop_height
114 | * @param mode
115 | */
116 | void ConvertNV21ToNV12(const uint8_t* sample,
117 | uint8_t* dst_y,
118 | int dst_stride_y,
119 | uint8_t* dst_uv,
120 | int dst_stride_uv,
121 | int crop_x,
122 | int crop_y,
123 | int src_width,
124 | int src_height,
125 | int crop_width,
126 | int crop_height,
127 | libyuv::RotationMode mode) {
128 |
129 | const uint8_t *src = sample + (src_width * crop_y + crop_x);
130 | const uint8_t* src_vu = sample + (src_width * src_height) +
131 | ((crop_y / 2) * src_width) + ((crop_x / 2) * 2);
132 | NV21ToNV12Rotate(src, src_width, src_vu, src_width, dst_y, dst_stride_y,
133 | dst_uv, dst_stride_uv, crop_width, crop_height, mode);
134 | }
135 |
--------------------------------------------------------------------------------
/app/src/main/cpp/ConvertNV.h:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Created by you on 2018-09-06. NV21,NV12之间的UV剪切并旋转操作
4 | * NV21与NV12区别只是UV与VU交差的区别,因此以下方法NV21,NV12可通用
5 | */
6 | #include "RotateNV_UV.h"
7 |
8 | #ifndef CAMERAMEDIACODEC_CONVERTNV_H
9 | #define CAMERAMEDIACODEC_CONVERTNV_H
10 |
11 | /**
12 | * 同时剪切NV21数据并旋转
13 | * @param sample
14 | * @param sample_size
15 | * @param dst_y
16 | * @param dst_stride_y
17 | * @param dst_u
18 | * @param dst_stride_u
19 | * @param dst_v
20 | * @param dst_stride_v
21 | * @param crop_x
22 | * @param crop_y
23 | * @param src_width
24 | * @param src_height
25 | * @param crop_width
26 | * @param crop_height
27 | * @param mode
28 | */
29 | void ConvertNV21(const uint8_t* sample,
30 | uint8_t* dst_y,
31 | int dst_stride_y,
32 | uint8_t* dst_vu,
33 | int dst_stride_vu,
34 | int crop_x,
35 | int crop_y,
36 | int src_width,
37 | int src_height,
38 | int crop_width,
39 | int crop_height,
40 | libyuv::RotationMode mode);
41 |
42 | /**
43 | * 同时NV21转NV12并旋转
44 | * @param src_y
45 | * @param src_stride_y
46 | * @param src_uv
47 | * @param src_stride_uv
48 | * @param dst_y
49 | * @param dst_stride_y
50 | * @param dst_uv
51 | * @param dst_stride_uv
52 | * @param width
53 | * @param height
54 | * @param mode
55 | */
56 | void NV21ToNV12Rotate(const uint8_t* src_y,
57 | int src_stride_y,
58 | const uint8_t* src_uv,
59 | int src_stride_uv,
60 | uint8_t* dst_y,
61 | int dst_stride_y,
62 | uint8_t* dst_uv,
63 | int dst_stride_uv,
64 | int width,
65 | int height,
66 | libyuv::RotationMode mode);
67 |
68 | /**
69 | * 同时裁剪NV21转NV12并旋转
70 | * @param sample
71 | * @param dst_y
72 | * @param dst_stride_y
73 | * @param dst_uv
74 | * @param dst_stride_uv
75 | * @param crop_x
76 | * @param crop_y
77 | * @param src_width
78 | * @param src_height
79 | * @param crop_width
80 | * @param crop_height
81 | * @param mode
82 | */
83 | void ConvertNV21ToNV12(const uint8_t* sample,
84 | uint8_t* dst_y,
85 | int dst_stride_y,
86 | uint8_t* dst_uv,
87 | int dst_stride_uv,
88 | int crop_x,
89 | int crop_y,
90 | int src_width,
91 | int src_height,
92 | int crop_width,
93 | int crop_height,
94 | libyuv::RotationMode mode);
95 |
96 | #endif //CAMERAMEDIACODEC_CONVERTNV_H
97 |
--------------------------------------------------------------------------------
/app/src/main/cpp/Log.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by you on 2018/6/5.
3 | //
4 | #ifndef RSADEMO_LOG_H
5 | #define RSADEMO_LOG_H
6 |
7 | #include
8 | #define TAG "youchenNdk" // 这个是自定义的LOG的标识
9 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
10 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
11 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
12 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
13 | #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
14 |
15 |
16 |
17 | #endif //RSADEMO_LOG_H
18 |
--------------------------------------------------------------------------------
/app/src/main/cpp/RotateNV_UV.h:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Created by you on 2018-03-06. NV21,NV12之间的UV旋转操作
4 | * NV21与NV12区别只是UV与VU交差的区别,因此以下方法NV21,NV12可通用
5 | * 所有_X的交叉即可用作NV12与NV21之间的UV转换
6 | */
7 |
8 | #ifndef CAMERAMEDIACODEC_ROTATENV_UV_H
9 | #define CAMERAMEDIACODEC_ROTATENV_UV_H
10 |
11 | #include
12 |
13 | /**
14 | * 适合NV21或者NV12的UV数据的操作
15 | * @param src
16 | * @param src_stride
17 | * @param dst
18 | * @param dst_stride
19 | * @param width
20 | * @param height
21 | */
22 | void TransposeWxH_C2(const uint16_t* src,
23 | int src_stride,
24 | uint16_t* dst,
25 | int dst_stride,
26 | int width,
27 | int height);
28 |
29 | /**
30 | * 适合NV21或者NV12的UV数据的操作,并转换UV顺序
31 | * @param src
32 | * @param src_stride
33 | * @param dst
34 | * @param dst_stride
35 | * @param width
36 | * @param height
37 | */
38 | void TransposeWxH_C2_X(const uint16_t* src,
39 | int src_stride,
40 | uint16_t* dst,
41 | int dst_stride,
42 | int width,
43 | int height);
44 |
45 | /**
46 | * 镜面翻转uint8_t类型的数据,并交叉变换一个步长的顺序,适合UV与VU之间的镜面转换, 与源码稍作修改
47 | * @param src
48 | * @param dst
49 | * @param width
50 | */
51 | void MirrorRow_C_X(const uint8_t* src,
52 | uint8_t* dst,
53 | int width);
54 |
55 | /**
56 | * 将NV21或者NV12的UV数据旋转90
57 | * @param src
58 | * @param src_stride
59 | * @param dst
60 | * @param dst_stride
61 | * @param width UV数据的宽
62 | * @param height UV数据的高
63 | */
64 | void RotateNV_UV90(const uint16_t* src,
65 | int src_stride,
66 | uint16_t* dst,
67 | int dst_stride,
68 | int width,
69 | int height);
70 |
71 | /**
72 | * 将NV21或者NV12的UV数据旋转270
73 | * @param src
74 | * @param src_stride
75 | * @param dst
76 | * @param dst_stride
77 | * @param width UV数据的宽
78 | * @param height UV数据的高
79 | */
80 | void RotateNV_UV270(const uint16_t* src,
81 | int src_stride,
82 | uint16_t* dst,
83 | int dst_stride,
84 | int width,
85 | int height);
86 |
87 | /**
88 | * 将NV21或者NV12的UV数据旋转180
89 | * @param src
90 | * @param src_stride
91 | * @param dst
92 | * @param dst_stride
93 | * @param width
94 | * @param height
95 | */
96 | void RotateNV_UV180(const uint8_t* src,
97 | int src_stride,
98 | uint8_t* dst,
99 | int dst_stride,
100 | int width,
101 | int height);
102 |
103 | /**
104 | * 同时将NV21或者NV12的UV数据旋转90,并交叉转换UV顺序, 可用作NV12与NV21之间的UV互转
105 | * @param src
106 | * @param src_stride
107 | * @param dst
108 | * @param dst_stride
109 | * @param width UV数据的宽
110 | * @param height UV数据的高
111 | */
112 | void RotateNV_UV90_X(const uint16_t* src,
113 | int src_stride,
114 | uint16_t* dst,
115 | int dst_stride,
116 | int width,
117 | int height);
118 |
119 | /**
120 | * 同时将NV21或者NV12的UV数据旋转270, 并交叉转换UV顺序, 可用作NV12与NV21之间的UV互转
121 | * @param src
122 | * @param src_stride
123 | * @param dst
124 | * @param dst_stride
125 | * @param width UV数据的宽
126 | * @param height UV数据的高
127 | */
128 | void RotateNV_UV270_X(const uint16_t* src,
129 | int src_stride,
130 | uint16_t* dst,
131 | int dst_stride,
132 | int width,
133 | int height);
134 |
135 | /**
136 | * 同时将NV21或者NV12的UV数据旋转180, 并交叉转换UV顺序, 可用作NV12与NV21之间的UV互转
137 | * @param src
138 | * @param src_stride
139 | * @param dst
140 | * @param dst_stride
141 | * @param width
142 | * @param height
143 | */
144 | void RotateNV_UV180_X(const uint8_t* src,
145 | int src_stride,
146 | uint8_t* dst,
147 | int dst_stride,
148 | int width,
149 | int height);
150 |
151 | /**
152 | * NV21的数据拷贝
153 | * @param src_y
154 | * @param src_stride_y
155 | * @param src_vu
156 | * @param src_stride_vu
157 | * @param dst_y
158 | * @param dst_stride_y
159 | * @param dst_vu
160 | * @param dst_stride_vu
161 | * @param width
162 | * @param height
163 | */
164 | void NV21Copy(const uint8_t* src_y,
165 | int src_stride_y,
166 | const uint8_t* src_vu,
167 | int src_stride_vu,
168 | uint8_t* dst_y,
169 | int dst_stride_y,
170 | uint8_t* dst_vu,
171 | int dst_stride_vu,
172 | int width,
173 | int height);
174 |
175 | /**
176 | * NV21旋转
177 | * @param src_y
178 | * @param src_stride_y
179 | * @param src_vu
180 | * @param src_stride_vu
181 | * @param dst_y
182 | * @param dst_stride_y
183 | * @param dst_vu
184 | * @param dst_stride_vu
185 | * @param width
186 | * @param height
187 | * @param mode
188 | */
189 | void NV21Rotate(const uint8_t* src_y,
190 | int src_stride_y,
191 | const uint8_t* src_vu,
192 | int src_stride_vu,
193 | uint8_t* dst_y,
194 | int dst_stride_y,
195 | uint8_t* dst_vu,
196 | int dst_stride_vu,
197 | int width,
198 | int height,
199 | enum libyuv::RotationMode mode);
200 |
201 | #endif //CAMERAMEDIACODEC_ROTATENV_UV_H
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_H_
12 | #define INCLUDE_LIBYUV_H_
13 |
14 | #include "libyuv/basic_types.h"
15 | #include "libyuv/compare.h"
16 | #include "libyuv/convert.h"
17 | #include "libyuv/convert_argb.h"
18 | #include "libyuv/convert_from.h"
19 | #include "libyuv/convert_from_argb.h"
20 | #include "libyuv/cpu_id.h"
21 | #include "libyuv/mjpeg_decoder.h"
22 | #include "libyuv/planar_functions.h"
23 | #include "libyuv/rotate.h"
24 | #include "libyuv/rotate_argb.h"
25 | #include "libyuv/row.h"
26 | #include "libyuv/scale.h"
27 | #include "libyuv/scale_argb.h"
28 | #include "libyuv/scale_row.h"
29 | #include "libyuv/version.h"
30 | #include "libyuv/video_common.h"
31 |
32 | #endif // INCLUDE_LIBYUV_H_
33 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/basic_types.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_BASIC_TYPES_H_
12 | #define INCLUDE_LIBYUV_BASIC_TYPES_H_
13 |
14 | #include // For size_t and NULL
15 |
16 | #if !defined(INT_TYPES_DEFINED) && !defined(GG_LONGLONG)
17 | #define INT_TYPES_DEFINED
18 |
19 | #if defined(_MSC_VER) && (_MSC_VER < 1600)
20 | #include // for uintptr_t on x86
21 | typedef unsigned __int64 uint64_t;
22 | typedef __int64 int64_t;
23 | typedef unsigned int uint32_t;
24 | typedef int int32_t;
25 | typedef unsigned short uint16_t;
26 | typedef short int16_t;
27 | typedef unsigned char uint8_t;
28 | typedef signed char int8_t;
29 | #else
30 | #include // for uintptr_t and C99 types
31 | #endif // defined(_MSC_VER) && (_MSC_VER < 1600)
32 | // Types are deprecated. Enable this macro for legacy types.
33 | #ifdef LIBYUV_LEGACY_TYPES
34 | typedef uint64_t uint64;
35 | typedef int64_t int64;
36 | typedef uint32_t uint32;
37 | typedef int32_t int32;
38 | typedef uint16_t uint16;
39 | typedef int16_t int16;
40 | typedef uint8_t uint8;
41 | typedef int8_t int8;
42 | #endif // LIBYUV_LEGACY_TYPES
43 | #endif // INT_TYPES_DEFINED
44 |
45 | #if !defined(LIBYUV_API)
46 | #if defined(_WIN32) || defined(__CYGWIN__)
47 | #if defined(LIBYUV_BUILDING_SHARED_LIBRARY)
48 | #define LIBYUV_API __declspec(dllexport)
49 | #elif defined(LIBYUV_USING_SHARED_LIBRARY)
50 | #define LIBYUV_API __declspec(dllimport)
51 | #else
52 | #define LIBYUV_API
53 | #endif // LIBYUV_BUILDING_SHARED_LIBRARY
54 | #elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__APPLE__) && \
55 | (defined(LIBYUV_BUILDING_SHARED_LIBRARY) || \
56 | defined(LIBYUV_USING_SHARED_LIBRARY))
57 | #define LIBYUV_API __attribute__((visibility("default")))
58 | #else
59 | #define LIBYUV_API
60 | #endif // __GNUC__
61 | #endif // LIBYUV_API
62 |
63 | // TODO(fbarchard): Remove bool macros.
64 | #define LIBYUV_BOOL int
65 | #define LIBYUV_FALSE 0
66 | #define LIBYUV_TRUE 1
67 |
68 | #endif // INCLUDE_LIBYUV_BASIC_TYPES_H_
69 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/compare.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_COMPARE_H_
12 | #define INCLUDE_LIBYUV_COMPARE_H_
13 |
14 | #include "libyuv/basic_types.h"
15 |
16 | #ifdef __cplusplus
17 | namespace libyuv {
18 | extern "C" {
19 | #endif
20 |
21 | // Compute a hash for specified memory. Seed of 5381 recommended.
22 | LIBYUV_API
23 | uint32_t HashDjb2(const uint8_t* src, uint64_t count, uint32_t seed);
24 |
25 | // Hamming Distance
26 | LIBYUV_API
27 | uint64_t ComputeHammingDistance(const uint8_t* src_a,
28 | const uint8_t* src_b,
29 | int count);
30 |
31 | // Scan an opaque argb image and return fourcc based on alpha offset.
32 | // Returns FOURCC_ARGB, FOURCC_BGRA, or 0 if unknown.
33 | LIBYUV_API
34 | uint32_t ARGBDetect(const uint8_t* argb,
35 | int stride_argb,
36 | int width,
37 | int height);
38 |
39 | // Sum Square Error - used to compute Mean Square Error or PSNR.
40 | LIBYUV_API
41 | uint64_t ComputeSumSquareError(const uint8_t* src_a,
42 | const uint8_t* src_b,
43 | int count);
44 |
45 | LIBYUV_API
46 | uint64_t ComputeSumSquareErrorPlane(const uint8_t* src_a,
47 | int stride_a,
48 | const uint8_t* src_b,
49 | int stride_b,
50 | int width,
51 | int height);
52 |
53 | static const int kMaxPsnr = 128;
54 |
55 | LIBYUV_API
56 | double SumSquareErrorToPsnr(uint64_t sse, uint64_t count);
57 |
58 | LIBYUV_API
59 | double CalcFramePsnr(const uint8_t* src_a,
60 | int stride_a,
61 | const uint8_t* src_b,
62 | int stride_b,
63 | int width,
64 | int height);
65 |
66 | LIBYUV_API
67 | double I420Psnr(const uint8_t* src_y_a,
68 | int stride_y_a,
69 | const uint8_t* src_u_a,
70 | int stride_u_a,
71 | const uint8_t* src_v_a,
72 | int stride_v_a,
73 | const uint8_t* src_y_b,
74 | int stride_y_b,
75 | const uint8_t* src_u_b,
76 | int stride_u_b,
77 | const uint8_t* src_v_b,
78 | int stride_v_b,
79 | int width,
80 | int height);
81 |
82 | LIBYUV_API
83 | double CalcFrameSsim(const uint8_t* src_a,
84 | int stride_a,
85 | const uint8_t* src_b,
86 | int stride_b,
87 | int width,
88 | int height);
89 |
90 | LIBYUV_API
91 | double I420Ssim(const uint8_t* src_y_a,
92 | int stride_y_a,
93 | const uint8_t* src_u_a,
94 | int stride_u_a,
95 | const uint8_t* src_v_a,
96 | int stride_v_a,
97 | const uint8_t* src_y_b,
98 | int stride_y_b,
99 | const uint8_t* src_u_b,
100 | int stride_u_b,
101 | const uint8_t* src_v_b,
102 | int stride_v_b,
103 | int width,
104 | int height);
105 |
106 | #ifdef __cplusplus
107 | } // extern "C"
108 | } // namespace libyuv
109 | #endif
110 |
111 | #endif // INCLUDE_LIBYUV_COMPARE_H_
112 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/compare_row.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_COMPARE_ROW_H_
12 | #define INCLUDE_LIBYUV_COMPARE_ROW_H_
13 |
14 | #include "libyuv/basic_types.h"
15 |
16 | #ifdef __cplusplus
17 | namespace libyuv {
18 | extern "C" {
19 | #endif
20 |
21 | #if defined(__pnacl__) || defined(__CLR_VER) || \
22 | (defined(__native_client__) && defined(__x86_64__)) || \
23 | (defined(__i386__) && !defined(__SSE__) && !defined(__clang__))
24 | #define LIBYUV_DISABLE_X86
25 | #endif
26 | #if defined(__native_client__)
27 | #define LIBYUV_DISABLE_NEON
28 | #endif
29 | // MemorySanitizer does not support assembly code yet. http://crbug.com/344505
30 | #if defined(__has_feature)
31 | #if __has_feature(memory_sanitizer)
32 | #define LIBYUV_DISABLE_X86
33 | #endif
34 | #endif
35 | // Visual C 2012 required for AVX2.
36 | #if defined(_M_IX86) && !defined(__clang__) && defined(_MSC_VER) && \
37 | _MSC_VER >= 1700
38 | #define VISUALC_HAS_AVX2 1
39 | #endif // VisualStudio >= 2012
40 |
41 | // clang >= 3.4.0 required for AVX2.
42 | #if defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
43 | #if (__clang_major__ > 3) || (__clang_major__ == 3 && (__clang_minor__ >= 4))
44 | #define CLANG_HAS_AVX2 1
45 | #endif // clang >= 3.4
46 | #endif // __clang__
47 |
48 | // The following are available for Visual C and GCC:
49 | #if !defined(LIBYUV_DISABLE_X86) && \
50 | (defined(__x86_64__) || defined(__i386__) || defined(_M_IX86))
51 | #define HAS_HASHDJB2_SSE41
52 | #define HAS_SUMSQUAREERROR_SSE2
53 | #define HAS_HAMMINGDISTANCE_SSE42
54 | #endif
55 |
56 | // The following are available for Visual C and clangcl 32 bit:
57 | #if !defined(LIBYUV_DISABLE_X86) && defined(_M_IX86) && defined(_MSC_VER) && \
58 | (defined(VISUALC_HAS_AVX2) || defined(CLANG_HAS_AVX2))
59 | #define HAS_HASHDJB2_AVX2
60 | #define HAS_SUMSQUAREERROR_AVX2
61 | #endif
62 |
63 | // The following are available for GCC and clangcl 64 bit:
64 | #if !defined(LIBYUV_DISABLE_X86) && \
65 | (defined(__x86_64__) || (defined(__i386__) && !defined(_MSC_VER)))
66 | #define HAS_HAMMINGDISTANCE_SSSE3
67 | #endif
68 |
69 | // The following are available for GCC and clangcl 64 bit:
70 | #if !defined(LIBYUV_DISABLE_X86) && defined(CLANG_HAS_AVX2) && \
71 | (defined(__x86_64__) || (defined(__i386__) && !defined(_MSC_VER)))
72 | #define HAS_HAMMINGDISTANCE_AVX2
73 | #endif
74 |
75 | // The following are available for Neon:
76 | #if !defined(LIBYUV_DISABLE_NEON) && \
77 | (defined(__ARM_NEON__) || defined(LIBYUV_NEON) || defined(__aarch64__))
78 | #define HAS_SUMSQUAREERROR_NEON
79 | #define HAS_HAMMINGDISTANCE_NEON
80 | #endif
81 |
82 | #if !defined(LIBYUV_DISABLE_MSA) && defined(__mips_msa)
83 | #define HAS_HAMMINGDISTANCE_MSA
84 | #define HAS_SUMSQUAREERROR_MSA
85 | #endif
86 |
87 | #if !defined(LIBYUV_DISABLE_MMI) && defined(_MIPS_ARCH_LOONGSON3A)
88 | #define HAS_HAMMINGDISTANCE_MMI
89 | #define HAS_SUMSQUAREERROR_MMI
90 | #endif
91 |
92 | uint32_t HammingDistance_C(const uint8_t* src_a,
93 | const uint8_t* src_b,
94 | int count);
95 | uint32_t HammingDistance_SSE42(const uint8_t* src_a,
96 | const uint8_t* src_b,
97 | int count);
98 | uint32_t HammingDistance_SSSE3(const uint8_t* src_a,
99 | const uint8_t* src_b,
100 | int count);
101 | uint32_t HammingDistance_AVX2(const uint8_t* src_a,
102 | const uint8_t* src_b,
103 | int count);
104 | uint32_t HammingDistance_NEON(const uint8_t* src_a,
105 | const uint8_t* src_b,
106 | int count);
107 | uint32_t HammingDistance_MSA(const uint8_t* src_a,
108 | const uint8_t* src_b,
109 | int count);
110 | uint32_t HammingDistance_MMI(const uint8_t* src_a,
111 | const uint8_t* src_b,
112 | int count);
113 | uint32_t SumSquareError_C(const uint8_t* src_a,
114 | const uint8_t* src_b,
115 | int count);
116 | uint32_t SumSquareError_SSE2(const uint8_t* src_a,
117 | const uint8_t* src_b,
118 | int count);
119 | uint32_t SumSquareError_AVX2(const uint8_t* src_a,
120 | const uint8_t* src_b,
121 | int count);
122 | uint32_t SumSquareError_NEON(const uint8_t* src_a,
123 | const uint8_t* src_b,
124 | int count);
125 | uint32_t SumSquareError_MSA(const uint8_t* src_a,
126 | const uint8_t* src_b,
127 | int count);
128 | uint32_t SumSquareError_MMI(const uint8_t* src_a,
129 | const uint8_t* src_b,
130 | int count);
131 |
132 | uint32_t HashDjb2_C(const uint8_t* src, int count, uint32_t seed);
133 | uint32_t HashDjb2_SSE41(const uint8_t* src, int count, uint32_t seed);
134 | uint32_t HashDjb2_AVX2(const uint8_t* src, int count, uint32_t seed);
135 |
136 | #ifdef __cplusplus
137 | } // extern "C"
138 | } // namespace libyuv
139 | #endif
140 |
141 | #endif // INCLUDE_LIBYUV_COMPARE_ROW_H_
142 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/cpu_id.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_CPU_ID_H_
12 | #define INCLUDE_LIBYUV_CPU_ID_H_
13 |
14 | #include "libyuv/basic_types.h"
15 |
16 | #ifdef __cplusplus
17 | namespace libyuv {
18 | extern "C" {
19 | #endif
20 |
21 | // Internal flag to indicate cpuid requires initialization.
22 | static const int kCpuInitialized = 0x1;
23 |
24 | // These flags are only valid on ARM processors.
25 | static const int kCpuHasARM = 0x2;
26 | static const int kCpuHasNEON = 0x4;
27 | // 0x8 reserved for future ARM flag.
28 |
29 | // These flags are only valid on x86 processors.
30 | static const int kCpuHasX86 = 0x10;
31 | static const int kCpuHasSSE2 = 0x20;
32 | static const int kCpuHasSSSE3 = 0x40;
33 | static const int kCpuHasSSE41 = 0x80;
34 | static const int kCpuHasSSE42 = 0x100; // unused at this time.
35 | static const int kCpuHasAVX = 0x200;
36 | static const int kCpuHasAVX2 = 0x400;
37 | static const int kCpuHasERMS = 0x800;
38 | static const int kCpuHasFMA3 = 0x1000;
39 | static const int kCpuHasF16C = 0x2000;
40 | static const int kCpuHasGFNI = 0x4000;
41 | static const int kCpuHasAVX512BW = 0x8000;
42 | static const int kCpuHasAVX512VL = 0x10000;
43 | static const int kCpuHasAVX512VBMI = 0x20000;
44 | static const int kCpuHasAVX512VBMI2 = 0x40000;
45 | static const int kCpuHasAVX512VBITALG = 0x80000;
46 | static const int kCpuHasAVX512VPOPCNTDQ = 0x100000;
47 |
48 | // These flags are only valid on MIPS processors.
49 | static const int kCpuHasMIPS = 0x200000;
50 | static const int kCpuHasMSA = 0x400000;
51 | static const int kCpuHasMMI = 0x800000;
52 |
53 | // Optional init function. TestCpuFlag does an auto-init.
54 | // Returns cpu_info flags.
55 | LIBYUV_API
56 | int InitCpuFlags(void);
57 |
58 | // Detect CPU has SSE2 etc.
59 | // Test_flag parameter should be one of kCpuHas constants above.
60 | // Returns non-zero if instruction set is detected
61 | static __inline int TestCpuFlag(int test_flag) {
62 | LIBYUV_API extern int cpu_info_;
63 | #ifdef __ATOMIC_RELAXED
64 | int cpu_info = __atomic_load_n(&cpu_info_, __ATOMIC_RELAXED);
65 | #else
66 | int cpu_info = cpu_info_;
67 | #endif
68 | return (!cpu_info ? InitCpuFlags() : cpu_info) & test_flag;
69 | }
70 |
71 | // Internal function for parsing /proc/cpuinfo.
72 | LIBYUV_API
73 | int ArmCpuCaps(const char* cpuinfo_name);
74 |
75 | // For testing, allow CPU flags to be disabled.
76 | // ie MaskCpuFlags(~kCpuHasSSSE3) to disable SSSE3.
77 | // MaskCpuFlags(-1) to enable all cpu specific optimizations.
78 | // MaskCpuFlags(1) to disable all cpu specific optimizations.
79 | // MaskCpuFlags(0) to reset state so next call will auto init.
80 | // Returns cpu_info flags.
81 | LIBYUV_API
82 | int MaskCpuFlags(int enable_flags);
83 |
84 | // Sets the CPU flags to |cpu_flags|, bypassing the detection code. |cpu_flags|
85 | // should be a valid combination of the kCpuHas constants above and include
86 | // kCpuInitialized. Use this method when running in a sandboxed process where
87 | // the detection code might fail (as it might access /proc/cpuinfo). In such
88 | // cases the cpu_info can be obtained from a non sandboxed process by calling
89 | // InitCpuFlags() and passed to the sandboxed process (via command line
90 | // parameters, IPC...) which can then call this method to initialize the CPU
91 | // flags.
92 | // Notes:
93 | // - when specifying 0 for |cpu_flags|, the auto initialization is enabled
94 | // again.
95 | // - enabling CPU features that are not supported by the CPU will result in
96 | // undefined behavior.
97 | // TODO(fbarchard): consider writing a helper function that translates from
98 | // other library CPU info to libyuv CPU info and add a .md doc that explains
99 | // CPU detection.
100 | static __inline void SetCpuFlags(int cpu_flags) {
101 | LIBYUV_API extern int cpu_info_;
102 | #ifdef __ATOMIC_RELAXED
103 | __atomic_store_n(&cpu_info_, cpu_flags, __ATOMIC_RELAXED);
104 | #else
105 | cpu_info_ = cpu_flags;
106 | #endif
107 | }
108 |
109 | // Low level cpuid for X86. Returns zeros on other CPUs.
110 | // eax is the info type that you want.
111 | // ecx is typically the cpu number, and should normally be zero.
112 | LIBYUV_API
113 | void CpuId(int info_eax, int info_ecx, int* cpu_info);
114 |
115 | #ifdef __cplusplus
116 | } // extern "C"
117 | } // namespace libyuv
118 | #endif
119 |
120 | #endif // INCLUDE_LIBYUV_CPU_ID_H_
121 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/rotate.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_ROTATE_H_
12 | #define INCLUDE_LIBYUV_ROTATE_H_
13 |
14 | #include "libyuv/basic_types.h"
15 |
16 | #ifdef __cplusplus
17 | namespace libyuv {
18 | extern "C" {
19 | #endif
20 |
21 | // Supported rotation.
22 | typedef enum RotationMode {
23 | kRotate0 = 0, // No rotation.
24 | kRotate90 = 90, // Rotate 90 degrees clockwise.
25 | kRotate180 = 180, // Rotate 180 degrees.
26 | kRotate270 = 270, // Rotate 270 degrees clockwise.
27 |
28 | // Deprecated.
29 | kRotateNone = 0,
30 | kRotateClockwise = 90,
31 | kRotateCounterClockwise = 270,
32 | } RotationModeEnum;
33 |
34 | // Rotate I420 frame.
35 | LIBYUV_API
36 | int I420Rotate(const uint8_t* src_y,
37 | int src_stride_y,
38 | const uint8_t* src_u,
39 | int src_stride_u,
40 | const uint8_t* src_v,
41 | int src_stride_v,
42 | uint8_t* dst_y,
43 | int dst_stride_y,
44 | uint8_t* dst_u,
45 | int dst_stride_u,
46 | uint8_t* dst_v,
47 | int dst_stride_v,
48 | int width,
49 | int height,
50 | enum RotationMode mode);
51 |
52 | // Rotate I444 frame.
53 | LIBYUV_API
54 | int I444Rotate(const uint8_t* src_y,
55 | int src_stride_y,
56 | const uint8_t* src_u,
57 | int src_stride_u,
58 | const uint8_t* src_v,
59 | int src_stride_v,
60 | uint8_t* dst_y,
61 | int dst_stride_y,
62 | uint8_t* dst_u,
63 | int dst_stride_u,
64 | uint8_t* dst_v,
65 | int dst_stride_v,
66 | int width,
67 | int height,
68 | enum RotationMode mode);
69 |
70 | // Rotate NV12 input and store in I420.
71 | LIBYUV_API
72 | int NV12ToI420Rotate(const uint8_t* src_y,
73 | int src_stride_y,
74 | const uint8_t* src_uv,
75 | int src_stride_uv,
76 | uint8_t* dst_y,
77 | int dst_stride_y,
78 | uint8_t* dst_u,
79 | int dst_stride_u,
80 | uint8_t* dst_v,
81 | int dst_stride_v,
82 | int width,
83 | int height,
84 | enum RotationMode mode);
85 |
86 | // Rotate a plane by 0, 90, 180, or 270.
87 | LIBYUV_API
88 | int RotatePlane(const uint8_t* src,
89 | int src_stride,
90 | uint8_t* dst,
91 | int dst_stride,
92 | int width,
93 | int height,
94 | enum RotationMode mode);
95 |
96 | // Rotate planes by 90, 180, 270. Deprecated.
97 | LIBYUV_API
98 | void RotatePlane90(const uint8_t* src,
99 | int src_stride,
100 | uint8_t* dst,
101 | int dst_stride,
102 | int width,
103 | int height);
104 |
105 | LIBYUV_API
106 | void RotatePlane180(const uint8_t* src,
107 | int src_stride,
108 | uint8_t* dst,
109 | int dst_stride,
110 | int width,
111 | int height);
112 |
113 | LIBYUV_API
114 | void RotatePlane270(const uint8_t* src,
115 | int src_stride,
116 | uint8_t* dst,
117 | int dst_stride,
118 | int width,
119 | int height);
120 |
121 | LIBYUV_API
122 | void RotateUV90(const uint8_t* src,
123 | int src_stride,
124 | uint8_t* dst_a,
125 | int dst_stride_a,
126 | uint8_t* dst_b,
127 | int dst_stride_b,
128 | int width,
129 | int height);
130 |
131 | // Rotations for when U and V are interleaved.
132 | // These functions take one input pointer and
133 | // split the data into two buffers while
134 | // rotating them. Deprecated.
135 | LIBYUV_API
136 | void RotateUV180(const uint8_t* src,
137 | int src_stride,
138 | uint8_t* dst_a,
139 | int dst_stride_a,
140 | uint8_t* dst_b,
141 | int dst_stride_b,
142 | int width,
143 | int height);
144 |
145 | LIBYUV_API
146 | void RotateUV270(const uint8_t* src,
147 | int src_stride,
148 | uint8_t* dst_a,
149 | int dst_stride_a,
150 | uint8_t* dst_b,
151 | int dst_stride_b,
152 | int width,
153 | int height);
154 |
155 | // The 90 and 270 functions are based on transposes.
156 | // Doing a transpose with reversing the read/write
157 | // order will result in a rotation by +- 90 degrees.
158 | // Deprecated.
159 | LIBYUV_API
160 | void TransposePlane(const uint8_t* src,
161 | int src_stride,
162 | uint8_t* dst,
163 | int dst_stride,
164 | int width,
165 | int height);
166 |
167 | LIBYUV_API
168 | void TransposeUV(const uint8_t* src,
169 | int src_stride,
170 | uint8_t* dst_a,
171 | int dst_stride_a,
172 | uint8_t* dst_b,
173 | int dst_stride_b,
174 | int width,
175 | int height);
176 |
177 | #ifdef __cplusplus
178 | } // extern "C"
179 | } // namespace libyuv
180 | #endif
181 |
182 | #endif // INCLUDE_LIBYUV_ROTATE_H_
183 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/rotate_argb.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_ROTATE_ARGB_H_
12 | #define INCLUDE_LIBYUV_ROTATE_ARGB_H_
13 |
14 | #include "libyuv/basic_types.h"
15 | #include "libyuv/rotate.h" // For RotationMode.
16 |
17 | #ifdef __cplusplus
18 | namespace libyuv {
19 | extern "C" {
20 | #endif
21 |
22 | // Rotate ARGB frame
23 | LIBYUV_API
24 | int ARGBRotate(const uint8_t* src_argb,
25 | int src_stride_argb,
26 | uint8_t* dst_argb,
27 | int dst_stride_argb,
28 | int src_width,
29 | int src_height,
30 | enum RotationMode mode);
31 |
32 | #ifdef __cplusplus
33 | } // extern "C"
34 | } // namespace libyuv
35 | #endif
36 |
37 | #endif // INCLUDE_LIBYUV_ROTATE_ARGB_H_
38 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/scale_argb.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_SCALE_ARGB_H_
12 | #define INCLUDE_LIBYUV_SCALE_ARGB_H_
13 |
14 | #include "libyuv/basic_types.h"
15 | #include "libyuv/scale.h" // For FilterMode
16 |
17 | #ifdef __cplusplus
18 | namespace libyuv {
19 | extern "C" {
20 | #endif
21 |
22 | LIBYUV_API
23 | int ARGBScale(const uint8_t* src_argb,
24 | int src_stride_argb,
25 | int src_width,
26 | int src_height,
27 | uint8_t* dst_argb,
28 | int dst_stride_argb,
29 | int dst_width,
30 | int dst_height,
31 | enum FilterMode filtering);
32 |
33 | // Clipped scale takes destination rectangle coordinates for clip values.
34 | LIBYUV_API
35 | int ARGBScaleClip(const uint8_t* src_argb,
36 | int src_stride_argb,
37 | int src_width,
38 | int src_height,
39 | uint8_t* dst_argb,
40 | int dst_stride_argb,
41 | int dst_width,
42 | int dst_height,
43 | int clip_x,
44 | int clip_y,
45 | int clip_width,
46 | int clip_height,
47 | enum FilterMode filtering);
48 |
49 | // Scale with YUV conversion to ARGB and clipping.
50 | LIBYUV_API
51 | int YUVToARGBScaleClip(const uint8_t* src_y,
52 | int src_stride_y,
53 | const uint8_t* src_u,
54 | int src_stride_u,
55 | const uint8_t* src_v,
56 | int src_stride_v,
57 | uint32_t src_fourcc,
58 | int src_width,
59 | int src_height,
60 | uint8_t* dst_argb,
61 | int dst_stride_argb,
62 | uint32_t dst_fourcc,
63 | int dst_width,
64 | int dst_height,
65 | int clip_x,
66 | int clip_y,
67 | int clip_width,
68 | int clip_height,
69 | enum FilterMode filtering);
70 |
71 | #ifdef __cplusplus
72 | } // extern "C"
73 | } // namespace libyuv
74 | #endif
75 |
76 | #endif // INCLUDE_LIBYUV_SCALE_ARGB_H_
77 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/libyuv/version.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 The LibYuv Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #ifndef INCLUDE_LIBYUV_VERSION_H_
12 | #define INCLUDE_LIBYUV_VERSION_H_
13 |
14 | #define LIBYUV_VERSION 1741
15 |
16 | #endif // INCLUDE_LIBYUV_VERSION_H_
17 |
--------------------------------------------------------------------------------
/app/src/main/cpp/jniLibs/arm64-v8a/libyuv.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/cpp/jniLibs/arm64-v8a/libyuv.so
--------------------------------------------------------------------------------
/app/src/main/cpp/jniLibs/armeabi-v7a/libyuv.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/cpp/jniLibs/armeabi-v7a/libyuv.so
--------------------------------------------------------------------------------
/app/src/main/cpp/jniLibs/x86/libyuv.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/cpp/jniLibs/x86/libyuv.so
--------------------------------------------------------------------------------
/app/src/main/cpp/jniLibs/x86_64/libyuv.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/cpp/jniLibs/x86_64/libyuv.so
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/App.java:
--------------------------------------------------------------------------------
1 | package you.chen.media;
2 |
3 | import android.app.Application;
4 |
5 | import you.chen.media.utils.Utils;
6 |
7 | /**
8 | * Created by you on 2018-01-18.
9 | */
10 | public class App extends Application {
11 |
12 | @Override
13 | public void onCreate() {
14 | super.onCreate();
15 | Utils.init(this);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/YuvTests.java:
--------------------------------------------------------------------------------
1 | package you.chen.media;
2 |
3 | /**
4 | * Created by you on 2018-07-06.
5 | * 简单的nv21与i420,NV12,YV12转换, 如需要旋转裁剪可以使用YuvUtils高性能
6 | * @deprecated Use {@link you.chen.media.core.YuvUtils}
7 | */
8 |
9 | @Deprecated
10 | public class YuvTests {
11 |
12 | public static void nv21ToI420(byte[] nv21, byte[] i420, int w, int h) {
13 | int frameSize = w * h;
14 | System.arraycopy(nv21, 0, i420, 0, frameSize);
15 |
16 | int start = frameSize + (frameSize >> 2);
17 | int i, half;
18 | for (i = 0; i < frameSize >> 1; i += 2) {
19 | half = i >> 1;
20 | i420[frameSize + half] = nv21[frameSize + i + 1];
21 | i420[start + half] = nv21[frameSize + i];
22 | }
23 | }
24 |
25 | public static void nv21ToNV12(byte[] nv21, byte[] nv12, int w, int h) {
26 | int frameSize = w * h;
27 | System.arraycopy(nv21, 0, nv12, 0, frameSize);
28 |
29 | int i, start;
30 | for (i = 0; i < frameSize >> 2; i++) {
31 | start = frameSize + (i << 1);
32 | nv12[start] = nv21[start + 1];
33 | nv12[start + 1] = nv21[start];
34 | }
35 | }
36 |
37 | public static void nv21ToYV12(byte[] nv21, byte[] yv12, int w, int h) {
38 | int frameSize = w * h;
39 | System.arraycopy(nv21, 0, yv12, 0, frameSize);
40 |
41 | int start = frameSize + (frameSize >> 2);
42 | int i, half;
43 | for (i = 0; i < frameSize >> 1; i += 2) {
44 | half = i >> 1;
45 | yv12[frameSize + half] = nv21[frameSize + i];
46 | yv12[start + half] = nv21[frameSize + i + 1];
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/camera/CameraUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.camera;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Matrix;
6 | import android.graphics.Point;
7 | import android.graphics.PointF;
8 |
9 | import you.chen.media.utils.LogUtils;
10 |
11 | /**
12 | *
13 | * Created by you on 2018/3/24.
14 | */
15 | final public class CameraUtils {
16 |
17 | private CameraUtils() {}
18 |
19 | /**
20 | * 计算出控件中坐标点击效果在裁剪旋转90度之前的实际坐标比例,逆旋转90度时,newX = y, newY = w - x
21 | * @param x
22 | * @param y
23 | * @param w
24 | * @param h
25 | * @param cameraMatrix
26 | * @return
27 | */
28 | public static PointF reverseRotate(float x, float y, int w, int h, Matrix cameraMatrix) {
29 | if (cameraMatrix != null) {
30 | float[] floats = new float[9];//数组0下标为X轴scale, 4为Y轴scale
31 | cameraMatrix.getValues(floats);
32 | if (floats[0] == 1.0f && floats[4] > 1.0f) {//Y轴被裁剪
33 | float preHeight = h * floats[4];//裁剪前的高度
34 | y = (preHeight - h) / 2 + y;//加上被裁剪的长度
35 | LogUtils.i("Y轴被裁剪了, x = %f, y = %f", x, y);
36 | return new PointF(y / preHeight, (w - x) / w);
37 | } else if (floats[0] > 1.0f && floats[4] == 1.0f) {//X轴被裁剪
38 | float preWidth = w * floats[0];//裁剪前的宽度
39 | x = (preWidth - w) / 2 + x;//加上被裁剪的长度
40 | LogUtils.i("X轴被裁剪了, x = %f, y = %f", x, y);
41 | return new PointF(y / h, (preWidth - x) / preWidth);
42 | }
43 | }
44 | return new PointF(y / h, (w - x) / w);
45 | }
46 |
47 | /**
48 | * 矩阵缩放处理后的大小
49 | * @param w 原始预览宽
50 | * @param h 原始预览高
51 | * @param matrix
52 | * @return
53 | */
54 | public static Point matrixSize(int w, int h, Matrix matrix) {
55 | if (matrix != null) {
56 | float[] floats = new float[9];//数组0下标为X轴scale, 4为Y轴scale
57 | matrix.getValues(floats);
58 |
59 | if (floats[0] == 1.0f && floats[4] > 1.0f) { //Y轴需要裁剪,实则裁剪相机的width
60 | int clipWidth = (int) (w / floats[4]);
61 | clipWidth = (clipWidth + 1) & ~1;//裁剪后的宽高都必须为偶数
62 | if (clipWidth < w) {
63 | return new Point(clipWidth, h);
64 | }
65 | } else if (floats[0] > 1.0f && floats[4] == 1.0f) {//X轴需要裁剪,实则裁剪相机的height
66 | int clipHeight = (int) (h / floats[0]);
67 | clipHeight = (clipHeight + 1) & ~1;//裁剪后的宽高都必须为偶数
68 | if (clipHeight < h) {
69 | return new Point(w, clipHeight);
70 | }
71 | }
72 | }
73 | return new Point(w, h);
74 | }
75 |
76 | /**
77 | * 旋转Bitmap
78 | * @param bitmap
79 | * @param degree
80 | * @return
81 | */
82 | public static Bitmap rotateBitmap(Bitmap bitmap, int degree) {
83 | if (degree == 0) return bitmap;
84 | Matrix matrix = new Matrix();
85 | matrix.setRotate(degree);
86 | Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
87 | bitmap.recycle();
88 | return newBitmap;
89 | }
90 |
91 | /**
92 | * 剪切并旋转角度
93 | * @param bitmap
94 | * @param x
95 | * @param y
96 | * @param degree
97 | * @return
98 | */
99 | public static Bitmap rotateClipBitmap(Bitmap bitmap, int x, int y, int width, int height, int degree) {
100 | Matrix matrix = null;
101 | if (degree > 0) {
102 | matrix = new Matrix();
103 | matrix.setRotate(degree);
104 | }
105 | Bitmap clipBitmap = Bitmap.createBitmap(bitmap, x, y, width, height, matrix, true);
106 | bitmap.recycle();
107 | return clipBitmap;
108 | }
109 |
110 | /**
111 | * 考虑到在Camera中直接设置parameters.setRotation(getCameraRotation(orientation));
112 | * 对部分机型无效,因此统一采用预览拍照后再作旋转
113 | * 对原始图片数据裁剪旋转
114 | * @param datas
115 | * @param cameraMatrix
116 | * @param orientation
117 | * @return
118 | */
119 | public static Bitmap bytesToBitmap(byte[] datas, Matrix cameraMatrix, int orientation) {
120 | Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length);
121 | orientation += 90;//需要在原有之上增加旋转的角度
122 | if (orientation >= 360) orientation = 0;
123 | if (cameraMatrix != null) {
124 | float[] floats = new float[9];//数组0下标为X轴scale, 4为Y轴scale
125 | cameraMatrix.getValues(floats);
126 | if (floats[0] == 1.0f && floats[4] > 1.0f) {//Y轴需要裁剪,实际为width需要裁剪
127 | int clipWidth = (int) (bitmap.getWidth() / floats[4]);
128 | int x = (bitmap.getWidth() - clipWidth) >> 1;
129 | return rotateClipBitmap(bitmap, x, 0, clipWidth, bitmap.getHeight(), orientation);
130 | } else if (floats[0] > 1.0f && floats[4] == 1.0f) {//X轴需要裁剪,实际为height需要裁剪
131 | int clipHeight = (int) (bitmap.getHeight() / floats[0]);
132 | int y = (bitmap.getHeight() - clipHeight) >> 1;
133 | return rotateClipBitmap(bitmap, 0, y, bitmap.getWidth(), clipHeight, orientation);
134 | }
135 | }
136 | //不需要裁剪
137 | return rotateBitmap(bitmap, orientation);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/camera/OrientationHelper.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.camera;
2 |
3 | import android.content.Context;
4 | import android.view.OrientationEventListener;
5 |
6 |
7 | /**
8 | * Created by you on 2018-03-21.
9 | * 只监听手机方向感应旋转 0, 90, 180, 270
10 | *
11 | * 可以在后面新的appcompat版本中结合Lifecycle 实现 LifecycleEventObserver, 使用时 getLifecycle().addObserver(OrientationHelper)即可
12 | * override fun onStateChanged(owner: LifecycleOwner, event: Lifecycle.Event) {
13 | * if (event == Lifecycle.Event.ON_RESUME) {
14 | * enable()
15 | * } else if (event == Lifecycle.Event.ON_PAUSE) {
16 | * disable()
17 | * } else if (event == Lifecycle.Event.ON_DESTROY) {
18 | * owner.lifecycle.removeObserver(this)
19 | * }
20 | * }
21 | */
22 | public class OrientationHelper { //上面注释
23 |
24 | private int orientation = 0;
25 |
26 | private final OrientationListener listener;
27 |
28 | public OrientationHelper(Context context) {
29 | listener = new OrientationListener(context.getApplicationContext(), this);
30 | }
31 |
32 | /**
33 | * 开启方向感应
34 | */
35 | public final void enable() {
36 | if (listener.canDetectOrientation()) {
37 | listener.enable();
38 | } else {
39 | listener.disable();
40 | }
41 | }
42 |
43 | /**
44 | * 取消方向感应
45 | */
46 | public final void disable() {
47 | listener.disable();
48 | }
49 |
50 | /**
51 | * 获取当前方向感应
52 | * @return
53 | */
54 | public final int getOrientation() {
55 | return orientation;
56 | }
57 |
58 | /**
59 | * 旋转角度改变
60 | * @param orientation
61 | */
62 | public void onOrientationChanged(int orientation) {
63 | //nothing to override
64 | }
65 |
66 | static class OrientationListener extends OrientationEventListener {
67 |
68 | OrientationHelper helper;
69 |
70 | public OrientationListener(Context context, OrientationHelper helper) {
71 | super(context);
72 | this.helper = helper;
73 | }
74 |
75 | /**
76 | * 转换当前角度为 0, 90, 180, 270, 360即为0
77 | * @param orientation
78 | * @return
79 | */
80 | private int transformOrientation(int orientation) {
81 | int rotation = (orientation + 45) / 90 * 90;
82 | if (rotation >= 360) rotation = 0;
83 | return rotation;
84 | }
85 |
86 | @Override
87 | public void onOrientationChanged(int orientation) {
88 | if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
89 | orientation = transformOrientation(orientation);
90 | if (helper.orientation != orientation) {
91 | helper.orientation = orientation;
92 | helper.onOrientationChanged(orientation);
93 | }
94 | }
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/camera/SizeFilter.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.camera;
2 |
3 | import android.hardware.Camera;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * pic与preview 尺寸筛选器
9 | * Created by you on 2018/3/24.
10 | */
11 | public interface SizeFilter {
12 |
13 | /**
14 | * 查找出最合适的图片尺寸, 视频可以忽略
15 | * @param outs
16 | * @param w
17 | * @param h
18 | * @return
19 | */
20 | Camera.Size findOptimalPicSize(List outs, int w, int h);
21 |
22 | /**
23 | * 查找出最合适的预览尺寸, 一般先确认拍摄Pic的尺寸
24 | * @param outs
25 | * @param picSize
26 | * @param w
27 | * @param h
28 | * @return
29 | */
30 | Camera.Size findOptimalPreSize(List outs, Camera.Size picSize, int w, int h);
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/camera/impl/HightSizeFilter.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.camera.impl;
2 |
3 | import android.hardware.Camera;
4 |
5 | import java.util.Collections;
6 | import java.util.Comparator;
7 | import java.util.List;
8 |
9 | import you.chen.media.camera.SizeFilter;
10 |
11 | /**
12 | * 高清拍照, 只需要找出最大的尺寸即可,不推荐使用
13 | * Created by you on 2018/3/24.
14 | */
15 | public class HightSizeFilter implements SizeFilter {
16 |
17 | private CameraCompare compare;
18 |
19 | public HightSizeFilter() {
20 | compare = new CameraCompare();
21 | }
22 |
23 | @Override
24 | public Camera.Size findOptimalPicSize(List outs, int w, int h) {
25 | if (outs != null && !outs.isEmpty()) {
26 | Collections.sort(outs, compare);
27 | return outs.get(0);
28 | }
29 | return null;
30 | }
31 |
32 | /**
33 | * 优先找出宽高比例最按近pic的, 再找出尺寸最接近surface的
34 | * @param outs
35 | * @param picSize
36 | * @param w
37 | * @param h
38 | * @return
39 | */
40 | @Override
41 | public Camera.Size findOptimalPreSize(List outs, Camera.Size picSize, int w, int h) {
42 | if (outs == null || outs.isEmpty()) return null;
43 |
44 | float targetScale = picSize.width / (float) picSize.height;
45 | long targetSize = w * (long) h;
46 |
47 | Camera.Size optimalSize = null;
48 | float optimalDiff = Float.MAX_VALUE; //宽高比例与pic的差
49 | long optimalMaxDif = Long.MAX_VALUE; //最优的最大值差距
50 |
51 | for (Camera.Size size : outs) {
52 | float newDiff = Math.abs(size.width / (float) size.height - targetScale);
53 | if (newDiff < optimalDiff) {//更好的比例
54 | optimalDiff = newDiff;
55 | optimalSize = size;
56 | optimalMaxDif = Math.abs(targetSize - size.width * (long) size.height);
57 | } else if (newDiff == optimalDiff) {
58 | long newOptimalMaxDif = Math.abs(targetSize - size.width * (long) size.height);
59 | if (newOptimalMaxDif < optimalMaxDif) {
60 | optimalSize = size;
61 | optimalMaxDif = newOptimalMaxDif;
62 | }
63 | }
64 | }
65 | return optimalSize;
66 | }
67 |
68 | private static class CameraCompare implements Comparator {
69 | @Override
70 | public int compare(Camera.Size o1, Camera.Size o2) {
71 | return Long.signum((long) o2.width * o2.height - (long) o1.width * o1.height);
72 | }
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/camera/impl/VideoSizeFilter.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.camera.impl;
2 |
3 | import android.hardware.Camera;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import you.chen.media.camera.SizeFilter;
9 |
10 |
11 | /**
12 | * 常用的尺寸筛选, 优先采用尺寸限制(适用于尺寸比较固定的需求)
13 | * 优先筛选尺寸最接近, 再筛选宽高比例最接近的
14 | * Created by you on 2018/3/24.
15 | */
16 | public class VideoSizeFilter implements SizeFilter {
17 |
18 | //默认固定最大尺寸,此参数已属于高清,若节省流量适配早期低配手机可以设置 540 * 960
19 | public static final int DEF_MAX_WIDTH = 720;
20 | public static final int DEF_MAX_HEIGHT = 1280;
21 |
22 | private final int maxWidth, maxHeight;
23 |
24 | public VideoSizeFilter() {
25 | this(DEF_MAX_WIDTH, DEF_MAX_HEIGHT);
26 | }
27 |
28 | public VideoSizeFilter(int maxWidth, int maxHeight) {
29 | this.maxWidth = maxWidth;
30 | this.maxHeight = maxHeight;
31 | }
32 |
33 | /**
34 | * 拍摄视频可忽略Picture尺寸
35 | * @param outs
36 | * @param w
37 | * @param h
38 | * @return
39 | */
40 | @Override
41 | public Camera.Size findOptimalPicSize(List outs, int w, int h) {
42 | return null;
43 | }
44 |
45 | /**
46 | * 查找出最佳预览尺寸,优先找出尺寸小于并最接近最大限制的尺寸
47 | * @param outs
48 | * @param picSize
49 | * @param w
50 | * @param h
51 | * @return
52 | */
53 | @Override
54 | public Camera.Size findOptimalPreSize(List outs, Camera.Size picSize, int w, int h) {
55 | List filterSize = filterSize(outs);
56 | if (filterSize == null || filterSize.isEmpty()) return null;
57 | Camera.Size optimalSize = null;
58 |
59 | float targetScale = h / (float) w;
60 | int minDiffSize = Integer.MAX_VALUE;
61 | int maxDiffSize = Integer.MAX_VALUE;
62 | float optimalScale = Float.MAX_VALUE;
63 |
64 | for (Camera.Size size : filterSize) {
65 | float newOptimalScale = Math.abs(size.width / (float) size.height - targetScale);
66 | int diffWidth = Math.abs(maxWidth - size.height);
67 | int diffHeight = Math.abs(maxHeight - size.width);
68 | //先找出宽高与最大宽高需求的最小与最大差值
69 | int newMinDiffSize, newMaxDiffSize;
70 | if (diffWidth > diffHeight) {
71 | newMinDiffSize = diffHeight;
72 | newMaxDiffSize = diffWidth;
73 | } else {
74 | newMinDiffSize = diffWidth;
75 | newMaxDiffSize = diffHeight;
76 | }
77 |
78 | if (newMinDiffSize < minDiffSize) {
79 | optimalSize = size;
80 | minDiffSize = newMinDiffSize;
81 | maxDiffSize = newMaxDiffSize;
82 | optimalScale = newOptimalScale;
83 | } else if (newMinDiffSize == minDiffSize) {
84 | if (newMaxDiffSize < maxDiffSize) {
85 | optimalSize = size;
86 | maxDiffSize = newMaxDiffSize;
87 | optimalScale = newOptimalScale;
88 | } else if (newMaxDiffSize == maxDiffSize) {
89 | if (newOptimalScale < optimalScale) {
90 | optimalSize = size;
91 | optimalScale = newOptimalScale;
92 | }
93 | }
94 | }
95 | }
96 | return optimalSize;
97 | }
98 |
99 | /**
100 | * 优先找出尺寸小于最大限制的尺寸
101 | * @param outs
102 | * @return
103 | */
104 | private List filterSize(List outs) {
105 | if (outs == null || outs.isEmpty()) return null;
106 | List filterSizes = new ArrayList<>();//最佳的筛选
107 | List secondarySizes = new ArrayList<>();//次要的筛选
108 | for (Camera.Size size : outs) {
109 | if (size.width <= maxHeight && size.height <= maxWidth) {
110 | filterSizes.add(size);
111 | } else if (size.width <= maxHeight || size.height <= maxWidth) {
112 | secondarySizes.add(size);
113 | }
114 | }
115 | if (!filterSizes.isEmpty()) {
116 | return filterSizes;
117 | }
118 | if (!secondarySizes.isEmpty()) {
119 | return secondarySizes;
120 | }
121 | return outs;
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/BytePool.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core;
2 |
3 | import java.util.LinkedList;
4 |
5 | /**
6 | * 字节池, 在裁剪与编解码时会大量的使用大的byte[],防止内存抖动
7 | * Created by you on 2018/3/24.
8 | */
9 | public final class BytePool {
10 |
11 | private static final int DEF_MAX = 10;
12 |
13 | //缓存的字节数组大小, 只缓存一种大小的字节池
14 | private final int length;
15 | //最大缓存数量
16 | private final int maxSize;
17 |
18 | private final LinkedList bytepools = new LinkedList<>();
19 |
20 | public BytePool(int length) {
21 | this(DEF_MAX, length);
22 | }
23 | public BytePool(int maxSize, int length) {
24 | this.maxSize = maxSize;
25 | this.length = length;
26 | }
27 |
28 | public int getLength() {
29 | return length;
30 | }
31 |
32 | public synchronized final boolean put(byte[] bytes) {
33 | if (bytes.length == length && bytepools.size() < maxSize) {
34 | return bytepools.add(bytes);
35 | }
36 | return false;
37 | }
38 |
39 | public synchronized final byte[] get() {
40 | if (bytepools.isEmpty()) return new byte[length];
41 | return bytepools.remove();
42 | }
43 |
44 | public void clear() {
45 | bytepools.clear();
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/Constant.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core;
2 |
3 | import android.media.AudioFormat;
4 | import android.media.MediaCodecInfo;
5 |
6 | /**
7 | * Created by you on 2018-05-19.
8 | * 多媒体编码的常用参数
9 | */
10 | public interface Constant {
11 |
12 | // ----------------- H264 -----------------
13 | int FRAME_RATE = 20; //Camera中一般支持7~30
14 | int IFRAME_INTERVAL = 10; //关键帧间隔
15 |
16 | //相机的默认最小与最大帧率参数, 包含 FRAME_RATE 范围
17 | int DEF_MIN_FPS = 15000;
18 | int DEF_MAX_FPS = 25000;
19 |
20 | //扫描类的可以适当帧率高一些
21 | int SCAN_MIN_FPS = 2500;
22 | int SCAN_MAX_FPS = 3000;
23 |
24 | /**
25 | * 码率系数, w * h * 3, 此参数越大拍出的视频质量越大,最好不超过FRAME_RATE
26 | */
27 | int VIDEO_BITRATE_COEFFICIENT = 3;
28 |
29 |
30 | //----------------- Audio -----------------
31 |
32 | //双声道
33 | int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;
34 | int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
35 |
36 | int SAMPLE_RATE = 44100;
37 | //两声道对应的channelConfig为AudioFormat.CHANNEL_OUT_STEREO
38 | int CHANNEL_COUNT = 2;
39 | int AUDIO_RATE = 1000 << 6;
40 | int AAC_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectLC;
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/MediaEncoder.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core;
2 |
3 | import android.media.MediaCodec;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.util.concurrent.BlockingQueue;
7 | import java.util.concurrent.ExecutorService;
8 | import java.util.concurrent.LinkedBlockingDeque;
9 | import java.util.concurrent.atomic.AtomicBoolean;
10 |
11 | /**
12 | * Created by you on 2018-05-10.
13 | * MediaCodec核心编码器
14 | */
15 | public final class MediaEncoder implements Runnable {
16 |
17 | private static final String TAG = MediaEncoder.class.getSimpleName();
18 |
19 | public static final int DEF_TIMEOUT_USEC = 10_000;
20 | //编码器
21 | protected final MediaCodec mediaCodec;
22 | //待编码的数据 Camera.byte[], pcm
23 | private final BlockingQueue bufferQueue = new LinkedBlockingDeque<>();
24 | //字节池
25 | private final BytePool bytePool;
26 | //数据转换
27 | private final Transform transform;
28 | //编码的buffer信息
29 | private MediaCodec.BufferInfo bufferInfo;
30 | //是否正在录制待编码
31 | private final AtomicBoolean isCoding = new AtomicBoolean(false);
32 | //callback
33 | private final Callback callback;
34 | //等待取出编码后的数据时长
35 | private final int timeoutUs;
36 |
37 | public MediaEncoder(MediaCodec mediaCodec, BytePool bytePool, Transform transform, Callback callback) {
38 | this(mediaCodec, bytePool, transform, callback, DEF_TIMEOUT_USEC);
39 | }
40 |
41 | public MediaEncoder(MediaCodec mediaCodec, BytePool bytePool, Transform transform, Callback callback, int timeoutUs) {
42 | this.mediaCodec = mediaCodec;
43 | this.bytePool = bytePool;
44 | this.transform = transform;
45 | this.callback = callback;
46 | this.timeoutUs = timeoutUs;
47 | }
48 |
49 | /**
50 | * start
51 | * @param executorService
52 | */
53 | public void start(ExecutorService executorService) {
54 | if (mediaCodec != null) {
55 | mediaCodec.start();
56 | isCoding.set(true);
57 | }
58 | executorService.execute(this);
59 | }
60 |
61 | //stop
62 | public void stop() {
63 | isCoding.set(false);
64 | //如果队列已经处于阻塞时,用此唤醒, notify?
65 | bufferQueue.add(new byte[0]);
66 | }
67 |
68 | //往里添加数据
69 | public void push(byte[] data, int len) {
70 | if (isCoding.get()) {
71 | byte[] buffer = bytePool.get();
72 | //LogUtils.i(TAG, "buffQueue un codec %s - %d", callback.getClass().getSimpleName(), bufferQueue.size());
73 | transform.transform(data, buffer, len);
74 | //实际项目开发中要对Queue限制最大等待数量
75 | bufferQueue.add(buffer);
76 | }
77 | }
78 |
79 | public void push(byte[] data) {
80 | push(data, data.length);
81 | }
82 |
83 | //释放资源
84 | private void release() {
85 | bufferQueue.clear();
86 | bytePool.clear();
87 | if (mediaCodec != null) {
88 | mediaCodec.stop();
89 | mediaCodec.release();
90 | }
91 | callback.onRelease();
92 | }
93 |
94 | @Override
95 | public void run() {
96 | bufferInfo = new MediaCodec.BufferInfo();
97 | callback.onInitStart();
98 | while (isCoding.get()) {
99 | try {
100 | byte[] buffer = bufferQueue.take();
101 | if (buffer == null || buffer.length == 0) {
102 | break;//用空byte[]来终止循环与阻塞
103 | }
104 | codecDatas(buffer);
105 | //缓存
106 | bytePool.put(buffer);
107 | } catch (InterruptedException e) {
108 | if (!isCoding.get()) {
109 | break;
110 | }
111 | }
112 | }
113 | release();
114 | }
115 |
116 | /**
117 | * 编码datas数据
118 | * @param buffer
119 | */
120 | private void codecDatas(byte[] buffer) {
121 | //加入缓冲区, -1如果当前没有可用的缓冲时会进入阻塞状态, 0时会立刻返回
122 | int index = mediaCodec.dequeueInputBuffer(-1);
123 | if (index >= 0) {
124 | //填充数据
125 | ByteBuffer inputBuffer = mediaCodec.getInputBuffer(index);
126 | inputBuffer.clear();
127 | inputBuffer.put(buffer, 0, buffer.length);
128 |
129 | callback.onEncodeInputBuffer(mediaCodec, buffer, index);
130 | }
131 | int encodeStatus;
132 | while (true) {
133 | //返回的三种状态 INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED, INFO_OUTPUT_BUFFERS_CHANGED,
134 | encodeStatus = mediaCodec.dequeueOutputBuffer(bufferInfo, timeoutUs);
135 | if (encodeStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
136 | break;//稍后重试
137 | } else if (encodeStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
138 | //这里只会回调一次用于初始化
139 | callback.onFormatChanged(mediaCodec);
140 | } else if (encodeStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
141 | //忽略
142 | } else {
143 | //正常编码获得缓冲下标
144 | ByteBuffer encodeData = mediaCodec.getOutputBuffer(encodeStatus);
145 | //写入编码后的数据
146 | callback.onWriteData(bufferInfo, encodeData);
147 | //释放缓存冲,后续可以存放新的编码后的数据
148 | mediaCodec.releaseOutputBuffer(encodeStatus, false);
149 | }
150 | }
151 | }
152 |
153 | /**
154 | * 编码回调类
155 | */
156 | public interface Callback {
157 | /**
158 | * 初始化开始
159 | */
160 | void onInitStart();
161 |
162 | /**
163 | * 编码编入数据
164 | * @param mediaCodec
165 | * @param buffer
166 | * @param inputBufferIndex
167 | */
168 | void onEncodeInputBuffer(MediaCodec mediaCodec, byte[] buffer, int inputBufferIndex);
169 |
170 | /**
171 | * MediaCodec初始化
172 | * @param mediaCodec
173 | */
174 | void onFormatChanged(MediaCodec mediaCodec);
175 |
176 | /**
177 | * 写入数据
178 | * @param bufferInfo
179 | * @param encodeData
180 | */
181 | void onWriteData(MediaCodec.BufferInfo bufferInfo, ByteBuffer encodeData);
182 |
183 | /**
184 | * release
185 | */
186 | void onRelease();
187 | }
188 |
189 | }
190 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/MediaUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core;
2 |
3 | import android.media.MediaCodec;
4 | import android.media.MediaCodecInfo;
5 | import android.media.MediaCodecList;
6 | import android.media.MediaFormat;
7 |
8 | import java.io.IOException;
9 |
10 | import you.chen.media.utils.LogUtils;
11 |
12 | /**
13 | * Created by you on 2018-05-10.
14 | * MediaCodec Utils
15 | */
16 | final public class MediaUtils {
17 |
18 | private MediaUtils() {}
19 |
20 | //创建AVC MediaCodec
21 | public static MediaCodec createAvcMediaCodec(int w, int h, int colorFormat,
22 | @Orientation.OrientationMode int orientation,
23 | int bitRate, int frameRate, int frameInterval) {
24 | MediaFormat format;
25 | if (orientation == Orientation.ROTATE90 || orientation == Orientation.ROTATE270) {
26 | format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, h, w);
27 | } else {
28 | format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, w, h);
29 | }
30 | //色彩空间
31 | format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
32 | format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
33 | format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
34 | format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, frameInterval);
35 |
36 | try {
37 | MediaCodec mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
38 | mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
39 | return mediaCodec;
40 | } catch (IOException e) {
41 | LogUtils.e(e);
42 | }
43 | return null;
44 | }
45 |
46 | //创建AAC MediaCodec
47 | public static MediaCodec createAacMediaCodec(int bufferSize, int sampleRate,
48 | int channelCount, int bitRate, int aacProfile) {
49 | MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);
50 | format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize);
51 | format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
52 | format.setInteger(MediaFormat.KEY_AAC_PROFILE, aacProfile);
53 | try {
54 | MediaCodec mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
55 | mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
56 | return mediaCodec;
57 | } catch (IOException e) {
58 | LogUtils.e(e);
59 | }
60 | return null;
61 | }
62 |
63 | public static MediaCodecInfo selectCodecInfo(String mime) {
64 | int numCodecs = MediaCodecList.getCodecCount();
65 | for (int i = 0; i < numCodecs; i++) {
66 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
67 | if (!codecInfo.isEncoder()) {
68 | continue;
69 | }
70 | String[] types = codecInfo.getSupportedTypes();
71 | for (int j = 0; j < types.length; j++) {
72 | if (types[j].equalsIgnoreCase(mime)) {
73 | return codecInfo;
74 | }
75 | }
76 | }
77 | return null;
78 | }
79 |
80 | //查询支持的输入格式
81 | public static int selectColorFormat() {
82 | MediaCodecInfo codecInfo = selectCodecInfo(MediaFormat.MIMETYPE_VIDEO_AVC);
83 | if (codecInfo == null) {
84 | return -1;
85 | }
86 | MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AVC);
87 | int[] colorFormats = capabilities.colorFormats;
88 | for (int i = 0; i < colorFormats.length; i++) {
89 | if (colorFormats[i] == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar
90 | || colorFormats[i] == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar
91 | || colorFormats[i] == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar
92 | || colorFormats[i] == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar) {
93 | return colorFormats[i];
94 | }
95 | }
96 | return -1;
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/Orientation.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import androidx.annotation.IntDef;
7 |
8 | /**
9 | * Created by you on 2018-03-10.
10 | * 图像数据旋转角度
11 | */
12 | public interface Orientation {
13 |
14 | @IntDef({ROTATE0, ROTATE90, ROTATE180, ROTATE270})
15 | @Retention(RetentionPolicy.SOURCE)
16 | @interface OrientationMode {}
17 |
18 | /**
19 | * 此四种旋转的值与JNI中的RotationMode值对应
20 | */
21 | int ROTATE0 = 0;//不旋转
22 | int ROTATE90 = 90;
23 | int ROTATE180 = 180;
24 | int ROTATE270 = 270;
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/Transform.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core;
2 |
3 | /**
4 | * 编码数据的转换器, 图像NV21相关的一些转换或者音频数据的处理
5 | * Created by you on 2018-03-20.
6 | */
7 | public interface Transform {
8 |
9 | /**
10 | * 将原有数据进行转换
11 | * @param src
12 | * @param outs
13 | */
14 | void transform(byte[] src, byte[] outs, int len);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/YuvUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core;
2 |
3 | /**
4 | * Created by you on 2018-03-12.
5 | * 图像数据nv21,i420, nv12, yv12的一些转换裁剪旋转的相关操作
6 | */
7 | public final class YuvUtils {
8 |
9 | private YuvUtils() {}
10 |
11 | static {
12 | System.loadLibrary("yuv-utils");
13 | }
14 |
15 | /**
16 | * 同时将NV21数据转换成I420并旋转, 不剪切
17 | * @param nv21 camera datas
18 | * @param i420 out datas
19 | * @param w
20 | * @param h
21 | * @param orientation
22 | */
23 | public static native void nv21ToI420Rotate(byte[] nv21,
24 | byte[] i420,
25 | int w, int h,
26 | @Orientation.OrientationMode int orientation);
27 |
28 | /**
29 | * 同时剪切NV21数据转换成I420并旋转
30 | * @param nv21
31 | * @param i420
32 | * @param w 相机原尺寸
33 | * @param h
34 | * @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w
35 | * @param ch 同上 <= h
36 | * @param left
37 | * @param top
38 | * @param orientation
39 | */
40 | public static native void clipNv21ToI420Rotate(byte[] nv21,
41 | byte[] i420,
42 | int w, int h,
43 | int cw, int ch,
44 | int left, int top,
45 | @Orientation.OrientationMode int orientation);
46 |
47 | /**
48 | * 同时将NV21数据转换成NV12并旋转, 不剪切
49 | * @param nv21 camera datas
50 | * @param nv12 out datas
51 | * @param w
52 | * @param h
53 | * @param orientation
54 | */
55 | public static native void nv21ToNV12Rotate(byte[] nv21,
56 | byte[] nv12,
57 | int w, int h,
58 | @Orientation.OrientationMode int orientation);
59 |
60 | /**
61 | * 同时剪切NV21数据转换成NV12并旋转
62 | * @param nv21
63 | * @param nv12
64 | * @param w 相机原尺寸
65 | * @param h
66 | * @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w, h
67 | * @param ch 同上
68 | * @param left
69 | * @param top
70 | * @param orientation
71 | */
72 | public static native void clipNv21ToNV12Rotate(byte[] nv21,
73 | byte[] nv12,
74 | int w, int h,
75 | int cw, int ch,
76 | int left, int top,
77 | @Orientation.OrientationMode int orientation);
78 |
79 | /**
80 | * 同时将NV21数据转换成YV12并旋转, 不剪切
81 | * @param nv21 camera datas
82 | * @param yv12 out datas
83 | * @param w
84 | * @param h
85 | * @param orientation
86 | */
87 | public static native void nv21ToYV12Rotate(byte[] nv21,
88 | byte[] yv12,
89 | int w, int h,
90 | @Orientation.OrientationMode int orientation);
91 |
92 | /**
93 | * 同时剪切NV21数据转换成YV12并旋转
94 | * @param nv21
95 | * @param yv12
96 | * @param w 相机原尺寸
97 | * @param h
98 | * @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w
99 | * @param ch 同上 <= h
100 | * @param left
101 | * @param top
102 | * @param orientation
103 | */
104 | public static native void clipNv21ToYV12Rotate(byte[] nv21,
105 | byte[] yv12,
106 | int w, int h,
107 | int cw, int ch,
108 | int left, int top,
109 | @Orientation.OrientationMode int orientation);
110 |
111 | /**
112 | * 将NV21数据旋转, 不剪切
113 | * @param nv21 camera datas
114 | * @param outs out datas
115 | * @param w
116 | * @param h
117 | * @param orientation
118 | */
119 | public static native void nv21Rotate(byte[] nv21,
120 | byte[] outs,
121 | int w, int h,
122 | @Orientation.OrientationMode int orientation);
123 |
124 | /**
125 | * 同时剪切NV21数据并旋转
126 | * @param nv21
127 | * @param outs
128 | * @param w 相机原尺寸
129 | * @param h
130 | * @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w
131 | * @param ch 同上 <= h
132 | * @param left
133 | * @param top
134 | * @param orientation
135 | */
136 | public static native void clipNv21Rotate(byte[] nv21,
137 | byte[] outs,
138 | int w, int h,
139 | int cw, int ch,
140 | int left, int top,
141 | @Orientation.OrientationMode int orientation);
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/audio/AudioCallback.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.audio;
2 |
3 | import android.media.MediaCodec;
4 |
5 | import java.io.BufferedOutputStream;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.nio.ByteBuffer;
9 |
10 | import you.chen.media.core.MediaEncoder;
11 | import you.chen.media.utils.FileUtils;
12 | import you.chen.media.utils.LogUtils;
13 |
14 | /**
15 | * Created by you on 2018-05-19.
16 | * @deprecated use {@link AudioMuxerCallback}
17 | */
18 | @Deprecated
19 | public class AudioCallback implements MediaEncoder.Callback {
20 |
21 | //BufferInfo中的大小不固定,可以用大小固定的缓冲数组写出
22 | public static final int WRITE_BUFFER_SIZE = 1024;
23 |
24 | private final String path;
25 | //音频时间计算器
26 | private final AudioPresentationTime presentationTime;
27 |
28 | //储存数据
29 | private FileOutputStream fos;
30 | private BufferedOutputStream bos;
31 | //写入数据缓冲
32 | private byte[] writeBuffer;
33 | //aac header
34 | private byte[] adtsHeader = new byte[7];
35 |
36 | //aac header
37 | private static final int profile = 2;
38 | private static final int freqIdx = 4;//对应的44100 H
39 | private static final int chanCfg = 2;
40 |
41 | public AudioCallback(String path, AudioPresentationTime presentationTime) {
42 | this.path = path;
43 | this.presentationTime = presentationTime;
44 | }
45 |
46 | @Override
47 | public void onInitStart() {
48 | writeBuffer = new byte[WRITE_BUFFER_SIZE];
49 | //header前三位是固定
50 | adtsHeader[0] = (byte) 0xFF;
51 | adtsHeader[1] = (byte) 0xF9;
52 | adtsHeader[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
53 |
54 | presentationTime.start();
55 | }
56 |
57 | @Override
58 | public void onFormatChanged(MediaCodec mediaCodec) {
59 | try {
60 | fos = new FileOutputStream(path);
61 | bos = new BufferedOutputStream(fos);
62 | } catch (IOException e) {
63 | e.printStackTrace();
64 | }
65 | }
66 |
67 | @Override
68 | public void onEncodeInputBuffer(MediaCodec mediaCodec, byte[] buffer, int inputBufferIndex) {
69 | mediaCodec.queueInputBuffer(inputBufferIndex, 0, buffer.length, presentationTime.getPresentationTimeUs(), 0);
70 | }
71 |
72 | @Override
73 | public void onWriteData(MediaCodec.BufferInfo bufferInfo, ByteBuffer encodeData) {
74 | if (bufferInfo.size != 0) {
75 | encodeData.position(bufferInfo.offset);
76 | encodeData.limit(bufferInfo.offset + bufferInfo.size);
77 |
78 | addADTStoPacket(bufferInfo.size + 7);
79 | try {
80 | bos.write(adtsHeader, 0, 7);
81 | } catch (IOException e) {
82 | e.printStackTrace();
83 | }
84 |
85 | //将ByteBuffer中的数据写到文件中
86 | LogUtils.i("write buffinfosize %d", bufferInfo.size);
87 | int offset = bufferInfo.offset;
88 | int bufferSize = bufferInfo.size;
89 | while (bufferSize > writeBuffer.length) {
90 | writeByteBuffer(encodeData, offset, writeBuffer.length);
91 | bufferSize -= writeBuffer.length;
92 | offset += writeBuffer.length;
93 | }
94 | if (bufferSize > 0) {
95 | writeByteBuffer(encodeData, offset, bufferSize);
96 | }
97 | //byte[] buf = new byte[bufferInfo.size];
98 | //encodeData.get(buf); 不能用此种方式写入,内存抖动极大
99 | }
100 | }
101 |
102 | @Override
103 | public void onRelease() {
104 | FileUtils.closeCloseable(bos, fos);
105 | }
106 |
107 | private void addADTStoPacket(int packetLen) {
108 | adtsHeader[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
109 | adtsHeader[4] = (byte) ((packetLen & 0x7FF) >> 3);
110 | adtsHeader[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
111 | adtsHeader[6] = (byte) 0xFC;
112 | }
113 |
114 | /**
115 | * 将ByteBuffer通过byte[]写入到文件
116 | * @param encodeData
117 | * @param offset
118 | * @param length
119 | */
120 | private void writeByteBuffer(ByteBuffer encodeData, int offset, int length) {
121 | encodeData.position(offset);
122 | encodeData.limit(offset + length);
123 |
124 | encodeData.get(writeBuffer, 0, length);
125 | try {
126 | bos.write(writeBuffer, 0, length);
127 | } catch (IOException e) {
128 | e.printStackTrace();
129 | }
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/audio/AudioMuxerCallback.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.audio;
2 |
3 | import android.media.MediaCodec;
4 | import android.media.MediaMuxer;
5 |
6 | import java.io.IOException;
7 | import java.nio.ByteBuffer;
8 |
9 | import you.chen.media.core.MediaEncoder;
10 |
11 | /**
12 | * Created by you on 2018-05-19.
13 | *
14 | */
15 | public class AudioMuxerCallback implements MediaEncoder.Callback {
16 |
17 | //混合器
18 | private final MediaMuxer mediaMuxer;
19 |
20 | private final AudioPresentationTime presentationTime;
21 |
22 | private int audioTrack;
23 |
24 | private boolean isMuxerStarted;
25 |
26 | public AudioMuxerCallback(String path, AudioPresentationTime presentationTime) throws IOException {
27 | mediaMuxer = new MediaMuxer(path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
28 | this.presentationTime = presentationTime;
29 | }
30 |
31 | @Override
32 | public void onInitStart() {
33 | audioTrack = -1;
34 | isMuxerStarted = false;
35 |
36 | presentationTime.start();
37 | }
38 |
39 | @Override
40 | public void onFormatChanged(MediaCodec mediaCodec) {
41 | audioTrack = mediaMuxer.addTrack(mediaCodec.getOutputFormat());
42 | mediaMuxer.start();
43 | isMuxerStarted = true;
44 | }
45 |
46 | @Override
47 | public void onEncodeInputBuffer(MediaCodec mediaCodec, byte[] buffer, int inputBufferIndex) {
48 | mediaCodec.queueInputBuffer(inputBufferIndex, 0, buffer.length, presentationTime.getPresentationTimeUs(), 0);
49 | }
50 |
51 | @Override
52 | public void onWriteData(MediaCodec.BufferInfo bufferInfo, ByteBuffer encodeData) {
53 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
54 | bufferInfo.size = 0;
55 | }
56 | if (bufferInfo.size != 0) {
57 | encodeData.position(bufferInfo.offset);
58 | encodeData.limit(bufferInfo.offset + bufferInfo.size);
59 | mediaMuxer.writeSampleData(audioTrack, encodeData, bufferInfo);
60 | }
61 | }
62 |
63 | @Override
64 | public void onRelease() {
65 | if (isMuxerStarted) {
66 | mediaMuxer.stop();
67 | mediaMuxer.release();
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/audio/AudioPresentationTime.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.audio;
2 |
3 | import android.media.AudioFormat;
4 |
5 | /**
6 | * Created by you on 2018-05-16.
7 | * 音频时间采样计算
8 | * E/MPEG4Writer: timestampUs 6220411 < lastTimestampUs 6220442 for Audio track
9 | *
10 | */
11 | public final class AudioPresentationTime {
12 |
13 | private long startTime;
14 |
15 | private final long bufferDurationUs;
16 |
17 | private long currentCount;
18 |
19 | /**
20 | *
21 | * @param bufferSize
22 | * @param sampleRate
23 | * @param channelCount
24 | * @param audioFormat
25 | */
26 | public AudioPresentationTime(int bufferSize, int sampleRate, int channelCount, int audioFormat) {
27 | int bitByteSize = audioFormat == AudioFormat.ENCODING_PCM_16BIT ? 2 : 1; //16bit = 2 byte
28 | bufferDurationUs = 1_000_000L * (bufferSize / (channelCount * bitByteSize)) / sampleRate;
29 | }
30 |
31 | public void start() {
32 | startTime = System.nanoTime() / 1000L;
33 | currentCount = 0;
34 | }
35 |
36 | public long getPresentationTimeUs() {
37 | return currentCount++ * bufferDurationUs + startTime;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/audio/AudioRecorder.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.audio;
2 |
3 | import android.media.AudioRecord;
4 | import android.media.MediaCodec;
5 | import android.media.MediaRecorder;
6 |
7 | import java.io.IOException;
8 | import java.util.concurrent.ExecutorService;
9 |
10 | import you.chen.media.core.BytePool;
11 | import you.chen.media.core.Constant;
12 | import you.chen.media.core.MediaEncoder;
13 | import you.chen.media.core.MediaUtils;
14 |
15 | /**
16 | * Created by you on 2018-05-13.
17 | */
18 | public class AudioRecorder {
19 |
20 | public static AudioRecorder createMuxerAudioRecorder(String path) throws IOException {
21 | return createMuxerAudioRecorder(path, Constant.SAMPLE_RATE, Constant.CHANNEL_COUNT,
22 | Constant.AUDIO_RATE, Constant.AAC_PROFILE, Constant.CHANNEL_CONFIG, Constant.AUDIO_FORMAT);
23 | }
24 |
25 | public static AudioRecorder createMuxerAudioRecorder(String path, int sampleRate, int channelCount,
26 | int audioBitRate, int aacProfile,
27 | int channelConfig, int audioFormat) throws IOException {
28 | int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
29 | MediaCodec mediaCodec = MediaUtils.createAacMediaCodec(bufferSize,
30 | sampleRate, channelCount, audioBitRate, aacProfile);
31 | AudioPresentationTime presentationTime = new AudioPresentationTime(bufferSize, sampleRate, channelCount, audioFormat);
32 | AudioMuxerCallback callback = new AudioMuxerCallback(path, presentationTime);
33 |
34 | MediaEncoder encoder = new MediaEncoder(mediaCodec, new BytePool(bufferSize), new AudioTransform(), callback);
35 | AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
36 | sampleRate, channelConfig, audioFormat, bufferSize);
37 | AudioTaker audioTaker = new AudioTaker(audioRecord, bufferSize, encoder);
38 | return new AudioRecorder(encoder, audioTaker);
39 | }
40 |
41 | @Deprecated
42 | public static AudioRecorder createAudioRecorder(String path) {
43 | return createAudioRecorder(path, Constant.SAMPLE_RATE, Constant.CHANNEL_COUNT,
44 | Constant.AUDIO_RATE, Constant.AAC_PROFILE, Constant.CHANNEL_CONFIG, Constant.AUDIO_FORMAT);
45 | }
46 |
47 | @Deprecated
48 | public static AudioRecorder createAudioRecorder(String path, int sampleRate, int channelCount,
49 | int audioBitRate, int aacProfile,
50 | int channelConfig, int audioFormat) {
51 | int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
52 | MediaCodec mediaCodec = MediaUtils.createAacMediaCodec(bufferSize,
53 | sampleRate, channelCount, audioBitRate, aacProfile);
54 | AudioPresentationTime presentationTime = new AudioPresentationTime(bufferSize, sampleRate, channelCount, audioFormat);
55 | AudioCallback callback = new AudioCallback(path, presentationTime);
56 | MediaEncoder encoder = new MediaEncoder(mediaCodec, new BytePool(bufferSize), new AudioTransform(), callback);
57 |
58 | AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
59 | sampleRate, channelConfig, audioFormat, bufferSize);
60 | AudioTaker audioTaker = new AudioTaker(audioRecord, bufferSize, encoder);
61 | return new AudioRecorder(encoder, audioTaker);
62 | }
63 |
64 | private final MediaEncoder encoder;
65 |
66 | private final AudioTaker audioTaker;
67 |
68 | public AudioRecorder(MediaEncoder encoder, AudioTaker audioTaker) {
69 | this.encoder = encoder;
70 | this.audioTaker = audioTaker;
71 | }
72 |
73 | /**
74 | * 启动
75 | * @param service 需要最少2个线程
76 | */
77 | public void start(ExecutorService service) {
78 | encoder.start(service);
79 | audioTaker.start(service);
80 | }
81 |
82 | public void stop() {
83 | audioTaker.stop();
84 | encoder.stop();
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/audio/AudioTaker.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.audio;
2 |
3 | import android.media.AudioRecord;
4 |
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.atomic.AtomicBoolean;
7 |
8 | import you.chen.media.core.MediaEncoder;
9 | import you.chen.media.utils.LogUtils;
10 |
11 | /**
12 | * Created by you on 2018-05-10.
13 | * AudioRecord 取PCM数据
14 | */
15 | public class AudioTaker implements Runnable {
16 |
17 | //语音录制
18 | private final AudioRecord audioRecord;
19 | //编码器
20 | private final MediaEncoder encoder;
21 | //缓冲数组
22 | private final byte[] buffer;
23 | //是否正在录制
24 | private final AtomicBoolean isRecording = new AtomicBoolean(false);
25 |
26 | public AudioTaker(AudioRecord audioRecord, int bufferSize, MediaEncoder encoder) {
27 | this.audioRecord = audioRecord;
28 | this.buffer = new byte[bufferSize];
29 | this.encoder = encoder;
30 | }
31 |
32 | /**
33 | * 在线程执行runnable前调用start
34 | */
35 | public final void start(ExecutorService service) {
36 | audioRecord.startRecording();
37 | isRecording.set(true);
38 | service.execute(this);
39 | }
40 |
41 | public final void stop() {
42 | isRecording.set(false);
43 | }
44 |
45 | @Override
46 | public void run() {
47 | while (isRecording.get()) {
48 | int len = audioRecord.read(buffer, 0, buffer.length);
49 | if (len > 0) {
50 | encoder.push(buffer, len);
51 | }
52 | }
53 | release();
54 | }
55 |
56 | /**
57 | * 释放资源
58 | */
59 | private void release() {
60 | LogUtils.i("audio release...");
61 | if (audioRecord != null) {
62 | audioRecord.stop();
63 | audioRecord.release();
64 | }
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/audio/AudioTransform.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.audio;
2 |
3 | import you.chen.media.core.Transform;
4 |
5 | /**
6 | * Created by you on 2018-03-20.
7 | * 暂不做处理
8 | */
9 | public class AudioTransform implements Transform {
10 |
11 | @Override
12 | public void transform(byte[] src, byte[] outs, int len) {
13 | System.arraycopy(src, 0, outs, 0, len);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/h264/AvcTransform.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.h264;
2 |
3 | import android.media.MediaCodecInfo;
4 |
5 | import you.chen.media.core.Orientation;
6 | import you.chen.media.core.Transform;
7 | import you.chen.media.core.YuvUtils;
8 |
9 | /**
10 | * 相机NV21数据转换操作
11 | */
12 | public class AvcTransform implements Transform {
13 |
14 | /**
15 | * 转换的宽与高
16 | */
17 | private final int w, h;
18 | //MediaCodec colorFormat
19 | private final int colorFormat;
20 |
21 | @Orientation.OrientationMode
22 | private final int orientation;
23 |
24 | public AvcTransform(int w, int h, int colorFormat, @Orientation.OrientationMode int orientation) {
25 | this.w = w;
26 | this.h = h;
27 | this.colorFormat = colorFormat;
28 | this.orientation = orientation;
29 | }
30 |
31 | /**
32 | *
33 | * @param nv21 相机出来的数据
34 | * @param outs 转变后的数据储存数组
35 | */
36 | @Override
37 | public void transform(byte[] nv21, byte[] outs, int len) {
38 | switch (colorFormat) {
39 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
40 | YuvUtils.nv21ToI420Rotate(nv21, outs, w, h, orientation);
41 | break;
42 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
43 | YuvUtils.nv21ToNV12Rotate(nv21, outs, w, h, orientation);
44 | break;
45 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
46 | YuvUtils.nv21ToYV12Rotate(nv21, outs, w, h, orientation);
47 | break;
48 | default:
49 | YuvUtils.nv21Rotate(nv21, outs, w, h, orientation);
50 | break;
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/h264/ClipAvcTransform.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.h264;
2 |
3 | import android.media.MediaCodecInfo;
4 |
5 | import you.chen.media.core.Orientation;
6 | import you.chen.media.core.Transform;
7 | import you.chen.media.core.YuvUtils;
8 | import you.chen.media.utils.LogUtils;
9 |
10 | /**
11 | * 相机NV21数据剪切转换操作
12 | */
13 | public class ClipAvcTransform implements Transform {
14 | /**
15 | * 转换后的宽与高
16 | */
17 | private final int w, h;
18 | //MediaCodec colorFormat
19 | private final int colorFormat;
20 |
21 | @Orientation.OrientationMode
22 | private final int orientation;
23 |
24 | /**
25 | * 原始预览的图片宽与高
26 | */
27 | private final int width, height;
28 | /**
29 | * 裁剪的点坐标
30 | */
31 | private final int left, top;
32 |
33 | public ClipAvcTransform(int w, int h, int width, int height, int colorFormat,
34 | @Orientation.OrientationMode int orientation) {
35 | this.w = w;
36 | this.h = h;
37 | this.colorFormat = colorFormat;
38 | this.orientation = orientation;
39 | this.width = width;
40 | this.height = height;
41 |
42 | this.left = w < width ? (((width - w) / 2 + 1) & ~1) : 0;//偏移也必须为偶数
43 | this.top = h < height ? (((height - h) / 2 + 1) & ~1) : 0;
44 | LogUtils.i("%d - %d, %d - %d, %d - %d, %d", width, height, w, h, left, top, colorFormat);
45 | }
46 |
47 | @Override
48 | public void transform(byte[] nv21, byte[] outs, int len) {
49 | switch (colorFormat) {
50 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
51 | YuvUtils.clipNv21ToI420Rotate(nv21, outs, width, height, w, h, left, top, orientation);
52 | break;
53 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
54 | YuvUtils.clipNv21ToNV12Rotate(nv21, outs, width, height, w, h, left, top, orientation);
55 | break;
56 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
57 | YuvUtils.clipNv21ToYV12Rotate(nv21, outs, width, height, w, h, left, top, orientation);
58 | break;
59 | default:
60 | YuvUtils.clipNv21Rotate(nv21, outs, width, height, w, h, left, top, orientation);
61 | break;
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/h264/H264Callback.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.h264;
2 |
3 | import android.media.MediaCodec;
4 |
5 | import java.io.BufferedOutputStream;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.nio.ByteBuffer;
9 |
10 | import you.chen.media.core.MediaEncoder;
11 | import you.chen.media.utils.FileUtils;
12 |
13 | /**
14 | * Created by you on 2018-05-10.
15 | * h264是没有时间戳概念的,就是一堆流文件,需要播放速度统一可以使用MediaMuxer混合器进行时间戳对齐
16 | * @deprecated To use {@link H264MuxerCallback}
17 | */
18 | @Deprecated
19 | public final class H264Callback implements MediaEncoder.Callback {
20 |
21 | //BufferInfo中的大小不固定,可以用大小固定的缓冲数组写出
22 | public static final int WRITE_BUFFER_SIZE = 1024 << 4;
23 | //储存数据
24 | private final String path;
25 | private FileOutputStream fos;
26 | private BufferedOutputStream bos;
27 | //写入数据缓冲
28 | private byte[] writeBuffer;
29 |
30 | public H264Callback(String path) {
31 | this.path = path;
32 | }
33 |
34 | @Override
35 | public void onInitStart() {
36 | writeBuffer = new byte[WRITE_BUFFER_SIZE];
37 | }
38 |
39 | @Override
40 | public void onFormatChanged(MediaCodec mediaCodec) {
41 | try {
42 | fos = new FileOutputStream(path);
43 | bos = new BufferedOutputStream(fos);
44 | } catch (IOException e) {
45 | e.printStackTrace();
46 | }
47 | }
48 |
49 | @Override
50 | public void onEncodeInputBuffer(MediaCodec mediaCodec, byte[] buffer, int inputBufferIndex) {
51 | mediaCodec.queueInputBuffer(inputBufferIndex, 0, buffer.length,
52 | System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME);
53 | }
54 |
55 | @Override
56 | public void onWriteData(MediaCodec.BufferInfo bufferInfo, ByteBuffer encodeData) {
57 | if (bufferInfo.size != 0) {
58 | //将ByteBuffer中的数据写到文件中
59 | // LogUtils.i("write buffinfosize %d", bufferInfo.size);
60 | int offset = bufferInfo.offset;
61 | int bufferSize = bufferInfo.size;
62 | while (bufferSize > writeBuffer.length) {
63 | writeByteBuffer(encodeData, offset, writeBuffer.length);
64 | bufferSize -= writeBuffer.length;
65 | offset += writeBuffer.length;
66 | }
67 | if (bufferSize > 0) {
68 | writeByteBuffer(encodeData, offset, bufferSize);
69 | }
70 | //byte[] buf = new byte[bufferInfo.size];
71 | //encodeData.get(buf); 不能用此种方式写入,内存抖动极大
72 | }
73 | }
74 |
75 | @Override
76 | public void onRelease() {
77 | FileUtils.closeCloseable(bos, fos);
78 | }
79 |
80 | /**
81 | * 将ByteBuffer通过byte[]写入到文件
82 | * @param encodeData
83 | * @param offset
84 | * @param length
85 | */
86 | private void writeByteBuffer(ByteBuffer encodeData, int offset, int length) {
87 | encodeData.position(offset);
88 | encodeData.limit(offset + length);
89 |
90 | encodeData.get(writeBuffer, 0, length);
91 | try {
92 | bos.write(writeBuffer, 0, length);
93 | } catch (IOException e) {
94 | e.printStackTrace();
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/h264/H264MuxerCallback.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.h264;
2 |
3 | import android.media.MediaCodec;
4 | import android.media.MediaMuxer;
5 |
6 | import java.io.IOException;
7 | import java.nio.ByteBuffer;
8 |
9 | import you.chen.media.core.MediaEncoder;
10 |
11 | /**
12 | * Created by you on 2018-05-10.
13 | * Muxer时间对齐的H264
14 | */
15 | public class H264MuxerCallback implements MediaEncoder.Callback {
16 |
17 | //混合器
18 | private final MediaMuxer mediaMuxer;
19 |
20 | private int h264TrackIndex = -1;
21 |
22 | private boolean isMuxerStarted = false;
23 |
24 | public H264MuxerCallback(String path) throws IOException {
25 | mediaMuxer = new MediaMuxer(path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
26 | }
27 |
28 | @Override
29 | public void onInitStart() {
30 | h264TrackIndex = -1;
31 | isMuxerStarted = false;
32 | }
33 |
34 | @Override
35 | public void onEncodeInputBuffer(MediaCodec mediaCodec, byte[] buffer, int inputBufferIndex) {
36 | mediaCodec.queueInputBuffer(inputBufferIndex, 0, buffer.length,
37 | System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME);
38 | }
39 |
40 | @Override
41 | public void onFormatChanged(MediaCodec mediaCodec) {
42 | h264TrackIndex = mediaMuxer.addTrack(mediaCodec.getOutputFormat());
43 | mediaMuxer.start();
44 | isMuxerStarted = true;
45 | }
46 |
47 | @Override
48 | public void onWriteData(MediaCodec.BufferInfo bufferInfo, ByteBuffer encodeData) {
49 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
50 | bufferInfo.size = 0;
51 | }
52 | if (bufferInfo.size != 0) {
53 | encodeData.position(bufferInfo.offset);
54 | encodeData.limit(bufferInfo.offset + bufferInfo.size);
55 | mediaMuxer.writeSampleData(h264TrackIndex, encodeData, bufferInfo);
56 | }
57 | }
58 |
59 | @Override
60 | public void onRelease() {
61 | if (isMuxerStarted) {
62 | mediaMuxer.stop();
63 | mediaMuxer.release();
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/h264/H264Utils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.h264;
2 |
3 | import android.graphics.Matrix;
4 | import android.graphics.Point;
5 | import android.media.MediaCodec;
6 |
7 | import you.chen.media.camera.CameraUtils;
8 | import you.chen.media.core.BytePool;
9 | import you.chen.media.core.Constant;
10 | import you.chen.media.core.MediaEncoder;
11 | import you.chen.media.core.MediaUtils;
12 | import you.chen.media.core.Orientation;
13 | import you.chen.media.core.Transform;
14 |
15 | /**
16 | * Created by you on 2018-05-19.
17 | */
18 | public final class H264Utils {
19 |
20 | private H264Utils() {}
21 |
22 | public static MediaEncoder createH264MediaEncoder(int width, int height, Matrix matrix,
23 | @Orientation.OrientationMode int orientation,
24 | MediaEncoder.Callback callback) {
25 | int h264BitRate = width * height * Constant.VIDEO_BITRATE_COEFFICIENT;
26 | return createH264MediaEncoder(MediaUtils.selectColorFormat(), width, height, matrix,
27 | orientation, h264BitRate, Constant.FRAME_RATE, Constant.IFRAME_INTERVAL, callback);
28 | }
29 |
30 | public static MediaEncoder createH264MediaEncoder(int colorFormat, int width, int height, Matrix matrix,
31 | @Orientation.OrientationMode int orientation,
32 | int h264BitRate, int frameRate, int frameInterval,
33 | MediaEncoder.Callback callback) {
34 | Point matrixSize = CameraUtils.matrixSize(width, height, matrix);
35 | BytePool bytePool = new BytePool(matrixSize.x * matrixSize.y * 3 / 2);
36 | MediaCodec h264Codec = MediaUtils.createAvcMediaCodec(matrixSize.x, matrixSize.y,
37 | colorFormat, orientation, h264BitRate, frameRate, frameInterval);
38 | Transform transform = matrixSize.equals(width, height) ?
39 | new AvcTransform(width, height, colorFormat, orientation)
40 | : new ClipAvcTransform(matrixSize.x, matrixSize.y, width, height, colorFormat, orientation);
41 | return new MediaEncoder(h264Codec, bytePool, transform, callback);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/scan/DecodeThread.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.scan;
2 |
3 | import android.os.Handler;
4 |
5 | import com.google.zxing.Result;
6 |
7 | import java.util.concurrent.atomic.AtomicBoolean;
8 |
9 | import you.chen.media.core.Transform;
10 | import you.chen.media.utils.LogUtils;
11 |
12 | /**
13 | * Created by you on 2018-04-26.
14 | * 解析线程, 不断的{@link DecodeThread#push(byte[])} 并解析
15 | */
16 | public class DecodeThread extends Thread {
17 |
18 | public static final int HANDLE_SUCCESS = 1;
19 |
20 | private final int w, h;
21 | //扫描解析器
22 | private final FormatDecoder decoder;
23 | //要解码的转换处理后的数据
24 | private final byte[] buffer;
25 |
26 | private Handler handler;
27 |
28 | private final Transform transform;
29 |
30 | private boolean isRunning;
31 |
32 | //是否正在解码
33 | private final AtomicBoolean isCoding = new AtomicBoolean(false);
34 | //扫描成功后退出
35 | private final boolean successQuit;
36 |
37 | /**
38 | * Camera.setDisplayOrientation(90), 270; 时 w, h顺序调换
39 | * @param w
40 | * @param h
41 | * @param decoder
42 | * @param handler
43 | * @param transform
44 | * @param successQuit 需要持续性的扫描时可以设置false
45 | */
46 | public DecodeThread(int w, int h, FormatDecoder decoder, Handler handler, Transform transform, boolean successQuit) {
47 | this.w = w;
48 | this.h = h;
49 | this.decoder = decoder;
50 | this.buffer = new byte[w * h * 3 / 2];
51 | this.handler = handler;
52 | this.transform = transform;
53 | this.successQuit = successQuit;
54 | }
55 |
56 | public synchronized final void push(byte[] data) {
57 | if (isCoding.get() || !isRunning) {
58 | return;
59 | }
60 | transform.transform(data, buffer, data.length);
61 | isCoding.set(true);
62 | notify();
63 | }
64 |
65 | private synchronized void decode() {
66 | while (!isCoding.get() && isRunning) {
67 | try {
68 | wait();
69 | } catch (InterruptedException e) {
70 | e.printStackTrace();
71 | }
72 | }
73 | }
74 |
75 | @Override
76 | public synchronized void start() {
77 | isRunning = true;
78 | super.start();
79 | }
80 |
81 | public synchronized void quit() {
82 | isRunning = false;
83 | notify();
84 | }
85 |
86 | @Override
87 | public void run() {
88 | LogUtils.i("ScanDecoder thread start...");
89 | while (isRunning) {
90 | decode();
91 | if (!isRunning) {
92 | break;
93 | }
94 | Result result = decoder.decode(buffer, w, h);
95 | if (result != null) {
96 | handler.sendMessage(handler.obtainMessage(HANDLE_SUCCESS, result));
97 | if (successQuit) {
98 | isRunning = false;
99 | break;
100 | }
101 | try {
102 | sleep(2000);//需要持续性的扫描功能时,可以在扫描成功时短暂睡眠
103 | } catch (InterruptedException e) {
104 | e.printStackTrace();
105 | }
106 | }
107 | isCoding.set(false);
108 | }
109 | LogUtils.i("ScanDecoder quit...");
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/scan/DecoderHandler.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.scan;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 |
6 | import com.google.zxing.Result;
7 |
8 | import java.lang.ref.WeakReference;
9 |
10 | import androidx.annotation.NonNull;
11 | import you.chen.media.core.Transform;
12 | import you.chen.media.utils.Utils;
13 |
14 | /**
15 | * Created by you on 2018-04-26.
16 | * {@link DecodeThread}与主线程 交互
17 | */
18 | public final class DecoderHandler extends Handler {
19 |
20 | private final DecodeThread decodeThread;
21 |
22 | private SoundVibratorHelper soundVibratorHelper;
23 |
24 | private final WeakReference callbackWeakReference;
25 |
26 | public DecoderHandler(int w, int h, Transform transform, DecoderCallback callback) {
27 | this(w, h, new FormatDecoder(), transform, true, callback);
28 | }
29 |
30 | public DecoderHandler(int w, int h, Transform transform, boolean successQuit, DecoderCallback callback) {
31 | this(w, h, new FormatDecoder(), transform, successQuit, callback);
32 | }
33 |
34 | public DecoderHandler(int w, int h, FormatDecoder decoder,
35 | Transform transform, boolean successQuit, DecoderCallback callback) {
36 | callbackWeakReference = new WeakReference<>(callback);
37 | decodeThread = new DecodeThread(w, h, decoder, this, transform, successQuit);
38 | soundVibratorHelper = new SoundVibratorHelper(Utils.context());
39 | decodeThread.start();
40 | }
41 |
42 | public void push(byte[] data) {
43 | decodeThread.push(data);
44 | }
45 |
46 | public void stop() {
47 | soundVibratorHelper.stop();
48 | decodeThread.quit();
49 | }
50 |
51 | @Override
52 | public void handleMessage(@NonNull Message msg) {
53 | if (msg.what == DecodeThread.HANDLE_SUCCESS && msg.obj != null) {
54 | Result result = (Result) msg.obj;
55 | DecoderCallback callback = callbackWeakReference.get();
56 | if (callback != null) {
57 | soundVibratorHelper.play();
58 | callback.handleResult(result);
59 | }
60 | }
61 | }
62 |
63 | /**
64 | * 回调处理
65 | */
66 | public interface DecoderCallback {
67 |
68 | void handleResult(Result result);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/scan/FormatDecoder.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.scan;
2 |
3 | import com.google.zxing.BarcodeFormat;
4 | import com.google.zxing.BinaryBitmap;
5 | import com.google.zxing.DecodeHintType;
6 | import com.google.zxing.MultiFormatReader;
7 | import com.google.zxing.PlanarYUVLuminanceSource;
8 | import com.google.zxing.Result;
9 | import com.google.zxing.ResultPointCallback;
10 | import com.google.zxing.common.HybridBinarizer;
11 |
12 | import java.util.EnumMap;
13 | import java.util.EnumSet;
14 | import java.util.Set;
15 |
16 | /**
17 | * Created by you on 2018-04-26.
18 | * 扫描解析器
19 | */
20 | public class FormatDecoder {
21 |
22 | private MultiFormatReader formatReader;
23 |
24 | /**
25 | *
26 | * //根据实际需求添加BarcodeFormat, 此构造只支持二维码与各种条形码
27 | * BarcodeFormat.DATA_MATRIX, BarcodeFormat.AZTEC, BarcodeFormat.PDF_417
28 | *
29 | */
30 | public FormatDecoder() {
31 | this(EnumSet.of(BarcodeFormat.QR_CODE,
32 | BarcodeFormat.UPC_A,
33 | BarcodeFormat.UPC_E,
34 | BarcodeFormat.EAN_13,
35 | BarcodeFormat.EAN_8,
36 | BarcodeFormat.RSS_14,
37 | BarcodeFormat.RSS_EXPANDED,
38 | BarcodeFormat.CODE_39,
39 | BarcodeFormat.CODE_93,
40 | BarcodeFormat.CODE_128,
41 | BarcodeFormat.ITF,
42 | BarcodeFormat.CODABAR), null, null);
43 | }
44 |
45 | public FormatDecoder(Set formatSet, String characterSet, ResultPointCallback callback) {
46 | formatReader = new MultiFormatReader();
47 | EnumMap hints = new EnumMap<>(DecodeHintType.class);
48 | hints.put(DecodeHintType.POSSIBLE_FORMATS, formatSet);
49 | if (characterSet != null) {
50 | hints.put(DecodeHintType.CHARACTER_SET, characterSet);
51 | }
52 | if (callback != null) {
53 | hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, callback);
54 | }
55 | formatReader.setHints(hints);
56 | }
57 |
58 | /**
59 | * 解码camera datas
60 | * @param datas
61 | * @param w
62 | * @param h
63 | * @return
64 | */
65 | public Result decode(byte[] datas, int w, int h) {
66 | PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(datas, w, h, 0, 0, w, h, false);
67 | BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
68 | try {
69 | return formatReader.decodeWithState(bitmap);
70 | } catch (Exception e) {
71 | // LogUtils.e(e);
72 | } finally {
73 | formatReader.reset();
74 | }
75 | return null;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/core/scan/SoundVibratorHelper.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.core.scan;
2 |
3 | import android.content.Context;
4 | import android.media.AudioAttributes;
5 | import android.media.AudioManager;
6 | import android.media.SoundPool;
7 | import android.os.Vibrator;
8 |
9 | import you.chen.media.R;
10 |
11 | /**
12 | * Created by you on 2018-04-26.
13 | * 扫描成功时的声音震动操作
14 | */
15 | public final class SoundVibratorHelper {
16 | /**
17 | * 振动时间
18 | */
19 | private static final long DEF_VIBRATE_DURATION = 200L;
20 |
21 | private SoundPool soundPool;
22 |
23 | private int soundId;
24 | /**
25 | * 振动
26 | */
27 | private Vibrator vibrator;
28 |
29 | public SoundVibratorHelper(Context context) {
30 | context = context.getApplicationContext();
31 | vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
32 |
33 | SoundPool.Builder builder = new SoundPool.Builder();
34 | builder.setMaxStreams(2);
35 |
36 | AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
37 | attrBuilder.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION);
38 | builder.setAudioAttributes(attrBuilder.build());
39 | soundPool = builder.build();
40 |
41 | soundId = soundPool.load(context, R.raw.beep, 1);
42 | }
43 |
44 | public void play() {
45 | vibrator.cancel();
46 | vibrator.vibrate(DEF_VIBRATE_DURATION);
47 | soundPool.play(soundId, 1, 1, 0, 0, 1);
48 | }
49 |
50 | public void stop() {
51 | soundPool.release();
52 | vibrator.cancel();
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/NothingSubscribe.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx;
2 |
3 | import io.reactivex.Observer;
4 | import io.reactivex.disposables.Disposable;
5 |
6 | /**
7 | * Created by you on 2018/7/30.
8 | */
9 |
10 | public abstract class NothingSubscribe implements Observer {
11 |
12 | private Disposable disposable;
13 |
14 | @Override
15 | public void onSubscribe(Disposable disposable) {
16 | this.disposable = disposable;
17 | }
18 |
19 | @Override
20 | public void onError(Throwable e) {
21 | RxUtils.dispose(disposable);
22 | }
23 |
24 | @Override
25 | public void onComplete() {
26 | RxUtils.dispose(disposable);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/RequestRetry.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import io.reactivex.Observable;
6 | import io.reactivex.ObservableSource;
7 | import io.reactivex.functions.Function;
8 |
9 | /**
10 | * Created by you on 2017/7/5.
11 | * 请求重试类
12 | */
13 |
14 | public class RequestRetry implements Function, ObservableSource>> {
15 | /**
16 | * 默认重试次数
17 | */
18 | private static final int DEF_MAXRETRIES = 1;
19 | /**
20 | * 默认重试时间间隔
21 | */
22 | private static final int DEF_DELAYMILLIS = 2000;
23 | /**
24 | * 最大重试次数
25 | */
26 | private final int maxRetries;
27 | /**
28 | * 重试间隔
29 | */
30 | private final int retryDelayMillis;
31 |
32 | private int retryCount;
33 |
34 | public static RequestRetry def() {
35 | return new RequestRetry(DEF_MAXRETRIES, DEF_DELAYMILLIS);
36 | }
37 |
38 | public RequestRetry(int maxRetries, int retryDelayMillis) {
39 | this.maxRetries = maxRetries;
40 | this.retryDelayMillis = retryDelayMillis;
41 | }
42 |
43 | @Override
44 | public ObservableSource> apply(Observable extends Throwable> observable) throws Exception {
45 | return observable.flatMap(new Function>() {
46 | @Override
47 | public ObservableSource> apply(Throwable throwable) throws Exception {
48 | if (++retryCount <= maxRetries) {
49 | return Observable.timer(retryDelayMillis, TimeUnit.MILLISECONDS);
50 | }
51 | return Observable.error(throwable);
52 | }
53 | });
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/RxUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx;
2 |
3 | import android.view.View;
4 | import android.widget.TextView;
5 |
6 | import java.util.concurrent.TimeUnit;
7 |
8 | import io.reactivex.Observable;
9 | import io.reactivex.android.schedulers.AndroidSchedulers;
10 | import io.reactivex.disposables.Disposable;
11 | import you.chen.media.rx.viewbind.TextViewChangedOnSubscribe;
12 | import you.chen.media.rx.viewbind.ViewClickOnSubscribe;
13 |
14 | /**
15 | * Created by you on 2018/1/11.
16 | */
17 |
18 | public final class RxUtils {
19 |
20 | private RxUtils() {}
21 |
22 | public static void dispose(Disposable...disposables) {
23 | if (disposables != null && disposables.length > 0) {
24 | for (Disposable disposable : disposables) {
25 | if (disposable != null && !disposable.isDisposed()) {
26 | disposable.dispose();
27 | }
28 | }
29 | }
30 | }
31 |
32 | /**
33 | * 默认防抖一秒
34 | */
35 | public static final int DEF_DURATION_CLICK = 1000;
36 |
37 | public static Observable click(View... views) {
38 | return click(DEF_DURATION_CLICK, views);
39 | }
40 |
41 | /**
42 | *
43 | * @param viewArray 方便BindViews时的参数传入
44 | * @param views
45 | * @return
46 | */
47 | public static Observable click(View[] viewArray, View... views) {
48 | return click(DEF_DURATION_CLICK, viewArray, views);
49 | }
50 |
51 | /**
52 | * 生成防抖
53 | * @param clickDuration
54 | * @param views
55 | * @return
56 | */
57 | public static Observable click(long clickDuration , View... views ) {
58 | ViewClickOnSubscribe clickOnSubscribe = new ViewClickOnSubscribe();
59 | clickOnSubscribe.addOnClickListener(views);
60 | return Observable.create(clickOnSubscribe).throttleFirst(clickDuration , TimeUnit.MILLISECONDS);
61 | }
62 |
63 | /**
64 | *
65 | * @param clickDuration
66 | * @param viewArray 方便BindViews时的参数传入
67 | * @param views
68 | * @return
69 | */
70 | public static Observable click(long clickDuration, View[] viewArray, View... views) {
71 | ViewClickOnSubscribe clickOnSubscribe = new ViewClickOnSubscribe();
72 | clickOnSubscribe.addOnClickListener(viewArray);
73 | clickOnSubscribe.addOnClickListener(views);
74 | return Observable.create(clickOnSubscribe).throttleFirst(clickDuration , TimeUnit.MILLISECONDS);
75 | }
76 |
77 | public static final int DEF_TIMEOUT = 300;
78 |
79 | public static Observable textChanged(TextView tv) {
80 | return textChanged(DEF_TIMEOUT, tv);
81 | }
82 |
83 | public static Observable textChanged(long timeout, TextView tv) {
84 | TextViewChangedOnSubscribe subscribe = new TextViewChangedOnSubscribe();
85 | subscribe.addTextViewWatcher(tv);
86 | return Observable.create(subscribe).debounce(timeout, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread());
87 | }
88 |
89 | /**
90 | * 秒倒计时
91 | * @param second
92 | * @return
93 | */
94 | public static Observable stimer(long second) {
95 | return Observable.timer(second, TimeUnit.SECONDS, AndroidSchedulers.mainThread());
96 | }
97 |
98 | /**
99 | * 定时发送, 主线程
100 | * @param second 秒
101 | * @return
102 | */
103 | public static Observable sinterval(long second) {
104 | return Observable.interval(second, TimeUnit.SECONDS, AndroidSchedulers.mainThread());
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/ThrowableConsumer.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx;
2 |
3 |
4 | import io.reactivex.functions.Consumer;
5 |
6 | /**
7 | * Created by you on 2017-02-10.
8 | */
9 | public class ThrowableConsumer implements Consumer {
10 |
11 | @Override
12 | public void accept(Throwable throwable) throws Exception {
13 | //nothing
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/perm/Permission.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx.perm;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created by you on 2018/3/5.
7 | */
8 |
9 | public class Permission {
10 |
11 | public final String name;
12 | /**
13 | * 是否放行
14 | */
15 | public final boolean granted;
16 |
17 | public final boolean shouldShowRequestPermissionRationale;
18 |
19 | public Permission(String name, boolean granted) {
20 | this(name, granted, false);
21 | }
22 |
23 | public Permission(String name, boolean granted, boolean shouldShowRequestPermissionRationale) {
24 | this.name = name;
25 | this.granted = granted;
26 | this.shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale;
27 | }
28 |
29 | public Permission(List permissions) {
30 | name = combineName(permissions);
31 | granted = combineGranted(permissions);
32 | shouldShowRequestPermissionRationale = combineShouldShowRequestPermissionRationale(permissions);
33 | }
34 |
35 | @Override
36 | public boolean equals(final Object o) {
37 | if (this == o) return true;
38 | if (o == null || getClass() != o.getClass()) return false;
39 | final Permission that = (Permission) o;
40 | if (granted != that.granted) return false;
41 | if (shouldShowRequestPermissionRationale != that.shouldShowRequestPermissionRationale)
42 | return false;
43 | return name.equals(that.name);
44 | }
45 |
46 | @Override
47 | public int hashCode() {
48 | int result = name.hashCode();
49 | result = 31 * result + (granted ? 1 : 0);
50 | result = 31 * result + (shouldShowRequestPermissionRationale ? 1 : 0);
51 | return result;
52 | }
53 |
54 | @Override
55 | public String toString() {
56 | return "Permission{" +
57 | "name='" + name + '\'' +
58 | ", granted=" + granted +
59 | ", shouldShowRequestPermissionRationale=" + shouldShowRequestPermissionRationale +
60 | '}';
61 | }
62 |
63 | private String combineName(List permissions) {
64 | if (permissions == null || permissions.isEmpty()) return "";
65 | StringBuilder sb = new StringBuilder();
66 | for (Permission permission : permissions) {
67 | if (sb.length() == 0) {
68 | sb.append(permission.name);
69 | } else {
70 | sb.append(", ").append(permission.name);
71 | }
72 | }
73 | return sb.toString();
74 | }
75 |
76 | private Boolean combineGranted(List permissions) {
77 | if (permissions == null || permissions.isEmpty()) return false;
78 | for (Permission permission : permissions) {
79 | if (!permission.granted) {
80 | return false;
81 | }
82 | }
83 | return true;
84 | }
85 |
86 | private Boolean combineShouldShowRequestPermissionRationale(List permissions) {
87 | if (permissions == null || permissions.isEmpty()) return false;
88 | for (Permission permission : permissions) {
89 | if (permission.shouldShowRequestPermissionRationale) {
90 | return true;
91 | }
92 | }
93 | return false;
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/perm/PermissionFragment.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx.perm;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.pm.PackageManager;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.util.Log;
8 |
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 | import androidx.fragment.app.Fragment;
15 | import io.reactivex.subjects.PublishSubject;
16 |
17 |
18 | /**
19 | * Created by you on 2018/3/5.
20 | */
21 |
22 | public class PermissionFragment extends Fragment {
23 |
24 | private static final String TAG = "PermissionFragment";
25 |
26 | private static final int PERMISSIONS_REQUEST_CODE = 42;
27 | private Map> mSubjects = new HashMap<>();
28 |
29 | @Override
30 | public void onCreate(@Nullable Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setRetainInstance(true);
33 | }
34 |
35 | void requestPermissions(@NonNull String[] permissions) {
36 | requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
37 | }
38 |
39 | @Override
40 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
41 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
42 | if (requestCode != PERMISSIONS_REQUEST_CODE) return;
43 | boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];
44 | for (int i = 0; i < permissions.length; i++) {
45 | shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]);
46 | }
47 | onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);
48 | }
49 |
50 | void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {
51 | for (int i = 0, size = permissions.length; i < size; i++) {
52 | PublishSubject subject = mSubjects.get(permissions[i]);
53 | if (subject == null) {
54 | // No subject found
55 | Log.e(TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
56 | return;
57 | }
58 | mSubjects.remove(permissions[i]);
59 | boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
60 | subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
61 | subject.onComplete();
62 | }
63 | }
64 |
65 | @TargetApi(Build.VERSION_CODES.M)
66 | boolean isGranted(String permission) {
67 | return getActivity().checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
68 | }
69 |
70 | @TargetApi(Build.VERSION_CODES.M)
71 | boolean isRevoked(String permission) {
72 | return getActivity().getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName());
73 | }
74 |
75 | public PublishSubject getSubjectByPermission(@NonNull String permission) {
76 | return mSubjects.get(permission);
77 | }
78 |
79 | public boolean containsByPermission(@NonNull String permission) {
80 | return mSubjects.containsKey(permission);
81 | }
82 |
83 | public PublishSubject setSubjectForPermission(@NonNull String permission, @NonNull PublishSubject subject) {
84 | return mSubjects.put(permission, subject);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/perm/PermissionMustCallback.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx.perm;
2 |
3 |
4 | import io.reactivex.Observer;
5 | import io.reactivex.disposables.Disposable;
6 | import you.chen.media.rx.RxUtils;
7 |
8 | /**
9 | * Created by you on 2018/4/19.
10 | * 必要的权限请求,所请求的权限必须获取
11 | */
12 |
13 | public abstract class PermissionMustCallback implements Observer {
14 |
15 | private boolean isGranted = true;
16 |
17 | private Disposable permissionDisposable;
18 |
19 | @Override
20 | public void onSubscribe(Disposable disposable) {
21 | permissionDisposable = disposable;
22 | }
23 |
24 | @Override
25 | public void onComplete() {
26 | permissionCallback(isGranted);
27 | RxUtils.dispose(permissionDisposable);
28 | }
29 |
30 | @Override
31 | public void onError(Throwable e) {
32 | RxUtils.dispose(permissionDisposable);
33 | }
34 |
35 | @Override
36 | public void onNext(Permission permission) {
37 | isGranted &= permission.granted;
38 | }
39 |
40 | protected abstract void permissionCallback(boolean isGranted);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/viewbind/TextViewChangedOnSubscribe.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx.viewbind;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 | import android.widget.TextView;
6 |
7 | import io.reactivex.ObservableEmitter;
8 | import io.reactivex.ObservableOnSubscribe;
9 | import io.reactivex.functions.Cancellable;
10 |
11 | /**
12 | * Created by you on 2017/4/10.
13 | */
14 |
15 | public class TextViewChangedOnSubscribe implements ObservableOnSubscribe {
16 |
17 | private TextView mTextView;
18 |
19 | public void addTextViewWatcher(TextView mTextView) {
20 | this.mTextView = mTextView;
21 | }
22 |
23 | @Override
24 | public void subscribe(final ObservableEmitter emitter) throws Exception {
25 | final TextWatcher watcher = new TextWatcher() {
26 | @Override
27 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
28 | }
29 |
30 | @Override
31 | public void onTextChanged(CharSequence s, int start, int before, int count) {
32 | }
33 |
34 | @Override
35 | public void afterTextChanged(Editable s) {
36 | if (!emitter.isDisposed()) {
37 | emitter.onNext(s.toString().trim());
38 | }
39 | }
40 | };
41 | mTextView.addTextChangedListener(watcher);
42 | emitter.setCancellable(new Cancellable() {
43 | @Override
44 | public void cancel() throws Exception {
45 | mTextView.removeTextChangedListener(watcher);
46 | }
47 | });
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/rx/viewbind/ViewClickOnSubscribe.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.rx.viewbind;
2 |
3 | import android.view.View;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Iterator;
7 | import java.util.List;
8 |
9 | import io.reactivex.ObservableEmitter;
10 | import io.reactivex.ObservableOnSubscribe;
11 | import io.reactivex.functions.Cancellable;
12 |
13 | /**
14 | * Created by you on 2016/10/19.
15 | */
16 |
17 | public class ViewClickOnSubscribe implements ObservableOnSubscribe {
18 |
19 | /**
20 | * 注册防抖点击的控件
21 | */
22 | private List clickViews = new ArrayList();
23 |
24 | /**
25 | * 添加控件点击事件
26 | * @param views
27 | */
28 | public void addOnClickListener(View... views) {
29 | if (views == null) return;
30 | for (View v : views) {
31 | clickViews.add(v);
32 | }
33 | }
34 |
35 | @Override
36 | public void subscribe(final ObservableEmitter emitter) throws Exception {
37 | View.OnClickListener listener = new View.OnClickListener() {
38 | @Override
39 | public void onClick(View v) {
40 | if (!emitter.isDisposed()) {
41 | emitter.onNext(v);
42 | }
43 | }
44 | };
45 | for (View v : clickViews) {
46 | v.setOnClickListener(listener);
47 | }
48 | emitter.setCancellable(new Cancellable() {
49 | @Override
50 | public void cancel() throws Exception {
51 | Iterator iterator = clickViews.iterator();
52 | while (iterator.hasNext()) {
53 | iterator.next().setOnClickListener(null);
54 | iterator.remove();
55 | }
56 | }
57 | });
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/ui/AacActivity.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.ui;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.CheckBox;
8 | import android.widget.TextView;
9 |
10 | import java.io.IOException;
11 | import java.util.concurrent.ExecutorService;
12 | import java.util.concurrent.Executors;
13 |
14 | import androidx.appcompat.app.AppCompatActivity;
15 | import you.chen.media.R;
16 | import you.chen.media.core.audio.AudioRecorder;
17 | import you.chen.media.utils.FileUtils;
18 |
19 | /**
20 | * Created by you on 2018-03-10.
21 | */
22 | public class AacActivity extends AppCompatActivity implements View.OnClickListener {
23 |
24 | TextView bt;
25 |
26 | CheckBox cb_muxer;
27 |
28 | AudioRecorder recorder;
29 |
30 | //线程池执行
31 | ExecutorService service;
32 |
33 | public static void lanuch(Context context) {
34 | context.startActivity(new Intent(context, AacActivity.class));
35 | }
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.act_aac);
41 | service = Executors.newFixedThreadPool(2);
42 |
43 | initView();
44 | }
45 |
46 | private void initView() {
47 | cb_muxer = findViewById(R.id.cb_muxer);
48 | bt = findViewById(R.id.bt);
49 | bt.setOnClickListener(this);
50 | }
51 |
52 | @Override
53 | public void onClick(View v) {
54 | switch (v.getId()) {
55 | case R.id.bt:
56 | if (!bt.isSelected()) {//start
57 | startRecording();
58 | } else {
59 | stopRecording();
60 | }
61 | break;
62 | }
63 | }
64 |
65 | private void startRecording() {
66 | String saveName = cb_muxer.isChecked() ? "audioMuxer.aac" : "audioTest.aac";
67 | String path = FileUtils.getCacheDirPath() + saveName;
68 | try {
69 | recorder = cb_muxer.isChecked() ? AudioRecorder.createMuxerAudioRecorder(path) : AudioRecorder.createAudioRecorder(path);
70 | recorder.start(service);
71 | } catch (IOException e) {
72 | e.printStackTrace();
73 | }
74 |
75 |
76 | bt.setSelected(true);
77 | bt.setText("end");
78 | }
79 |
80 | private void stopRecording() {
81 | if (recorder != null) {
82 | recorder.stop();
83 | recorder = null;
84 | }
85 | bt.setSelected(false);
86 | bt.setText("start");
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/ui/CameraActivity.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.ui;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Matrix;
7 | import android.graphics.SurfaceTexture;
8 | import android.hardware.Camera;
9 | import android.os.Bundle;
10 | import android.view.TextureView;
11 | import android.view.View;
12 |
13 | import java.io.File;
14 |
15 | import androidx.appcompat.app.AppCompatActivity;
16 | import you.chen.media.R;
17 | import you.chen.media.camera.CameraHelper;
18 | import you.chen.media.camera.CameraUtils;
19 | import you.chen.media.camera.OrientationHelper;
20 | import you.chen.media.camera.SizeFilter;
21 | import you.chen.media.camera.impl.PictureSizeFilter;
22 | import you.chen.media.utils.BitmapUtils;
23 | import you.chen.media.utils.FileUtils;
24 | import you.chen.media.utils.LogUtils;
25 | import you.chen.media.utils.Utils;
26 | import you.chen.media.widget.CameraView;
27 | import you.chen.media.widget.FlashView;
28 | import you.chen.media.widget.FocusView;
29 |
30 | /**
31 | * Created by you on 2018-01-08.
32 | *
33 | */
34 | public class CameraActivity extends AppCompatActivity
35 | implements TextureView.SurfaceTextureListener, View.OnClickListener {
36 |
37 | CameraHelper helper;
38 | //camera最佳筛选
39 | SizeFilter filter;
40 | //预览的缩放相关参数
41 | Matrix matrix;
42 |
43 | //surface
44 | CameraView cv_camera;
45 | //聚焦动画控件
46 | FocusView fv_focus;
47 | //前后置相机切换
48 | View iv_switch;
49 | //闪光灯
50 | FlashView fv_flash;
51 | //方向传感
52 | OrientationHelper orientationHelper;
53 | //后,前置摄像头
54 | int cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
55 |
56 | public static void lanuch(Context context) {
57 | context.startActivity(new Intent(context, CameraActivity.class));
58 | }
59 |
60 | @Override
61 | protected void onCreate(Bundle savedInstanceState) {
62 | super.onCreate(savedInstanceState);
63 | setContentView(R.layout.act_camera);
64 |
65 | helper = new CameraHelper();
66 | filter = new PictureSizeFilter();
67 | orientationHelper = new OrientationHelper(Utils.context());
68 |
69 | initView();
70 | }
71 |
72 | private void initView() {
73 | cv_camera = findViewById(R.id.cv_camera);
74 | fv_focus = findViewById(R.id.fv_focus);
75 | iv_switch = findViewById(R.id.iv_switch);
76 | fv_flash = findViewById(R.id.fv_flash);
77 |
78 | findViewById(R.id.bt).setOnClickListener(this);
79 | iv_switch.setOnClickListener(this);
80 |
81 | cv_camera.setOnCameraGestureListener(new CameraView.OnCameraGestureListener() {
82 | @Override
83 | public void onHandleZoom(float zoomScale) {
84 | helper.handleZoom(zoomScale);
85 | LogUtils.i("handlerZoom %f", zoomScale);
86 | }
87 |
88 | @Override
89 | public void onHandleFocus(float x, float y, int w, int h) {
90 | if (cameraId != Camera.CameraInfo.CAMERA_FACING_FRONT) {
91 | helper.handleFocus(CameraUtils.reverseRotate(x, y, w, h, matrix));
92 | fv_focus.setCenter(x, y);
93 | }
94 | }
95 | });
96 |
97 | fv_flash.setOnFlashChangedListener(model -> helper.setFlashMode(model));
98 | if (CameraHelper.isSupportFrontCamera()) {
99 | iv_switch.setVisibility(View.VISIBLE);
100 | }
101 | }
102 |
103 | @Override
104 | protected void onResume() {
105 | super.onResume();
106 | orientationHelper.enable();
107 | if (cv_camera.isAvailable()) {
108 | startCamera();
109 | } else {
110 | cv_camera.setSurfaceTextureListener(this);
111 | }
112 | }
113 |
114 | @Override
115 | protected void onPause() {
116 | orientationHelper.disable();
117 | stopCamera();
118 | super.onPause();
119 | }
120 |
121 | @Override
122 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
123 | startCamera();
124 | }
125 |
126 | @Override
127 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
128 | }
129 |
130 | @Override
131 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
132 | return true;
133 | }
134 |
135 | @Override
136 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {//nothing
137 | }
138 |
139 | @Override
140 | public void onClick(View view) {
141 | switch (view.getId()) {
142 | case R.id.bt:
143 | helper.takePicture((data, camera) -> {
144 | Bitmap bitmap = CameraUtils.bytesToBitmap(data, matrix, orientationHelper.getOrientation());
145 | File f = new File(FileUtils.getCacheDirPath(), "123.png");
146 | BitmapUtils.saveBitmap(bitmap, f, true);
147 | });
148 | break;
149 | case R.id.iv_switch:
150 | if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
151 | cameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
152 | } else {
153 | cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
154 | }
155 | helper.closeCamera();
156 | matrix = null;
157 | startCamera();
158 | break;
159 | }
160 | }
161 |
162 | //开启相机
163 | private void startCamera() {
164 | matrix = helper.openPicCamera(cv_camera.getSurfaceTexture(), cameraId,
165 | cv_camera.getWidth(), cv_camera.getHeight(), filter, orientationHelper.getOrientation());
166 |
167 | if (matrix != null) {
168 | cv_camera.setTransform(matrix);
169 | }
170 | cv_camera.setMaxScale(helper.getMaxZoomScale());
171 | fv_flash.setFlashModes(helper.getSupportedFlashModes(), cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT);
172 | }
173 |
174 | //释放相机
175 | private void stopCamera() {
176 | helper.closeCamera();
177 | matrix = null;
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.ui;
2 |
3 | import android.Manifest;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import androidx.appcompat.app.AppCompatActivity;
8 | import you.chen.media.R;
9 | import you.chen.media.rx.perm.PermissionMustCallback;
10 | import you.chen.media.rx.perm.RxPermissions;
11 |
12 | public class MainActivity extends AppCompatActivity implements View.OnClickListener {
13 |
14 | RxPermissions permissions;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.act_main);
20 |
21 | permissions = new RxPermissions(this);
22 | findViewById(R.id.bt1).setOnClickListener(this);
23 | findViewById(R.id.bt2).setOnClickListener(this);
24 | findViewById(R.id.bt3).setOnClickListener(this);
25 | findViewById(R.id.bt4).setOnClickListener(this);
26 | findViewById(R.id.bt5).setOnClickListener(this);
27 | findViewById(R.id.bt6).setOnClickListener(this);
28 | findViewById(R.id.bt7).setOnClickListener(this);
29 | findViewById(R.id.bt8).setOnClickListener(this);
30 | }
31 |
32 | @Override
33 | public void onClick(View view) {
34 | switch (view.getId()) {
35 | case R.id.bt1:
36 | permissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE,
37 | Manifest.permission.READ_EXTERNAL_STORAGE,
38 | Manifest.permission.CAMERA)
39 | .subscribe(new PermissionMustCallback() {
40 | @Override
41 | protected void permissionCallback(boolean isGranted) {
42 | CameraActivity.lanuch(MainActivity.this);
43 | }
44 | });
45 | break;
46 | case R.id.bt2:
47 | permissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE,
48 | Manifest.permission.READ_EXTERNAL_STORAGE,
49 | Manifest.permission.CAMERA)
50 | .subscribe(new PermissionMustCallback() {
51 | @Override
52 | protected void permissionCallback(boolean isGranted) {
53 | H264Activity.lanuch(MainActivity.this);
54 | }
55 | });
56 | break;
57 | case R.id.bt3:
58 | TestActivity.lanuch(this);
59 | break;
60 | case R.id.bt4:
61 | permissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE,
62 | Manifest.permission.READ_EXTERNAL_STORAGE,
63 | Manifest.permission.RECORD_AUDIO)
64 | .subscribe(new PermissionMustCallback() {
65 | @Override
66 | protected void permissionCallback(boolean isGranted) {
67 | AacActivity.lanuch(MainActivity.this);
68 | }
69 | });
70 | break;
71 | case R.id.bt5:
72 | permissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE,
73 | Manifest.permission.READ_EXTERNAL_STORAGE,
74 | Manifest.permission.RECORD_AUDIO,
75 | Manifest.permission.CAMERA)
76 | .subscribe(new PermissionMustCallback() {
77 | @Override
78 | protected void permissionCallback(boolean isGranted) {
79 | Mp4Activity.lanuch(MainActivity.this);
80 | }
81 | });
82 | break;
83 | case R.id.bt6:
84 | permissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE,
85 | Manifest.permission.READ_EXTERNAL_STORAGE,
86 | Manifest.permission.CAMERA)
87 | .subscribe(new PermissionMustCallback() {
88 | @Override
89 | protected void permissionCallback(boolean isGranted) {
90 | ScanActivity.lanuch(MainActivity.this);
91 | }
92 | });
93 | break;
94 | case R.id.bt7:
95 | ViewTestActivity.lanuch(this);
96 | break;
97 | case R.id.bt8:
98 | permissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE,
99 | Manifest.permission.READ_EXTERNAL_STORAGE,
100 | Manifest.permission.RECORD_AUDIO)
101 | .subscribe(new PermissionMustCallback() {
102 | @Override
103 | protected void permissionCallback(boolean isGranted) {
104 |
105 | }
106 | });
107 | break;
108 | }
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/ui/ScanActivity.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.ui;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Matrix;
6 | import android.graphics.Point;
7 | import android.graphics.SurfaceTexture;
8 | import android.os.Bundle;
9 | import android.view.TextureView;
10 | import android.widget.Toast;
11 |
12 | import com.google.zxing.BarcodeFormat;
13 | import com.google.zxing.Result;
14 | import com.google.zxing.ResultPoint;
15 | import com.google.zxing.ResultPointCallback;
16 |
17 | import java.util.EnumSet;
18 |
19 | import androidx.appcompat.app.AppCompatActivity;
20 | import you.chen.media.R;
21 | import you.chen.media.camera.CameraHelper;
22 | import you.chen.media.camera.CameraUtils;
23 | import you.chen.media.camera.SizeFilter;
24 | import you.chen.media.camera.impl.PictureSizeFilter;
25 | import you.chen.media.core.Constant;
26 | import you.chen.media.core.Orientation;
27 | import you.chen.media.core.Transform;
28 | import you.chen.media.core.h264.AvcTransform;
29 | import you.chen.media.core.h264.ClipAvcTransform;
30 | import you.chen.media.core.scan.DecoderHandler;
31 | import you.chen.media.core.scan.FormatDecoder;
32 | import you.chen.media.utils.LogUtils;
33 | import you.chen.media.widget.CameraView;
34 |
35 | /**
36 | * Created by you on 2018-04-09.
37 | */
38 | public class ScanActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener, DecoderHandler.DecoderCallback {
39 |
40 | CameraView cv_camera;
41 |
42 | CameraHelper helper;
43 | //camera最佳筛选
44 | SizeFilter filter;
45 | //预览的缩放相关参数
46 | Matrix matrix;
47 | //扫描解析处理
48 | DecoderHandler handler;
49 |
50 | public static void lanuch(Context context) {
51 | context.startActivity(new Intent(context, ScanActivity.class));
52 | }
53 |
54 | @Override
55 | protected void onCreate(Bundle savedInstanceState) {
56 | super.onCreate(savedInstanceState);
57 | setContentView(R.layout.act_scan);
58 | helper = new CameraHelper();
59 | filter = new PictureSizeFilter();
60 |
61 | helper.setPreviewCallback((data, camera) -> {
62 | if (handler != null) {
63 | handler.push(data);
64 | }
65 | });
66 |
67 | initView();
68 | }
69 |
70 | private void initView() {
71 | cv_camera = findViewById(R.id.cv_camera);
72 |
73 | cv_camera.setOnCameraGestureListener(new CameraView.OnCameraGestureListener() {
74 | @Override
75 | public void onHandleZoom(float zoomScale) {
76 | helper.handleZoom(zoomScale);
77 | }
78 |
79 | @Override
80 | public void onHandleFocus(float x, float y, int w, int h) {
81 | helper.handleFocus(CameraUtils.reverseRotate(x, y, w, h, matrix));
82 | }
83 | });
84 | }
85 |
86 | @Override
87 | protected void onResume() {
88 | super.onResume();
89 | if (cv_camera.isAvailable()) {
90 | startCamera();
91 | } else {
92 | cv_camera.setSurfaceTextureListener(this);
93 | }
94 | }
95 |
96 | @Override
97 | protected void onPause() {
98 | stopCamera();
99 | super.onPause();
100 | }
101 |
102 | @Override
103 | public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
104 | startCamera();
105 | }
106 |
107 | @Override
108 | public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
109 | }
110 |
111 | @Override
112 | public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
113 | return true;
114 | }
115 |
116 | @Override
117 | public void onSurfaceTextureUpdated(SurfaceTexture texture) {
118 | }
119 |
120 | @Override
121 | public void handleResult(Result result) {
122 | if (isFinishing()) return;
123 | LogUtils.i("handlerResult " + result.getText());
124 | Toast.makeText(this, result.getText(), Toast.LENGTH_LONG).show();
125 | }
126 |
127 | private void startCamera() {
128 | matrix = helper.openScanCamera(cv_camera.getSurfaceTexture(), cv_camera.getWidth(), cv_camera.getHeight(),
129 | filter, Constant.SCAN_MIN_FPS, Constant.SCAN_MAX_FPS);
130 |
131 | if (matrix != null) {
132 | cv_camera.setTransform(matrix);
133 | }
134 | cv_camera.setMaxScale(helper.getMaxZoomScale());
135 |
136 | int w = helper.getPreSize().width;
137 | int h = helper.getPreSize().height;
138 | Point matrixSize = CameraUtils.matrixSize(w, h, matrix);
139 |
140 | Transform transform = matrixSize.equals(w, h) ?
141 | new AvcTransform(w, h, 0, Orientation.ROTATE90)
142 | : new ClipAvcTransform(matrixSize.x, matrixSize.y, w, h, 0, Orientation.ROTATE90);
143 | //Camera旋转90, 270时, w, h调换
144 | handler = new DecoderHandler(matrixSize.y, matrixSize.x, new FormatDecoder(EnumSet.of(BarcodeFormat.QR_CODE), null, new ResultPointCallback() {
145 | @Override
146 | public void foundPossibleResultPoint(ResultPoint point) {
147 | if (point != null) {
148 | LogUtils.i("point " + point.getX() + " " + point.getY() + " " + point.hashCode());
149 | } else {
150 | LogUtils.i("point null");
151 | }
152 | }
153 | }), transform, false, this);
154 | }
155 |
156 | //释放相机
157 | private void stopCamera() {
158 | helper.closeCamera();
159 | if (handler != null) {
160 | handler.stop();
161 | handler = null;
162 | }
163 | matrix = null;
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/ui/TestActivity.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.ui;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | import androidx.appcompat.app.AppCompatActivity;
9 | import you.chen.media.R;
10 | import you.chen.media.camera.OrientationHelper;
11 | import you.chen.media.core.YuvUtils;
12 | import you.chen.media.utils.LogUtils;
13 | import you.chen.media.utils.Utils;
14 |
15 | /**
16 | * Created by you on 2018-01-08.
17 | */
18 | public class TestActivity extends AppCompatActivity implements View.OnClickListener {
19 |
20 | int width = 12;
21 | int height = 10;
22 | int ySize = width * height;
23 | int size = ySize * 3 / 2;
24 | byte[] src = new byte[size];
25 |
26 | int clipWidth = 8;
27 | int clipHeight = 4;
28 |
29 | int top = 2;
30 | int left = 2;
31 |
32 | //方向传感
33 | OrientationHelper orientationHelper;
34 |
35 | public static void lanuch(Context context) {
36 | context.startActivity(new Intent(context, TestActivity.class));
37 | }
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.act_test);
43 |
44 | findViewById(R.id.bt1).setOnClickListener(this);
45 | findViewById(R.id.bt2).setOnClickListener(this);
46 | findViewById(R.id.bt3).setOnClickListener(this);
47 | findViewById(R.id.bt4).setOnClickListener(this);
48 | findViewById(R.id.bt5).setOnClickListener(this);
49 | findViewById(R.id.bt6).setOnClickListener(this);
50 | findViewById(R.id.bt7).setOnClickListener(this);
51 | findViewById(R.id.bt8).setOnClickListener(this);
52 |
53 | orientationHelper = new OrientationHelper(Utils.context());
54 |
55 | for (byte i = 0; i < ySize; i++) {//正数代表Y数据
56 | src[i] = i;
57 | }
58 | for (int i = ySize; i < size; i++) {//负数代表UV数据
59 | src[i] = (byte) -(i - ySize);
60 | }
61 |
62 | printfBuf(src);
63 | }
64 |
65 | @Override
66 | protected void onResume() {
67 | super.onResume();
68 | orientationHelper.enable();
69 | }
70 |
71 | @Override
72 | protected void onPause() {
73 | super.onPause();
74 | orientationHelper.disable();
75 | }
76 |
77 | /**
78 | * 测试时只需要旋转手机即可传入不同的方向参数
79 | * @param view
80 | */
81 | @Override
82 | public void onClick(View view) {
83 | byte[] buff = null;
84 | switch (view.getId()) {
85 | case R.id.bt1:
86 | buff = new byte[width * height * 3 / 2];
87 | YuvUtils.nv21ToI420Rotate(src, buff, width, height, orientationHelper.getOrientation());
88 | break;
89 | case R.id.bt2:
90 | buff = new byte[clipWidth * clipHeight * 3 / 2];
91 | YuvUtils.clipNv21ToI420Rotate(src, buff, width, height, clipWidth, clipHeight, left, top, orientationHelper.getOrientation());
92 | break;
93 | case R.id.bt3:
94 | buff = new byte[width * height * 3 / 2];
95 | YuvUtils.nv21ToNV12Rotate(src, buff, width, height, orientationHelper.getOrientation());
96 | break;
97 | case R.id.bt4:
98 | buff = new byte[clipWidth * clipHeight * 3 / 2];
99 | YuvUtils.clipNv21ToNV12Rotate(src, buff, width, height, clipWidth, clipHeight, left, top, orientationHelper.getOrientation());
100 | break;
101 | case R.id.bt5:
102 | buff = new byte[width * height * 3 / 2];
103 | YuvUtils.nv21ToYV12Rotate(src, buff, width, height, orientationHelper.getOrientation());
104 | break;
105 | case R.id.bt6:
106 | buff = new byte[clipWidth * clipHeight * 3 / 2];
107 | YuvUtils.clipNv21ToYV12Rotate(src, buff, width, height, clipWidth, clipHeight, left, top, orientationHelper.getOrientation());
108 | break;
109 | case R.id.bt7:
110 | buff = new byte[width * height * 3 / 2];
111 | YuvUtils.nv21Rotate(src, buff, width, height, orientationHelper.getOrientation());
112 | break;
113 | case R.id.bt8:
114 | buff = new byte[clipWidth * clipHeight * 3 / 2];
115 | YuvUtils.clipNv21Rotate(src, buff, width, height, clipWidth, clipHeight, left, top, orientationHelper.getOrientation());
116 | break;
117 | }
118 | printfBuf(buff);
119 | }
120 |
121 | private void printfBuf(byte[] buff) {
122 | int ySize = buff.length * 2 / 3;
123 | StringBuilder sb = new StringBuilder("YData:");
124 | for (int i = 0; i < ySize; i++) {
125 | sb.append(buff[i]).append(" ");
126 | }
127 | sb.append('\n').append("UVdata:");
128 | for (int i = ySize; i < buff.length; i++) {
129 | sb.append(buff[i]).append(" ");
130 | }
131 | LogUtils.i(sb.toString());
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/ui/ViewTestActivity.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.ui;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.TextureView;
7 |
8 | import androidx.annotation.Nullable;
9 | import androidx.appcompat.app.AppCompatActivity;
10 | import you.chen.media.R;
11 |
12 | /**
13 | * Created by you on 2018-04-27.
14 | */
15 | public class ViewTestActivity extends AppCompatActivity {
16 |
17 | private TextureView tv_test;
18 |
19 | public static void lanuch(Context context) {
20 | context.startActivity(new Intent(context, ViewTestActivity.class));
21 | }
22 |
23 | @Override
24 | protected void onCreate(@Nullable Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.act_viewtest);
27 | tv_test = findViewById(R.id.tv_test);
28 | }
29 |
30 |
31 |
32 |
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/utils/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.utils;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.ImageFormat;
6 | import android.graphics.Rect;
7 | import android.graphics.YuvImage;
8 |
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.File;
11 | import java.io.FileOutputStream;
12 | import java.io.IOException;
13 |
14 | public final class BitmapUtils {
15 |
16 | private BitmapUtils() {}
17 |
18 | public static boolean saveBitmap(Bitmap bitmap, File file) {
19 | return saveBitmap(bitmap, file, false);
20 | }
21 |
22 | public static boolean saveBitmap(Bitmap bmp, File file, boolean recycle) {
23 | FileOutputStream fos = null;
24 | try {
25 | fos = new FileOutputStream(file);
26 | bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
27 | fos.flush();
28 | return true;
29 | }catch (IOException e) {
30 | LogUtils.e(e);
31 | } finally {
32 | if (recycle) {
33 | bmp.recycle();
34 | }
35 | FileUtils.closeCloseable(fos);
36 | }
37 | return false;
38 | }
39 |
40 | //NV21转bitmap
41 | public static Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
42 | Bitmap bitmap = null;
43 | try {
44 | YuvImage image = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
45 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
46 | image.compressToJpeg(new Rect(0, 0, width, height), 100, stream);
47 | bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
48 | stream.close();
49 | } catch (IOException e) {
50 | LogUtils.e(e);
51 | }
52 | return bitmap;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.utils;
2 |
3 | import android.os.Environment;
4 |
5 | import java.io.Closeable;
6 | import java.io.File;
7 | import java.io.IOException;
8 |
9 |
10 | /**
11 | * Created by you on 2016/12/2.
12 | */
13 |
14 | public final class FileUtils {
15 |
16 | private FileUtils() {
17 | }
18 |
19 | /**
20 | * 缓存文件根目录名
21 | */
22 | private static final String FILE_DIR = "youxiaochen";
23 |
24 | /**
25 | * SD卡是否存在
26 | */
27 | public static boolean isSDCardExist() {
28 | return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
29 | }
30 |
31 | /**
32 | * 获取缓存目录路径
33 | *
34 | * @return
35 | */
36 | public static String getCacheDirPath() {
37 | if (isSDCardExist()) {
38 | String path = Environment.getExternalStorageDirectory() + File.separator + FILE_DIR + File.separator;
39 | File directory = new File(path);
40 | if (!directory.exists()) directory.mkdirs();
41 | return path;
42 | } else {
43 | File directory = new File(Utils.context().getCacheDir(), FileUtils.FILE_DIR);
44 | if (!directory.exists()) directory.mkdirs();
45 | return directory.getAbsolutePath();
46 | }
47 | }
48 |
49 | /**
50 | * 获取缓存目录
51 | *
52 | * @return
53 | */
54 | public static File getCacheDir() {
55 | if (isSDCardExist()) {
56 | String path = Environment.getExternalStorageDirectory() + File.separator + FILE_DIR + File.separator;
57 | File directory = new File(path);
58 | if (!directory.exists()) directory.mkdirs();
59 | return directory;
60 | } else {
61 | File directory = new File(Utils.context().getCacheDir(), FileUtils.FILE_DIR);
62 | if (!directory.exists()) directory.mkdirs();
63 | return directory;
64 | }
65 | }
66 |
67 | /**
68 | * 关闭资源
69 | *
70 | * @param closees
71 | */
72 | public static void closeCloseable(Closeable...closees) {
73 | for (Closeable close : closees) {
74 | if (close != null) {
75 | try {
76 | close.close();
77 | } catch (IOException e) {
78 | e.printStackTrace();
79 | }
80 | }
81 | }
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/utils/LogUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.utils;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.PrintWriter;
6 | import java.io.StringWriter;
7 | import java.io.Writer;
8 | import java.util.Locale;
9 |
10 | public final class LogUtils {
11 |
12 | private static final String TAG = "youxiaochen";
13 |
14 | private LogUtils() {}
15 |
16 | public static void i(String msg) {
17 | Log.i(TAG, msg);
18 | }
19 |
20 | public static void i(String tag, String msg) {
21 | Log.i(tag, msg);
22 | }
23 |
24 | public static void i(String format, Object ...args) {
25 | i(TAG, String.format(format, args));
26 | }
27 |
28 | public static void i(String tag, String format, Object ...args) {
29 | i(tag, String.format(format, args));
30 | }
31 |
32 | public static void e(Throwable throwable) {
33 | Log.e(TAG, throwableToString(throwable));
34 | }
35 |
36 | /**
37 | * 打印异常信息到log中
38 | * @param throwable
39 | * @return
40 | */
41 | private static String throwableToString(Throwable throwable) {
42 | if (throwable == null) {
43 | return "throwable is null";
44 | }
45 | Writer info = new StringWriter();
46 | PrintWriter printWriter = new PrintWriter(info);
47 | throwable.printStackTrace(printWriter);
48 | return info.toString();
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.utils;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | /**
8 | * Created by you on 2017-02-20.
9 | * for context
10 | */
11 | public final class Utils {
12 |
13 | private static Context context;
14 |
15 | public static void init(@NonNull Context context) {
16 | if (Utils.context != null) return;
17 | Utils.context = context.getApplicationContext();
18 | }
19 |
20 | public static Context context() {
21 | if (context == null) {
22 | throw new NullPointerException("MediaUtils context must be init");
23 | }
24 | return context;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/utils/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.utils;
2 |
3 | /**
4 | * Created by you on 2018/1/15.
5 | */
6 |
7 | public final class ViewUtils {
8 |
9 | private ViewUtils() {}
10 |
11 | /**
12 | * dp 转 px
13 | * @param dpValue
14 | * @return
15 | */
16 | public static int dp2px(float dpValue) {
17 | final float scale = Utils.context().getResources().getDisplayMetrics().density;
18 | return (int) (dpValue * scale + 0.5f);
19 | }
20 |
21 | /**
22 | * px 转 dp
23 | * @param pxValue
24 | * @return
25 | */
26 | public static int px2dp(float pxValue) {
27 | final float scale = Utils.context().getResources().getDisplayMetrics().density;
28 | return (int) (pxValue / scale + 0.5f);
29 | }
30 |
31 | /**
32 | * sp 转 px
33 | * @param spValue
34 | * @return
35 | */
36 | public static int sp2px(float spValue) {
37 | final float fontScale = Utils.context().getResources().getDisplayMetrics().scaledDensity;
38 | return (int) (spValue * fontScale + 0.5f);
39 | }
40 |
41 | /**
42 | * px 转 sp
43 | * @param pxValue
44 | * @return
45 | */
46 | public static int px2sp(float pxValue) {
47 | final float fontScale = Utils.context().getResources().getDisplayMetrics().scaledDensity;
48 | return (int) (pxValue / fontScale + 0.5f);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/widget/CameraView.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.GestureDetector;
6 | import android.view.MotionEvent;
7 | import android.view.ScaleGestureDetector;
8 | import android.view.TextureView;
9 |
10 | /**
11 | * Created by you on 2018-03-23.
12 | */
13 | public class CameraView extends TextureView {
14 |
15 | private GestureDetector gestureDetector;
16 | //手势监听
17 | private OnCameraGestureListener listener;
18 |
19 | private float currentScale = 1.0f;
20 |
21 | private float maxScale = Float.MAX_VALUE;
22 |
23 | public CameraView(Context context) {
24 | super(context);
25 | init(context);
26 | }
27 |
28 | public CameraView(Context context, AttributeSet attrs) {
29 | super(context, attrs);
30 | init(context);
31 | }
32 |
33 | public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
34 | super(context, attrs, defStyleAttr);
35 | init(context);
36 | }
37 |
38 | private void init(Context context) {
39 | gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
40 | @Override
41 | public boolean onDown(MotionEvent e) {
42 | return true;
43 | }
44 |
45 | @Override
46 | public boolean onSingleTapConfirmed(MotionEvent e) {
47 | if (listener != null) {
48 | listener.onHandleFocus(e.getX(), e.getY(), getWidth(), getHeight());
49 | }
50 | return super.onSingleTapConfirmed(e);
51 | }
52 | });
53 |
54 | scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
55 | @Override
56 | public boolean onScale(ScaleGestureDetector detector) {
57 | currentScale *= detector.getScaleFactor();
58 | if (currentScale < 1.f) currentScale = 1.f;
59 | if (currentScale > maxScale) currentScale = maxScale;
60 | if (listener != null) {
61 | listener.onHandleZoom(currentScale);
62 | }
63 | return true;
64 | }
65 | });
66 | }
67 |
68 | private ScaleGestureDetector scaleGestureDetector;
69 |
70 | @Override
71 | public boolean onTouchEvent(MotionEvent event) {
72 | boolean res = scaleGestureDetector.onTouchEvent(event);
73 | if (!scaleGestureDetector.isInProgress()) {
74 | return gestureDetector.onTouchEvent(event);
75 | }
76 | return res;
77 | }
78 |
79 | public void setOnCameraGestureListener(OnCameraGestureListener listener) {
80 | this.listener = listener;
81 | }
82 |
83 | /**
84 | * 设置支持的最大比例
85 | * @param maxScale
86 | */
87 | public final void setMaxScale(float maxScale) {
88 | this.maxScale = maxScale;
89 | }
90 |
91 | /**
92 | * 相机手势操作, 点击聚集与缩放预览
93 | */
94 | public interface OnCameraGestureListener {
95 | /**
96 | * 缩放手势
97 | * @param zoomScale 放大比例
98 | */
99 | void onHandleZoom(float zoomScale);
100 |
101 | /**
102 | * 聚集坐标
103 | * @param x
104 | * @param y
105 | * @param w
106 | * @param h
107 | */
108 | void onHandleFocus(float x, float y, int w, int h);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/widget/FlashView.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.widget;
2 |
3 | import android.content.Context;
4 | import android.hardware.Camera;
5 | import android.util.AttributeSet;
6 | import android.view.Gravity;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.widget.ImageView;
10 | import android.widget.LinearLayout;
11 | import android.widget.RadioGroup;
12 |
13 | import java.util.List;
14 |
15 | import androidx.annotation.Nullable;
16 | import you.chen.media.R;
17 | import you.chen.media.utils.ViewUtils;
18 |
19 | /**
20 | * Created by you on 2018-03-26.
21 | */
22 | public class FlashView extends LinearLayout implements
23 | RadioGroup.OnCheckedChangeListener, ToggleRadioButton.OnUnToggleListener {
24 |
25 |
26 | private ImageView iv_flash;
27 |
28 | private RadioGroup rg_flash;
29 |
30 | private ToggleRadioButton rb_close, rb_open, rb_auto, rb_light;
31 |
32 | public FlashView(Context context) {
33 | super(context);
34 | init(context);
35 | }
36 |
37 | public FlashView(Context context, @Nullable AttributeSet attrs) {
38 | super(context, attrs);
39 | init(context);
40 | }
41 |
42 | public FlashView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
43 | super(context, attrs, defStyleAttr);
44 | init(context);
45 | }
46 |
47 | private void init(Context context) {
48 | this.setOrientation(HORIZONTAL);
49 | this.setGravity(Gravity.CENTER_VERTICAL);
50 |
51 | iv_flash = new ImageView(context);
52 | int padding = ViewUtils.dp2px(13);
53 | iv_flash.setPadding(padding, 0, padding, 0);
54 | iv_flash.setImageResource(R.drawable.flash_camera);
55 | iv_flash.setOnClickListener(v -> rg_flash.setVisibility(View.VISIBLE));
56 | this.addView(iv_flash, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
57 |
58 | LayoutInflater.from(context).inflate(R.layout.include_flash, this);
59 | rg_flash = findViewById(R.id.rg_flash);
60 | rb_close = rg_flash.findViewById(R.id.rb_close);
61 | rb_open = rg_flash.findViewById(R.id.rb_open);
62 | rb_auto = rg_flash.findViewById(R.id.rb_auto);
63 | rb_light = rg_flash.findViewById(R.id.rb_light);
64 |
65 | rg_flash.setOnCheckedChangeListener(this);
66 | rb_close.setOnUnToggleListener(this);
67 | rb_open.setOnUnToggleListener(this);
68 | rb_auto.setOnUnToggleListener(this);
69 | rb_light.setOnUnToggleListener(this);
70 | }
71 |
72 | @Override
73 | public void onCheckedChanged(RadioGroup group, int checkedId) {
74 | rg_flash.setVisibility(View.INVISIBLE);
75 | switch (checkedId) {
76 | case R.id.rb_close:
77 | iv_flash.setSelected(false);
78 | if (listener != null) {
79 | listener.onFlashChanged(Camera.Parameters.FLASH_MODE_OFF);
80 | }
81 | break;
82 | case R.id.rb_open:
83 | iv_flash.setSelected(true);
84 | if (listener != null) {
85 | listener.onFlashChanged(Camera.Parameters.FLASH_MODE_ON);
86 | }
87 | break;
88 | case R.id.rb_auto:
89 | iv_flash.setSelected(false);
90 | if (listener != null) {
91 | listener.onFlashChanged(Camera.Parameters.FLASH_MODE_AUTO);
92 | }
93 | break;
94 | case R.id.rb_light:
95 | iv_flash.setSelected(true);
96 | if (listener != null) {
97 | listener.onFlashChanged(Camera.Parameters.FLASH_MODE_TORCH);
98 | }
99 | break;
100 | }
101 |
102 | }
103 |
104 | @Override
105 | public void onUnToggle(ToggleRadioButton button) {
106 | rg_flash.setVisibility(View.INVISIBLE);
107 | }
108 |
109 | public void setFlashModes(List flashModes, boolean isFront) {
110 | if (isFront || flashModes == null || flashModes.size() < 2) {
111 | this.setVisibility(View.GONE);
112 | return;
113 | }
114 | //一定会有关闭功能
115 | this.setVisibility(View.VISIBLE);
116 | iv_flash.setSelected(false);
117 | rb_close.setChecked(true);
118 | rb_open.setVisibility(flashModes.contains(Camera.Parameters.FLASH_MODE_ON) ? View.VISIBLE : View.GONE);
119 | rb_auto.setVisibility(flashModes.contains(Camera.Parameters.FLASH_MODE_AUTO) ? View.VISIBLE : View.GONE);
120 | rb_light.setVisibility(flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH) ? View.VISIBLE : View.GONE);
121 | }
122 |
123 | private OnFlashChangedListener listener;
124 |
125 | public void setOnFlashChangedListener(OnFlashChangedListener listener) {
126 | this.listener = listener;
127 | }
128 |
129 | public interface OnFlashChangedListener {
130 | void onFlashChanged(String model);
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/widget/FocusView.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.view.animation.AlphaAnimation;
7 | import android.view.animation.Animation;
8 | import android.view.animation.AnimationSet;
9 | import android.view.animation.ScaleAnimation;
10 | import android.widget.FrameLayout;
11 | import android.widget.ImageView;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.annotation.Nullable;
15 | import you.chen.media.R;
16 |
17 | /**
18 | * Created by you on 2018-03-26.
19 | */
20 | public class FocusView extends FrameLayout {
21 |
22 | private static final int DEF_SIZE = 200;
23 |
24 | private ImageView iv_focus;
25 |
26 | private Animation animation;
27 |
28 | public FocusView(@NonNull Context context) {
29 | super(context);
30 | init(context);
31 | }
32 |
33 | public FocusView(@NonNull Context context, @Nullable AttributeSet attrs) {
34 | super(context, attrs);
35 | init(context);
36 | }
37 |
38 | public FocusView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
39 | super(context, attrs, defStyleAttr);
40 | init(context);
41 | }
42 |
43 | private void init(Context context) {
44 | iv_focus = new ImageView(context);
45 | iv_focus.setScaleType(ImageView.ScaleType.FIT_XY);
46 | iv_focus.setImageResource(R.drawable.focus_camera);
47 | iv_focus.setVisibility(View.INVISIBLE);
48 | LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
49 | addView(iv_focus, params);
50 | }
51 |
52 | @Override
53 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
54 | int widthModel = MeasureSpec.getMode(widthMeasureSpec);
55 | int size;//宽高一样
56 | if (widthModel == MeasureSpec.EXACTLY) {
57 | size = MeasureSpec.getSize(widthMeasureSpec);
58 | } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
59 | size = MeasureSpec.getSize(heightMeasureSpec);
60 | } else {
61 | size = DEF_SIZE;
62 | }
63 | int sizeSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
64 | super.onMeasure(sizeSpec, sizeSpec);
65 | }
66 |
67 | @Override
68 | protected void onDetachedFromWindow() {
69 | iv_focus.clearAnimation();
70 | super.onDetachedFromWindow();
71 | }
72 |
73 | /**
74 | * 设置当前聚焦中心坐标点
75 | * @param x
76 | * @param y
77 | */
78 | public final void setCenter(float x, float y) {
79 | View parent = (View) getParent();
80 | int left = parent.getPaddingLeft();
81 | int right = parent.getWidth() - parent.getPaddingRight() - getWidth();
82 | int top = parent.getPaddingTop();
83 | int bottom = parent.getHeight() - parent.getPaddingBottom() - getHeight();
84 |
85 | x = x - getWidth() / 2.0f + parent.getPaddingLeft();
86 | y = y - getHeight() / 2.0f + parent.getPaddingTop();
87 | //不能超过边缘
88 | x = clamp(x, left, right);
89 | y = clamp(y, top, bottom);
90 | setX(x);
91 | setY(y);
92 |
93 | iv_focus.clearAnimation();
94 | if (animation == null) {
95 | animation = initAnim();
96 | }
97 | iv_focus.startAnimation(animation);
98 | }
99 |
100 | /**
101 | * x值不能超出min~max范围
102 | */
103 | private float clamp(float x, int min, int max) {
104 | if (x > max) return max;
105 | if (x < min) return min;
106 | return x;
107 | }
108 |
109 | private Animation initAnim() {
110 | AnimationSet animation = new AnimationSet(false);
111 |
112 | ScaleAnimation scaleAnim = new ScaleAnimation(1.f, 0.5f, 1.f, 0.5f,
113 | iv_focus.getWidth() / 2f, iv_focus.getHeight() / 2f);
114 | scaleAnim.setDuration(300);
115 | scaleAnim.setFillAfter(true);
116 | AlphaAnimation alphaAnim = new AlphaAnimation(1.f, 0.75f);
117 | alphaAnim.setDuration(700);
118 |
119 | animation.addAnimation(scaleAnim);
120 | animation.addAnimation(alphaAnim);
121 |
122 | animation.setAnimationListener(new Animation.AnimationListener() {
123 | @Override
124 | public void onAnimationStart(Animation animation) {
125 | iv_focus.setVisibility(View.VISIBLE);
126 | }
127 |
128 | @Override
129 | public void onAnimationEnd(Animation animation) {
130 | iv_focus.setVisibility(View.INVISIBLE);
131 | }
132 |
133 | @Override
134 | public void onAnimationRepeat(Animation animation) {
135 | }
136 | });
137 | return animation;
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/widget/ScanFormatView.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Rect;
6 | import android.graphics.drawable.Drawable;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import you.chen.media.R;
13 | import you.chen.media.utils.LogUtils;
14 | import you.chen.media.utils.ViewUtils;
15 |
16 | /**
17 | * Created by you on 2018-04-27.
18 | * 扫码控件
19 | */
20 | public class ScanFormatView extends View {
21 |
22 | private static final int DEF_SIZE = ViewUtils.dp2px(100);
23 | //刷新界面的时间
24 | private static final long ANIMATION_DELAY = 40L;
25 | //动画移动间距
26 | private static final int ANIMATION_SIZE = ViewUtils.dp2px(5);
27 |
28 | private int lineHeight = ViewUtils.dp2px(2);
29 |
30 | private Drawable lineDrawable;
31 |
32 | private Rect rect;
33 |
34 | public ScanFormatView(@NonNull Context context) {
35 | super(context);
36 | init(context);
37 | }
38 |
39 | public ScanFormatView(@NonNull Context context, @Nullable AttributeSet attrs) {
40 | super(context, attrs);
41 | init(context);
42 | }
43 |
44 | public ScanFormatView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
45 | super(context, attrs, defStyleAttr);
46 | init(context);
47 | }
48 |
49 | private void init(Context context) {
50 | lineDrawable = context.getResources().getDrawable(R.drawable.qrcode_scan_line);
51 | rect = new Rect();
52 | }
53 |
54 | @Override
55 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
56 | int widthModel = MeasureSpec.getMode(widthMeasureSpec);
57 | int size;//宽高一样
58 | if (widthModel == MeasureSpec.EXACTLY) {
59 | size = MeasureSpec.getSize(widthMeasureSpec);
60 | } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
61 | size = MeasureSpec.getSize(heightMeasureSpec);
62 | } else {
63 | size = DEF_SIZE;
64 | }
65 | int sizeSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
66 | super.onMeasure(sizeSpec, sizeSpec);
67 | }
68 |
69 | @Override
70 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
71 | super.onLayout(changed, left, top, right, bottom);
72 | rect.set(0, 0, getWidth(), lineHeight);
73 | }
74 |
75 | @Override
76 | protected void onDraw(Canvas canvas) {
77 | lineDrawable.setBounds(rect);
78 | lineDrawable.draw(canvas);
79 | rect.offset(0, ANIMATION_SIZE);
80 | if (rect.bottom > getHeight()) {
81 | rect.top = 0;
82 | rect.bottom = lineHeight;
83 | }
84 | postInvalidateDelayed(ANIMATION_DELAY);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/you/chen/media/widget/ToggleRadioButton.java:
--------------------------------------------------------------------------------
1 | package you.chen.media.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.RadioButton;
6 |
7 | /**
8 | * Created by you on 2018-03-27.
9 | */
10 | public class ToggleRadioButton extends RadioButton {
11 |
12 | public ToggleRadioButton(Context context) {
13 | super(context);
14 | }
15 |
16 | public ToggleRadioButton(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | public ToggleRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
21 | super(context, attrs, defStyleAttr);
22 | }
23 |
24 | @Override
25 | public void toggle() {
26 | if (!isChecked()) {
27 | super.toggle();
28 | } else {
29 | if (listener != null) {
30 | listener.onUnToggle(this);
31 | }
32 | }
33 | }
34 |
35 | private OnUnToggleListener listener;
36 |
37 | public void setOnUnToggleListener(OnUnToggleListener listener) {
38 | this.listener = listener;
39 | }
40 |
41 | /**
42 | * 已经选中时的点击响应
43 | */
44 | public interface OnUnToggleListener {
45 |
46 | void onUnToggle(ToggleRadioButton button);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/color/flash_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/flash_camera_s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/drawable-xxhdpi/flash_camera_s.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/flash_camera_un.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/drawable-xxhdpi/flash_camera_un.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/focus_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/drawable-xxhdpi/focus_camera.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/line_scan_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/drawable-xxhdpi/line_scan_camera.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/qrcode_scan_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/drawable-xxhdpi/qrcode_scan_line.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/scan_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/drawable-xxhdpi/scan_success.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/switch_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/drawable-xxhdpi/switch_camera.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/flash_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_aac.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
18 |
19 |
20 |
21 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
19 |
20 |
29 |
30 |
34 |
35 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_h264.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
16 |
17 |
25 |
26 |
32 |
33 |
38 |
39 |
40 |
41 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
20 |
21 |
27 |
28 |
34 |
35 |
41 |
42 |
48 |
49 |
55 |
56 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_mp4.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
16 |
17 |
25 |
26 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_pcm.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_scan.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
19 |
20 |
26 |
27 |
33 |
34 |
40 |
41 |
47 |
48 |
54 |
55 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/act_viewtest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/include_flash.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
30 |
31 |
40 |
41 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/beep.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youxiaochen/CameraMedia/2d9ed6882e1e2a9b0dc80126dafb118d9cfa64af/app/src/main/res/raw/beep.ogg
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CameraMedia
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/test/java/you/chen/media/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package you.chen.media;
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() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.2'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name='CameraMedia'
3 |
--------------------------------------------------------------------------------