├── .gitignore ├── ReadMe.md ├── app ├── .gitignore ├── build.gradle ├── libs │ └── weplayer-release.aar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── wtz │ │ └── videomaker │ │ ├── AudioRecordActivity.java │ │ ├── CameraActivity.java │ │ ├── ImageVideoActivity.java │ │ ├── MainActivity.java │ │ ├── MixAudioActivity.java │ │ ├── MultiSurfaceActivity.java │ │ ├── SingleSurfaceActivity.java │ │ ├── VideoPushActivity.java │ │ ├── VideoRecordActivity.java │ │ ├── adapter │ │ ├── BaseRecyclerViewAdapter.java │ │ └── ImageChooserGridAdapter.java │ │ ├── surfaceview │ │ ├── FilterSurfaceView.java │ │ ├── GraySurfaceView.java │ │ ├── LuminanceSurfaceView.java │ │ ├── MultiImgSurfaceView.java │ │ ├── ReverseSurfaceView.java │ │ └── SingleImgSurfaceView.java │ │ ├── utils │ │ ├── AudioUtils.java │ │ ├── DateTimeUtil.java │ │ ├── FileChooser.java │ │ ├── ImageChooser.java │ │ ├── PermissionChecker.java │ │ ├── PermissionHandler.java │ │ └── UriUtil.java │ │ └── views │ │ └── GridItemDecoration.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── carry_up.jpg │ ├── circle_ball_shape.xml │ ├── grid_divider_line_shape.xml │ ├── happy.jpg │ ├── ic_launcher_background.xml │ ├── icon_select.png │ ├── image_default.png │ ├── lotus.jpg │ ├── purple_ball.png │ ├── seekbar_style.xml │ ├── seekbar_thumb.xml │ ├── sunflower.jpg │ └── tree.jpg │ ├── layout │ ├── activity_audio_record.xml │ ├── activity_camera.xml │ ├── activity_image_chooser.xml │ ├── activity_image_video.xml │ ├── activity_main.xml │ ├── activity_mix_audio.xml │ ├── activity_multi_surface.xml │ ├── activity_single_surface.xml │ ├── activity_video_push.xml │ ├── activity_video_record.xml │ ├── item_image_chooser.xml │ └── item_spinner_time_unit.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 │ ├── values-sw1024dp │ └── dimens.xml │ ├── values-sw1280dp │ └── dimens.xml │ ├── values-sw1365dp │ └── dimens.xml │ ├── values-sw384dp │ └── dimens.xml │ ├── values-sw392dp │ └── dimens.xml │ ├── values-sw400dp │ └── dimens.xml │ ├── values-sw410dp │ └── dimens.xml │ ├── values-sw411dp │ └── dimens.xml │ ├── values-sw432dp │ └── dimens.xml │ ├── values-sw480dp │ └── dimens.xml │ ├── values-sw533dp │ └── dimens.xml │ ├── values-sw592dp │ └── dimens.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw640dp │ └── dimens.xml │ ├── values-sw662dp │ └── dimens.xml │ ├── values-sw720dp │ └── dimens.xml │ ├── values-sw768dp │ └── dimens.xml │ ├── values-sw800dp │ └── dimens.xml │ ├── values-sw811dp │ └── dimens.xml │ ├── values-sw820dp │ └── dimens.xml │ ├── values-sw960dp │ └── dimens.xml │ ├── values-sw961dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── images ├── VideoMaker-app-audio-mix.jpg ├── VideoMaker-app-audio-record.jpg ├── VideoMaker-app-camera-1.jpg ├── VideoMaker-app-camera-2.jpg ├── VideoMaker-app-camera-3.jpg ├── VideoMaker-app-image-video.jpg ├── VideoMaker-app-menu.jpg ├── VideoMaker-app-multi-texture-single-surface.jpg ├── VideoMaker-app-single-texture-multi-surface.jpg ├── VideoMaker-app-video-push.jpg ├── VideoMaker-app-video-record-1.jpg └── VideoMaker-app-video-record-2.jpg ├── libmp3util ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ ├── lamemp3 │ │ ├── VbrTag.c │ │ ├── VbrTag.h │ │ ├── bitstream.c │ │ ├── bitstream.h │ │ ├── encoder.c │ │ ├── encoder.h │ │ ├── fft.c │ │ ├── fft.h │ │ ├── gain_analysis.c │ │ ├── gain_analysis.h │ │ ├── id3tag.c │ │ ├── id3tag.h │ │ ├── include │ │ │ └── lame.h │ │ ├── l3side.h │ │ ├── lame-analysis.h │ │ ├── lame.c │ │ ├── lame_global_flags.h │ │ ├── lameerror.h │ │ ├── machine.h │ │ ├── mpglib_interface.c │ │ ├── newmdct.c │ │ ├── newmdct.h │ │ ├── presets.c │ │ ├── psymodel.c │ │ ├── psymodel.h │ │ ├── quantize.c │ │ ├── quantize.h │ │ ├── quantize_pvt.c │ │ ├── quantize_pvt.h │ │ ├── reservoir.c │ │ ├── reservoir.h │ │ ├── set_get.c │ │ ├── set_get.h │ │ ├── tables.c │ │ ├── tables.h │ │ ├── takehiro.c │ │ ├── util.c │ │ ├── util.h │ │ ├── vbrquantize.c │ │ ├── vbrquantize.h │ │ ├── vector │ │ │ ├── lame_intrin.h │ │ │ └── xmm_quantize_sub.c │ │ ├── version.c │ │ └── version.h │ └── wemp3 │ │ ├── JavaListener.cpp │ │ ├── WeMp3Encoder.cpp │ │ ├── WeMp3JNI.cpp │ │ └── include │ │ ├── AndroidLog.h │ │ ├── JavaListener.h │ │ ├── OnEncodeProgressListener.h │ │ └── WeMp3Encoder.h │ ├── java │ └── com │ │ └── wtz │ │ └── libmp3util │ │ ├── WeMp3Encoder.java │ │ └── utlis │ │ └── LogUtils.java │ └── res │ └── values │ └── strings.xml ├── libnaudiorecord ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ └── weaudiorecord │ │ ├── DoubleBuffer.cpp │ │ ├── JavaListener.cpp │ │ ├── WeAudioRecordJNI.cpp │ │ ├── WeAudioRecorder.cpp │ │ └── include │ │ ├── AndroidLog.h │ │ ├── DoubleBuffer.h │ │ ├── JavaListener.h │ │ ├── OnPCMDataCall.h │ │ └── WeAudioRecorder.h │ ├── java │ └── com │ │ └── wtz │ │ └── libnaudiorecord │ │ ├── WeNAudioRecorder.java │ │ └── utlis │ │ └── LogUtils.java │ └── res │ └── values │ └── strings.xml ├── libpushflow ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ ├── librtmp │ │ ├── amf.c │ │ ├── amf.h │ │ ├── bytes.h │ │ ├── dh.h │ │ ├── dhgroups.h │ │ ├── handshake.h │ │ ├── hashswf.c │ │ ├── http.h │ │ ├── log.c │ │ ├── log.h │ │ ├── parseurl.c │ │ ├── rtmp.c │ │ ├── rtmp.h │ │ └── rtmp_sys.h │ └── pushflow │ │ ├── JavaListener.cpp │ │ ├── RtmpPacketQueue.cpp │ │ ├── WePushFlow.cpp │ │ ├── WePushFlowJNI.cpp │ │ └── include │ │ ├── AndroidLog.h │ │ ├── JavaListener.h │ │ ├── OnPushDisconnectCall.h │ │ ├── OnStartPushResultListener.h │ │ ├── RtmpPacketQueue.h │ │ └── WePushFlow.h │ ├── java │ └── com │ │ └── wtz │ │ └── libpushflow │ │ ├── WePushFlow.java │ │ └── utlis │ │ └── LogUtils.java │ └── res │ └── values │ └── strings.xml ├── libvideomaker ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── wtz │ │ └── libvideomaker │ │ ├── camera │ │ ├── AcceleFocusListener.java │ │ ├── WeCamera.java │ │ └── WeCameraView.java │ │ ├── egl │ │ ├── WeEGLHelper.java │ │ ├── WeGLRenderer.java │ │ ├── WeGLSurfaceView.java │ │ ├── WeGLThread.java │ │ ├── WeGLVideoEncoder.java │ │ └── WeGLVideoPushEncoder.java │ │ ├── imagevideo │ │ └── WeImageVideoView.java │ │ ├── push │ │ └── WeVideoPusher.java │ │ ├── recorder │ │ ├── WeJAudioRecorder.java │ │ └── WeVideoRecorder.java │ │ ├── renderer │ │ ├── BaseRender.java │ │ ├── OnScreenRenderer.java │ │ ├── filters │ │ │ ├── FilterRenderer.java │ │ │ ├── GrayFilterRenderer.java │ │ │ ├── LuminanceFilterRenderer.java │ │ │ ├── ReverseFilterRenderer.java │ │ │ └── WatermarkRenderer.java │ │ └── origins │ │ │ ├── CameraRenderer.java │ │ │ ├── ImgRenderer.java │ │ │ ├── MultiImgRenderer.java │ │ │ └── SingleImgRenderer.java │ │ └── utils │ │ ├── ExponentialWaitStrategy.java │ │ ├── GLBitmapUtils.java │ │ ├── HexUtils.java │ │ ├── LogUtils.java │ │ ├── ScreenUtils.java │ │ ├── ShaderUtil.java │ │ ├── TextUtils.java │ │ └── TextureUtils.java │ └── res │ ├── raw │ ├── we_vidmk_fragment_camera_oes_shader.glsl │ ├── we_vidmk_fragment_gray_texture2d_shader.glsl │ ├── we_vidmk_fragment_luminance_texture2d_shader.glsl │ ├── we_vidmk_fragment_normal_texture2d_shader.glsl │ ├── we_vidmk_fragment_reverse_texture2d_shader.glsl │ ├── we_vidmk_vertex_offscreen_shader.glsl │ └── we_vidmk_vertex_onscreen_shader.glsl │ └── values │ ├── dimens.xml │ └── strings.xml ├── screenMatch.properties ├── screenMatch_example_dimens.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | build/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | gradlew 20 | gradlew.bat 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # Android Lint 29 | lint.xml 30 | 31 | # NDK 32 | obj/ 33 | 34 | # Eclipse 35 | .settings/ 36 | 37 | # Android Studio 38 | .idea/ 39 | .navigation/ 40 | .externalNativeBuild/ 41 | captures/ 42 | .cxx/ 43 | 44 | # IntelliJ IDEA 45 | .idea/ 46 | *.iml 47 | *.ipr 48 | *.iws 49 | 50 | # Local configuration file (sdk path, etc) 51 | local.properties 52 | 53 | # Mac system files 54 | .DS_Store 55 | 56 | # Keystore files 57 | *.jks -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # VideoMaker 2 | 3 | Video Making base on OpenGL ES + OpenSL ES + SurfaceTexture + SurfaceView + MediaCodec + MediaMuxer + AudioRecord + lame + rtmpdump + WePlayer. 4 | 5 | 此项目是在学习 CSDN 学院[杨万理](https://edu.csdn.net/lecturer/1846)的视频编码和直播推流课程基础上,加上自己的理解、查阅相关专业理论资料和一步步软件构思设计调试而成的,在此记录学习成果,也同时感谢杨老师的知识传播! 6 | 7 | ## libvideomaker 模块 8 | 9 | 实现视频制作的核心功能库,Java 层提供的主要最外层接口类有: 10 | WeCameraView、WeJAudioRecorder、WeImageVideoView、WeVideoRecorder、WeVideoPusher。 11 | 12 | ### WeCameraView 13 | 14 | 基于 OpenGL ES + SurfaceView + SurfaceTexture 实现的相机预览器。 15 | 16 | - 支持相机基本功能:画面预览、自动对焦、前后摄像头切换、拍照图片保存; 17 | - 支持水印:文字水印、图片水印、图文叠加水印、动态改变水印位置; 18 | - 支持简单滤镜:灰度滤镜、反色滤镜; 19 | - 支持预览画面自适应 SurfaceView 容器大小; 20 | - 支持纹理 TextureId 和 EGLContext 共享,可用于视频录制; 21 | 22 | ### WeJAudioRecorder 23 | 24 | 对 Java 层的 AudioRecord 的封装。 25 | 26 | - 支持基本的录音功能:回调 byte[] 数据; 27 | - 支持获取录音时间、声音分贝数等参数; 28 | - 使用单线程执行功能,避免并发调用导致的问题; 29 | 30 | ### WeImageVideoView 31 | 32 | 基于 OpenGL ES + SurfaceView 实现的图片渲染器。 33 | 34 | - 支持图片播放功能:可动态设置改变图片资源; 35 | - 支持水印:文字水印、图片水印、图文叠加水印、动态改变水印位置; 36 | - 支持纹理 TextureId 和 EGLContext 共享,可用于视频录制; 37 | 38 | ### WeVideoRecorder 39 | 40 | 基于 OpenGL ES + MediaCodec + MediaMuxer 实现的视频录制器。 41 | 42 | - 支持视频录制保存为 mp4,图像使用 h264 编码,音频使用 AAC 编码; 43 | - 图像来源可以是相机或图片渲染的纹理; 44 | - 声音来源可以是录音或本地音乐播放的字节数据; 45 | 46 | ### WeVideoPusher 47 | 48 | 基于 OpenGL ES + MediaCodec + rtmpdump 实现的视频直播推流器。 49 | 50 | - 图像使用 h264 编码,音频使用 AAC 编码,通过 RTMP 协议对音视频数据分别做相应的封装发送; 51 | - 图像来源可以是相机或图片渲染的纹理; 52 | - 声音来源可以是录音或本地音乐播放的字节数据; 53 | - 支持推流url、连接超时、音频相关参数等设置; 54 | - 推流连接失败自动重试,采用指数递增延时策略重试; 55 | 56 | ## libpushflow 模块 57 | 58 | 移植 C 库 rtmpdump 到 Android 平台 + 直播推流接口封装。 59 | Java 层提供的接口类为 WePushFlow。 60 | 61 | - 支持推流url、连接超时、音频相关参数等设置; 62 | - 分别提供 SPS/PPS、图像 H264 数据、音频 AAC 数据推送接口; 63 | 64 | ## libnaudiorecord 模块 65 | 66 | 使用 Native 层 OpenSL ES 实现的录音器。Java 层提供的接口类为 WeNAudioRecorder。 67 | 68 | - 支持基本的录音功能:回调 byte[] 数据; 69 | - 支持获取录音时间、声音分贝数等参数; 70 | - 使用单线程执行功能,避免并发调用导致的问题; 71 | 72 | ## libmp3util 模块 73 | 74 | 集成 C 库 lame 到 Android 平台 + mp3 编码接口封装。 75 | Java 层提供的接口类为 WeMp3Encoder。 76 | 77 | - 支持同一进程多实例操作。 78 | - 支持设置通道数、编码位数、输出质量等参数设置。 79 | - MP3 编码数据来源分两种: 80 | - 来源为 PCM 文件 / WAV 文件。每个文件独立编码,可以多任务、多线程、多文件操作。适用场景:批量文件异步编码。 81 | - 来源为 PCM buffer。从 PCM buffer 取数据编码的方法分成了多步操作,涉及状态切换,适合单任务执行。支持多线程操作。 82 | 83 | ## app 模块 84 | 85 | 主要是对以上各个库模块的使用验证测试,如下: 86 | 87 | ![测试目录](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-menu.jpg) 88 | 89 | - EGL 环境测试:单纹理、多 Surface 渲染; 90 | 91 | ![单纹理、多 Surface](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-single-texture-multi-surface.jpg) 92 | 93 | - EGL 环境测试:多纹理、单 Surface 渲染; 94 | 95 | ![多纹理、单 Surface](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-multi-texture-single-surface.jpg) 96 | 97 | - 音频录制测试:使用 WeJAudioRecorder、WeNAudioRecorder 录音,使用 WeMp3Encoder 保存为 mp3文件,使用 WePlayer 播放录音文件; 98 | 99 | ![音频录制](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-audio-record.jpg) 100 | 101 | - 音频混音测试:使用线性叠加平均混音算法对录音与背景音乐进行混音测试; 102 | 103 | ![音频混音](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-audio-mix.jpg) 104 | 105 | - 摄像头预览测试:使用 WeCameraView 实现画面预览、自动对焦、前后摄像头切换、拍照图片保存、水印、滤镜效果; 106 | 107 | ![camera-1](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-camera-1.jpg) 108 | ![camera-2](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-camera-2.jpg) 109 | ![camera-3](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-camera-3.jpg) 110 | 111 | - 视频录制测试:使用 WeVideoRecorder 实现视频编码保存为 mp4,支持声音来源有:麦克风、音乐、麦克风与音乐混音,图像来自 WeCameraView; 112 | 113 | ![video-record-1](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-video-record-1.jpg) 114 | ![video-record-2](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-video-record-2.jpg) 115 | 116 | - 图像合成视频测试:使用 WeImageVideoView 手动或自动连续加载渲染多张图片,配合音乐播放,使用 WeVideoRecorder 合成为视频; 117 | 118 | ![图像合成视频](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-image-video.jpg) 119 | 120 | - 直播推流测试:与视频录制测试类似,区别在于使用 WeVideoPusher 而不是 WeVideoRecorder 来编码和封装数据并发送到直播服务器; 121 | 122 | ![直播推流](https://github.com/wtz2017/VideoMaker/raw/master/images/VideoMaker-app-video-push.jpg) 123 | 124 | -------------------------------------------------------------------------------- /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 "com.wtz.videomaker" 8 | minSdkVersion 19 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | repositories { 14 | flatDir { 15 | dirs 'libs' 16 | } 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | ndk { 23 | abiFilters 'armeabi-v7a', 'arm64-v8a' 24 | } 25 | } 26 | debug { 27 | ndk { 28 | abiFilters 'armeabi-v7a', 'arm64-v8a' 29 | } 30 | } 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation fileTree(dir: 'libs', include: ['*.jar']) 36 | implementation 'androidx.appcompat:appcompat:1.1.0' 37 | implementation project(path: ':libvideomaker') 38 | implementation project(path: ':libnaudiorecord') 39 | implementation(name:'weplayer-release', ext:'aar') 40 | implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' 41 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 42 | implementation 'com.squareup.picasso:picasso:2.71828' 43 | implementation 'com.github.zcweng:switch-button:0.0.3@aar' 44 | compile project(path: ':libmp3util') 45 | } 46 | -------------------------------------------------------------------------------- /app/libs/weplayer-release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/libs/weplayer-release.aar -------------------------------------------------------------------------------- /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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 30 | 32 | 34 | 37 | 40 | 43 | 46 | 48 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import androidx.appcompat.app.AppCompatActivity; 8 | 9 | import com.wtz.libvideomaker.utils.LogUtils; 10 | 11 | 12 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 13 | private static final String TAG = "app.MainActivity"; 14 | 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | LogUtils.d(TAG, "onCreate"); 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | 22 | findViewById(R.id.btn_multi_surface).setOnClickListener(this); 23 | findViewById(R.id.btn_single_surface).setOnClickListener(this); 24 | findViewById(R.id.btn_audio_record).setOnClickListener(this); 25 | findViewById(R.id.btn_camera).setOnClickListener(this); 26 | findViewById(R.id.btn_video_record).setOnClickListener(this); 27 | findViewById(R.id.btn_image_video).setOnClickListener(this); 28 | findViewById(R.id.btn_mix_audio).setOnClickListener(this); 29 | findViewById(R.id.btn_push_video).setOnClickListener(this); 30 | } 31 | 32 | @Override 33 | public void onClick(View view) { 34 | switch (view.getId()) { 35 | case R.id.btn_multi_surface: 36 | startActivity(new Intent(MainActivity.this, MultiSurfaceActivity.class)); 37 | break; 38 | case R.id.btn_single_surface: 39 | startActivity(new Intent(MainActivity.this, SingleSurfaceActivity.class)); 40 | break; 41 | case R.id.btn_audio_record: 42 | startActivity(new Intent(MainActivity.this, AudioRecordActivity.class)); 43 | break; 44 | case R.id.btn_mix_audio: 45 | startActivity(new Intent(MainActivity.this, MixAudioActivity.class)); 46 | break; 47 | case R.id.btn_camera: 48 | startActivity(new Intent(MainActivity.this, CameraActivity.class)); 49 | break; 50 | case R.id.btn_video_record: 51 | startActivity(new Intent(MainActivity.this, VideoRecordActivity.class)); 52 | break; 53 | case R.id.btn_image_video: 54 | startActivity(new Intent(MainActivity.this, ImageVideoActivity.class)); 55 | break; 56 | case R.id.btn_push_video: 57 | startActivity(new Intent(MainActivity.this, VideoPushActivity.class)); 58 | break; 59 | } 60 | } 61 | 62 | @Override 63 | protected void onDestroy() { 64 | LogUtils.d(TAG, "onDestroy"); 65 | super.onDestroy(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/SingleSurfaceActivity.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import com.wtz.libvideomaker.utils.LogUtils; 8 | import com.wtz.videomaker.surfaceview.MultiImgSurfaceView; 9 | 10 | 11 | public class SingleSurfaceActivity extends AppCompatActivity { 12 | private static final String TAG = "SingleSurfaceActivity"; 13 | 14 | private MultiImgSurfaceView mMainSurfaceView; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | LogUtils.d(TAG, "onCreate"); 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_single_surface); 21 | mMainSurfaceView = findViewById(R.id.main_surface_view); 22 | } 23 | 24 | @Override 25 | protected void onDestroy() { 26 | LogUtils.d(TAG, "onDestroy"); 27 | mMainSurfaceView.clearSourceImage(); 28 | mMainSurfaceView = null; 29 | super.onDestroy(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/adapter/BaseRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.adapter; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter { 9 | 10 | public interface OnRecyclerViewItemClickListener { 11 | 12 | void onItemClick(View view, int position); 13 | 14 | boolean onItemLongClick(View view, int position); 15 | 16 | } 17 | 18 | protected OnRecyclerViewItemClickListener mOnItemClickListener; 19 | 20 | public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) { 21 | this.mOnItemClickListener = listener; 22 | } 23 | 24 | protected void bindItemClickListener(@NonNull final RecyclerView.ViewHolder holder) { 25 | holder.itemView.setOnClickListener(new View.OnClickListener() { 26 | @Override 27 | public void onClick(View v) { 28 | if (mOnItemClickListener != null) { 29 | int pos = holder.getLayoutPosition(); 30 | mOnItemClickListener.onItemClick(holder.itemView, pos); 31 | } 32 | } 33 | }); 34 | holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { 35 | @Override 36 | public boolean onLongClick(View v) { 37 | if (mOnItemClickListener != null) { 38 | int pos = holder.getLayoutPosition(); 39 | return mOnItemClickListener.onItemLongClick(holder.itemView, pos); 40 | } 41 | return false; 42 | } 43 | }); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/surfaceview/FilterSurfaceView.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.surfaceview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | 7 | import com.wtz.libvideomaker.egl.WeGLSurfaceView; 8 | import com.wtz.libvideomaker.egl.WeGLRenderer; 9 | import com.wtz.libvideomaker.renderer.OnScreenRenderer; 10 | import com.wtz.libvideomaker.renderer.filters.FilterRenderer; 11 | 12 | public abstract class FilterSurfaceView extends WeGLSurfaceView 13 | implements WeGLRenderer, FilterRenderer.OnFilterTextureChangedListener { 14 | 15 | private FilterRenderer mFilterRender; 16 | private OnScreenRenderer mOnScreenRenderer; 17 | 18 | public FilterSurfaceView(Context context) { 19 | this(context, null); 20 | } 21 | 22 | public FilterSurfaceView(Context context, AttributeSet attrs) { 23 | this(context, attrs, 0); 24 | } 25 | 26 | public FilterSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 27 | super(context, attrs, defStyleAttr); 28 | setRenderMode(RENDERMODE_WHEN_DIRTY); 29 | 30 | mFilterRender = createFilterRenderer(context); 31 | mFilterRender.setFilterTextureChangedListener(this); 32 | 33 | mOnScreenRenderer = new OnScreenRenderer(context, getExternalLogTag()); 34 | mOnScreenRenderer.setExternalTextureId(mFilterRender.getFilterTextureId()); 35 | } 36 | 37 | protected abstract FilterRenderer createFilterRenderer(Context context); 38 | 39 | public void setExternalTextureId(int id) { 40 | mFilterRender.setExternalTextureId(id); 41 | } 42 | 43 | @Override 44 | protected WeGLRenderer getRenderer() { 45 | return this; 46 | } 47 | 48 | @Override 49 | public void onEGLContextCreated() { 50 | Log.d(getExternalLogTag(), "onEGLContextCreated"); 51 | mFilterRender.onEGLContextCreated(); 52 | mOnScreenRenderer.onEGLContextCreated(); 53 | } 54 | 55 | @Override 56 | public void onSurfaceChanged(int width, int height) { 57 | Log.d(getExternalLogTag(), "onSurfaceChanged " + width + "x" + height); 58 | mFilterRender.onSurfaceChanged(width, height); 59 | mOnScreenRenderer.onSurfaceChanged(width, height); 60 | } 61 | 62 | @Override 63 | public void onFilterTextureChanged(FilterRenderer renderer, int textureID) { 64 | mOnScreenRenderer.setExternalTextureId(textureID); 65 | } 66 | 67 | @Override 68 | public void onDrawFrame() { 69 | Log.d(getExternalLogTag(), "onDrawFrame"); 70 | mFilterRender.onDrawFrame(); 71 | mOnScreenRenderer.onDrawFrame(); 72 | } 73 | 74 | @Override 75 | public void onEGLContextToDestroy() { 76 | Log.d(getExternalLogTag(), "onEGLContextToDestroy"); 77 | mFilterRender.onEGLContextToDestroy(); 78 | mOnScreenRenderer.onEGLContextToDestroy(); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/surfaceview/GraySurfaceView.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.surfaceview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import com.wtz.libvideomaker.renderer.filters.FilterRenderer; 7 | import com.wtz.libvideomaker.renderer.filters.GrayFilterRenderer; 8 | 9 | public class GraySurfaceView extends FilterSurfaceView{ 10 | private static final String TAG = GraySurfaceView.class.getSimpleName(); 11 | 12 | public GraySurfaceView(Context context) { 13 | super(context); 14 | } 15 | 16 | public GraySurfaceView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public GraySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | } 23 | 24 | @Override 25 | protected String getExternalLogTag() { 26 | return TAG; 27 | } 28 | 29 | @Override 30 | protected FilterRenderer createFilterRenderer(Context context) { 31 | return new GrayFilterRenderer(context); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/surfaceview/LuminanceSurfaceView.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.surfaceview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import com.wtz.libvideomaker.renderer.filters.FilterRenderer; 7 | import com.wtz.libvideomaker.renderer.filters.LuminanceFilterRenderer; 8 | 9 | public class LuminanceSurfaceView extends FilterSurfaceView{ 10 | private static final String TAG = LuminanceSurfaceView.class.getSimpleName(); 11 | 12 | public LuminanceSurfaceView(Context context) { 13 | super(context); 14 | } 15 | 16 | public LuminanceSurfaceView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public LuminanceSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | } 23 | 24 | @Override 25 | protected String getExternalLogTag() { 26 | return TAG; 27 | } 28 | 29 | @Override 30 | protected FilterRenderer createFilterRenderer(Context context) { 31 | return new LuminanceFilterRenderer(context); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/surfaceview/MultiImgSurfaceView.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.surfaceview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.util.Log; 6 | 7 | import com.wtz.libvideomaker.egl.WeGLSurfaceView; 8 | import com.wtz.libvideomaker.egl.WeGLRenderer; 9 | import com.wtz.libvideomaker.renderer.OnScreenRenderer; 10 | import com.wtz.libvideomaker.renderer.origins.ImgRenderer; 11 | import com.wtz.libvideomaker.renderer.origins.MultiImgRenderer; 12 | import com.wtz.videomaker.R; 13 | 14 | public class MultiImgSurfaceView extends WeGLSurfaceView implements WeGLRenderer, ImgRenderer.OnSharedTextureChangedListener { 15 | private static final String TAG = MultiImgSurfaceView.class.getSimpleName(); 16 | 17 | private MultiImgRenderer mImgOffScreenRenderer; 18 | private OnScreenRenderer mOnScreenRenderer; 19 | 20 | public MultiImgSurfaceView(Context context) { 21 | this(context, null); 22 | } 23 | 24 | public MultiImgSurfaceView(Context context, AttributeSet attrs) { 25 | this(context, attrs, 0); 26 | } 27 | 28 | public MultiImgSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 29 | super(context, attrs, defStyleAttr); 30 | setRenderMode(RENDERMODE_WHEN_DIRTY); 31 | 32 | mImgOffScreenRenderer = new MultiImgRenderer(context); 33 | mImgOffScreenRenderer.setImageList(new int[]{ 34 | R.drawable.tree, R.drawable.sunflower, R.drawable.lotus, 35 | R.drawable.carry_up, R.drawable.happy 36 | }); 37 | mImgOffScreenRenderer.setSharedTextureChangedListener(this); 38 | 39 | mOnScreenRenderer = new OnScreenRenderer(context, TAG); 40 | mOnScreenRenderer.setExternalTextureId(mImgOffScreenRenderer.getSharedTextureId()); 41 | } 42 | 43 | @Override 44 | protected String getExternalLogTag() { 45 | return TAG; 46 | } 47 | 48 | @Override 49 | public void onSharedTextureChanged(int textureID) { 50 | mOnScreenRenderer.setExternalTextureId(textureID); 51 | } 52 | 53 | @Override 54 | protected WeGLRenderer getRenderer() { 55 | return this; 56 | } 57 | 58 | @Override 59 | public void onEGLContextCreated() { 60 | Log.d(TAG, "onEGLContextCreated"); 61 | mImgOffScreenRenderer.onEGLContextCreated(); 62 | mOnScreenRenderer.onEGLContextCreated(); 63 | } 64 | 65 | @Override 66 | public void onSurfaceChanged(int width, int height) { 67 | Log.d(TAG, "onSurfaceChanged " + width + "x" + height); 68 | mImgOffScreenRenderer.onSurfaceChanged(width, height); 69 | mOnScreenRenderer.onSurfaceChanged(width, height); 70 | } 71 | 72 | @Override 73 | public void onDrawFrame() { 74 | Log.d(TAG, "onDrawFrame"); 75 | mImgOffScreenRenderer.onDrawFrame(); 76 | mOnScreenRenderer.onDrawFrame(); 77 | } 78 | 79 | @Override 80 | public void onEGLContextToDestroy() { 81 | Log.d(TAG, "onEGLContextToDestroy"); 82 | mImgOffScreenRenderer.onEGLContextToDestroy(); 83 | mOnScreenRenderer.onEGLContextToDestroy(); 84 | } 85 | 86 | public void clearSourceImage() { 87 | if (mImgOffScreenRenderer != null) { 88 | mImgOffScreenRenderer.clearSourceImage(); 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/surfaceview/ReverseSurfaceView.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.surfaceview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import com.wtz.libvideomaker.renderer.filters.FilterRenderer; 7 | import com.wtz.libvideomaker.renderer.filters.ReverseFilterRenderer; 8 | 9 | public class ReverseSurfaceView extends FilterSurfaceView{ 10 | private static final String TAG = ReverseSurfaceView.class.getSimpleName(); 11 | 12 | public ReverseSurfaceView(Context context) { 13 | super(context); 14 | } 15 | 16 | public ReverseSurfaceView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public ReverseSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | } 23 | 24 | @Override 25 | protected String getExternalLogTag() { 26 | return TAG; 27 | } 28 | 29 | @Override 30 | protected FilterRenderer createFilterRenderer(Context context) { 31 | return new ReverseFilterRenderer(context); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/utils/DateTimeUtil.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.utils; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | public class DateTimeUtil { 7 | 8 | /** 9 | * @param format e.g. "yy-MM-dd_HH-mm-ss" 10 | * @return DateTime 11 | */ 12 | public static String getCurrentDateTime(String format) { 13 | Date date = new Date(); 14 | SimpleDateFormat df = new SimpleDateFormat(format); 15 | String nowTime = df.format(date); 16 | return nowTime; 17 | } 18 | 19 | /** 20 | * 把剩余毫秒数转化成“时:分:秒”字符串 21 | * 22 | * @param timeMilli 23 | * @return 24 | */ 25 | public static String changeRemainTimeToHms(long timeMilli) { 26 | if (timeMilli == 0) { 27 | return "00:00:00"; 28 | } 29 | int totalSeconds = Math.round((float) timeMilli / 1000);// 毫秒数转秒数,毫秒部分四舍五入 30 | int second = totalSeconds % 60;// 秒数除60得分钟数再取余得秒数 31 | int minute = totalSeconds / 60 % 60;// 秒数除两个60得小时再取余得分钟数 32 | int hour = totalSeconds / 60 / 60;// 秒数除两个60得小时数 33 | String hourString = formatTime(String.valueOf(hour)); 34 | String minuteString = formatTime(String.valueOf(minute)); 35 | String secondString = formatTime(String.valueOf(second)); 36 | return hourString + ":" + minuteString + ":" + secondString; 37 | } 38 | /** 39 | * 把剩余毫秒数转化成“分:秒”字符串 40 | * 41 | * @param timeMilli 42 | * @return 43 | */ 44 | public static String changeRemainTimeToMs(long timeMilli) { 45 | if (timeMilli == 0) { 46 | return "00:00"; 47 | } 48 | int totalSeconds = Math.round((float) timeMilli / 1000);// 毫秒数转秒数,毫秒部分四舍五入 49 | int second = totalSeconds % 60;// 秒数除60得分钟数再取余得秒数 50 | int minute = totalSeconds / 60;// 秒数除1个60得分钟数 51 | String minuteString = formatTime(String.valueOf(minute)); 52 | String secondString = formatTime(String.valueOf(second)); 53 | return minuteString + ":" + secondString; 54 | } 55 | 56 | private static String formatTime(String original) { 57 | if (original != null && original.length() < 2) { 58 | original = "0" + original; 59 | } 60 | return original; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/wtz/videomaker/utils/UriUtil.java: -------------------------------------------------------------------------------- 1 | package com.wtz.videomaker.utils; 2 | 3 | import android.content.ContentUris; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | import android.os.Build; 8 | import android.os.Environment; 9 | import android.provider.DocumentsContract; 10 | import android.provider.MediaStore; 11 | 12 | public class UriUtil { 13 | public static String getUriPath(Context context, Uri uri) { 14 | if (uri == null) { 15 | return null; 16 | } 17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) { 18 | if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { 19 | final String docId = DocumentsContract.getDocumentId(uri); 20 | final String[] split = docId.split(":"); 21 | final String type = split[0]; 22 | if ("primary".equalsIgnoreCase(type)) { 23 | return Environment.getExternalStorageDirectory() + "/" + split[1]; 24 | } 25 | } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { 26 | final String id = DocumentsContract.getDocumentId(uri); 27 | final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); 28 | return getDataColumn(context, contentUri, null, null); 29 | } else if ("com.android.providers.media.documents".equals(uri.getAuthority())) { 30 | final String docId = DocumentsContract.getDocumentId(uri); 31 | final String[] split = docId.split(":"); 32 | final String type = split[0]; 33 | Uri contentUri = null; 34 | if ("image".equals(type)) { 35 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 36 | } else if ("video".equals(type)) { 37 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 38 | } else if ("audio".equals(type)) { 39 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 40 | } 41 | final String selection = "_id=?"; 42 | final String[] selectionArgs = new String[]{split[1]}; 43 | return getDataColumn(context, contentUri, selection, selectionArgs); 44 | } 45 | } else if ("content".equalsIgnoreCase(uri.getScheme())) { 46 | if ("com.google.android.apps.photos.content".equals(uri.getAuthority())) { 47 | return uri.getLastPathSegment(); 48 | } 49 | return getDataColumn(context, uri, null, null); 50 | } else if ("file".equalsIgnoreCase(uri.getScheme())) { 51 | return uri.getPath(); 52 | } 53 | return null; 54 | } 55 | 56 | public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { 57 | Cursor cursor = null; 58 | final String column = "_data"; 59 | final String[] projection = {column}; 60 | try { 61 | cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); 62 | if (cursor != null && cursor.moveToFirst()) { 63 | final int column_index = cursor.getColumnIndexOrThrow(column); 64 | return cursor.getString(column_index); 65 | } 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | } finally { 69 | if (cursor != null) cursor.close(); 70 | } 71 | return null; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /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/carry_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/carry_up.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_ball_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/grid_divider_line_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/happy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/happy.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/icon_select.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/image_default.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/lotus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/lotus.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/purple_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/purple_ball.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/seekbar_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 24 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/seekbar_thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sunflower.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/sunflower.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/tree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wtz2017/VideoMaker/49fb888984122f757b11b8a182addff141926b01/app/src/main/res/drawable/tree.jpg -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 18 | 19 | 23 | 24 | 25 | 26 | 33 | 34 | 43 | 44 |