├── jitpack.yml
├── SSffmpegVideoOperation
├── consumer-rules.pro
├── src
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── simform
│ │ │ └── videooperations
│ │ │ ├── ISize.kt
│ │ │ ├── Paths.kt
│ │ │ ├── FileSelection.kt
│ │ │ ├── FFmpegCallBack.kt
│ │ │ ├── LogMessage.kt
│ │ │ ├── SizeOfImage.kt
│ │ │ ├── CallBackOfQuery.kt
│ │ │ └── Statistics.kt
│ │ └── AndroidManifest.xml
├── .gitignore
├── proguard-rules.pro
└── build.gradle
├── .idea
└── .gitignore
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── app
├── src
│ └── main
│ │ ├── assets
│ │ └── little_lord.ttf
│ │ ├── res
│ │ ├── 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
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── values-v26
│ │ │ └── themes.xml
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ ├── layout
│ │ │ ├── progress_view.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_other_ffmpeg_process.xml
│ │ │ ├── activity_combine_videos.xml
│ │ │ ├── activity_audios_merge.xml
│ │ │ ├── activity_compress_audio.xml
│ │ │ ├── activity_extract_audio.xml
│ │ │ ├── activity_extract_images.xml
│ │ │ ├── activity_remove_audio_from_video.xml
│ │ │ ├── activity_aspect_ratio.xml
│ │ │ ├── activity_change_audio_valume.xml
│ │ │ ├── activity_video_to_gif.xml
│ │ │ ├── activity_video_fade_in_fade_out.xml
│ │ │ ├── activity_image_to_video_convert.xml
│ │ │ ├── activity_combine_images.xml
│ │ │ ├── activity_compress_video.xml
│ │ │ ├── activity_reverse.xml
│ │ │ ├── activity_fast_and_slow_video_motion.xml
│ │ │ ├── activity_fast_and_slow_audio.xml
│ │ │ ├── activity_merge_audio_video.xml
│ │ │ ├── activity_merge_image_and_mp3.xml
│ │ │ ├── activity_merge_image_and_video.xml
│ │ │ ├── activity_add_text_on_video.xml
│ │ │ ├── activity_crop_audio.xml
│ │ │ └── activity_cut_video_using_time.xml
│ │ ├── values-night
│ │ │ └── themes.xml
│ │ └── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ └── com
│ │ │ └── simform
│ │ │ └── videoimageeditor
│ │ │ ├── Utils.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── BaseActivity.kt
│ │ │ ├── middlewareActivity
│ │ │ ├── OtherFFMPEGProcessActivity.kt
│ │ │ └── VideoProcessActivity.kt
│ │ │ ├── videoProcessActivity
│ │ │ ├── ExtractAudioActivity.kt
│ │ │ ├── ReverseVideoActivity.kt
│ │ │ ├── VideoToGifActivity.kt
│ │ │ ├── AspectRatioActivity.kt
│ │ │ ├── RemoveAudioFromVideoActivity.kt
│ │ │ ├── FastAndSlowVideoMotionActivity.kt
│ │ │ ├── ExtractImagesActivity.kt
│ │ │ ├── ImageToVideoConvertActivity.kt
│ │ │ ├── CompressVideoActivity.kt
│ │ │ ├── VideoFadeInFadeOutActivity.kt
│ │ │ ├── CombineImagesActivity.kt
│ │ │ ├── MergeAudioVideoActivity.kt
│ │ │ ├── MergeImageAndMP3Activity.kt
│ │ │ └── CombineVideosActivity.kt
│ │ │ └── otherFFMPEGProcessActivity
│ │ │ ├── ChangeAudioVolumeActivity.kt
│ │ │ ├── CompressAudioActivity.kt
│ │ │ ├── FastAndSlowAudioActivity.kt
│ │ │ └── AudiosMergeActivity.kt
│ │ └── AndroidManifest.xml
├── .gitignore
├── proguard-rules.pro
└── build.gradle
├── CONTRIBUTING.md
├── .gitignore
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ └── github-actions.yml
├── gradle.properties
├── gradlew.bat
└── CODE_OF_CONDUCT.md
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk11
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':SSffmpegVideoOperation'
2 | include ':app'
3 | rootProject.name = "FFMPEG Video Operation"
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/assets/little_lord.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/assets/little_lord.ttf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimformSolutionsPvtLtd/SSffmpegVideoOperation/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 14 18:23:01 IST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Way to contribute
2 |
3 | 1. Fork the repo and create your branch from `master`.
4 | 2. Clone the project to your own machine.
5 | 3. Commit changes to your own branch
6 | 4. Make sure your code lints.
7 | 5. Push your work back up to your fork.
8 | 6. Issue that pull request!
9 |
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/ISize.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | /**
4 | * Created by Ashvin Vavaliya on 29,December,2020
5 | * Simform Solutions Pvt Ltd.
6 | */
7 | interface ISize {
8 | fun width(): Int
9 | fun height(): Int
10 | }
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | libs/
12 | .idea/gradle.xml
13 | .idea/misc.xml
14 | .idea/modules.xml
15 | .idea/runConfigurations.xml
16 | .idea/vcs.xml
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/Paths.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | /**
4 | * Created by Ashvin Vavaliya on 29,December,2020
5 | * Simform Solutions Pvt Ltd.
6 | */
7 | class Paths {
8 | var filePath : String = ""
9 | var isImageFile : Boolean = false
10 | }
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | libs/
12 | .idea/gradle.xml
13 | .idea/misc.xml
14 | .idea/modules.xml
15 | .idea/runConfigurations.xml
16 | .idea/vcs.xml
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/FileSelection.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | import com.jaiselrahman.filepicker.model.MediaFile
4 |
5 | /**
6 | * Created by Ashvin Vavaliya on 31,December,2020
7 | * Simform Solutions Pvt Ltd.
8 | */
9 | interface FileSelection {
10 | fun selectedFiles(mediaFiles:List?,requestCode: Int){}
11 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /.gradle/
4 | /local.properties
5 | /.idea/caches
6 | /.idea/codeStyles
7 | /.idea/inspectionProfiles
8 | /.idea/libraries
9 | /.idea/modules.xml
10 | /.idea/workspace.xml
11 | /.idea/compiler.xml
12 | /.idea/gradle.xml
13 | /.idea/jarRepositories.xml
14 | /.idea/misc.xml
15 | /.idea/modules.xml
16 | /.idea/vcs.xml
17 | /.idea/.name
18 | /.idea/navEditor.xml
19 | /.idea/assetWizardSettings.xml
20 | .DS_Store
21 | /build
22 | /captures
23 | .externalNativeBuild
24 | .cxx
25 | github.properties
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/FFmpegCallBack.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | import com.simform.videooperations.Statistics
4 | import com.simform.videooperations.LogMessage
5 |
6 | /**
7 | * Created by Ashvin Vavaliya on 01,January,2021
8 | * Simform Solutions Pvt Ltd.
9 | */
10 | interface FFmpegCallBack {
11 | fun process(logMessage: LogMessage){}
12 | fun statisticsProcess(statistics: Statistics) {}
13 | fun success(){}
14 | fun cancel(){}
15 | fun failed(){}
16 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-v26/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #2D647D
4 | #2D647D
5 | #46a0c6
6 | #FFBB86FC
7 | #FF6200EE
8 | #FF3700B3
9 | #FF03DAC5
10 | #FF018786
11 | #FF000000
12 | #FFFFFFFF
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/LogMessage.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | import com.arthenica.mobileffmpeg.Level
4 |
5 | class LogMessage(val executionId: Long, val level: Level, val text: String) {
6 | override fun toString(): String {
7 | val stringBuilder = StringBuilder()
8 | stringBuilder.append("LogMessage{")
9 | stringBuilder.append("executionId=")
10 | stringBuilder.append(executionId)
11 | stringBuilder.append(", level=")
12 | stringBuilder.append(level)
13 | stringBuilder.append(", text=")
14 | stringBuilder.append("\'")
15 | stringBuilder.append(text)
16 | stringBuilder.append('\'')
17 | stringBuilder.append('}')
18 | return stringBuilder.toString()
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/progress_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/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
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import androidx.appcompat.app.AppCompatActivity
6 |
7 | /**
8 | * Created by Ashvin Vavaliya on 06,July,2021
9 | * Simform Solutions Pvt Ltd.
10 | */
11 | class Utils {
12 | fun addSupportActionBar(context: AppCompatActivity, title: Int) {
13 | if (context.supportActionBar != null) {
14 | context.supportActionBar?.setDisplayHomeAsUpEnabled(true)
15 | context.supportActionBar?.setDisplayShowHomeEnabled(true)
16 | context.supportActionBar?.title = context.getString(title)
17 | }
18 | }
19 |
20 | fun openActivity(context: Context, activity: AppCompatActivity) {
21 | context.startActivity(Intent(context, activity::class.java))
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/SizeOfImage.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | import android.graphics.BitmapFactory
4 |
5 | /**
6 | * Created by Ashvin Vavaliya on 29,December,2020
7 | * Simform Solutions Pvt Ltd.
8 | */
9 | class SizeOfImage(private val path: String) : ISize {
10 | private var width: Int
11 | private var height: Int
12 | override fun width(): Int {
13 | if (width == -1) {
14 | init()
15 | }
16 | return width
17 | }
18 |
19 | override fun height(): Int {
20 | if (height == -1) {
21 | init()
22 | }
23 | return height
24 | }
25 |
26 | private fun init() {
27 | val options = BitmapFactory.Options()
28 | options.inJustDecodeBounds = true
29 | BitmapFactory.decodeFile(path, options)
30 | width = options.outWidth
31 | height = options.outHeight
32 | }
33 |
34 | init {
35 | width = -1
36 | height = -1
37 | }
38 | }
--------------------------------------------------------------------------------
/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 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor
2 |
3 | import android.view.View
4 | import com.simform.videoimageeditor.middlewareActivity.OtherFFMPEGProcessActivity
5 | import com.simform.videoimageeditor.middlewareActivity.VideoProcessActivity
6 | import kotlinx.android.synthetic.main.activity_main.imageGifOperation
7 | import kotlinx.android.synthetic.main.activity_main.videoOperation
8 |
9 | class MainActivity : BaseActivity(R.layout.activity_main, R.string.ffpmeg_title) {
10 | override fun initialization() {
11 | supportActionBar?.title = getString(R.string.ffpmeg_title)
12 | supportActionBar?.setDisplayHomeAsUpEnabled(false)
13 | supportActionBar?.setDisplayShowHomeEnabled(false)
14 | videoOperation.setOnClickListener(this)
15 | imageGifOperation.setOnClickListener(this)
16 | }
17 |
18 | override fun onClick(v: View?) {
19 | when (v?.id) {
20 | R.id.videoOperation -> {
21 | utils.openActivity(this, VideoProcessActivity())
22 | }
23 | R.id.imageGifOperation -> {
24 | utils.openActivity(this, OtherFFMPEGProcessActivity())
25 | }
26 | }
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/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 | -keep public class * implements com.bumptech.glide.module.GlideModule
23 | -keep class * extends com.bumptech.glide.module.AppGlideModule {
24 | (...);
25 | }
26 | -keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
27 | **[] $VALUES;
28 | public *;
29 | }
30 | -keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
31 | *** rewind();
32 | }
33 |
34 | # for DexGuard only
35 | -keepresourcexmlelements manifest/application/meta-data@value=GlideModule
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
21 |
22 |
33 |
34 |
--------------------------------------------------------------------------------
/.github/workflows/github-actions.yml:
--------------------------------------------------------------------------------
1 | name: GitHub Actions Demo
2 | on:
3 | push:
4 | paths:
5 | - '**.yml'
6 | branches:
7 | - main
8 | pull_request:
9 | branches:
10 | - main
11 | jobs:
12 | Explore-GitHub-Actions:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
16 | - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
17 | - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
18 | - name: Check out repository code
19 | uses: actions/checkout@v2
20 | - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
21 | - run: echo "🖥️ The workflow is now ready to test your code on the runner."
22 | - name: List files in the repository
23 | run: |
24 | ls ${{ github.workspace }}
25 | - run: echo "🍏 This job's status is ${{ job.status }}."
26 | - uses: actions/checkout@v1
27 |
28 | - name: Set Up JDK // 1
29 | uses: actions/setup-java@v1
30 | with:
31 | java-version: 11
32 |
33 | - name: Change wrapper permissions // 2
34 | run: chmod +x ./gradlew
35 |
36 | - name: Run Tests // 3
37 | run: ./gradlew test
38 |
39 | - name: Build Project // 4
40 | run: ./gradlew assemble
41 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-android-extensions'
5 | id 'kotlin-kapt'
6 | }
7 |
8 | android {
9 | compileSdkVersion 31
10 |
11 | defaultConfig {
12 | applicationId "com.simform.videoimageeditor"
13 | minSdkVersion 24
14 | targetSdkVersion 31
15 | versionCode 1
16 | versionName "1.0"
17 | testInstrumentationRunner "androidx...test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | kotlinOptions {
27 | jvmTarget = '11'
28 | }
29 | flavorDimensions "default"
30 |
31 | androidExtensions {
32 | experimental = true
33 | }
34 | }
35 |
36 | final roomSchemaDir = "$projectDir/roomSchemas"
37 |
38 | kapt {
39 | mapDiagnosticLocations = true
40 | arguments {
41 | arg("room.schemaLocation", roomSchemaDir)
42 | }
43 | }
44 |
45 | dependencies {
46 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
47 | implementation 'androidx.core:core-ktx:1.8.0'
48 | implementation 'androidx.appcompat:appcompat:1.4.2'
49 | implementation 'com.github.bumptech.glide:glide:4.12.0'
50 | annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
51 | implementation 'com.google.android.material:material:1.6.1'
52 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
53 | implementation 'pub.devrel:easypermissions:3.0.0'
54 | implementation 'com.github.jaiselrahman:FilePicker:1.3.2'
55 | implementation 'com.kovachcode:timePickerWithSeconds:1.0.1'
56 | implementation project(':SSffmpegVideoOperation')
57 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor
2 |
3 | import android.content.Intent
4 | import android.media.MediaMetadataRetriever
5 | import android.os.Bundle
6 | import android.view.MenuItem
7 | import android.view.View
8 | import androidx.appcompat.app.AppCompatActivity
9 | import com.jaiselrahman.filepicker.activity.FilePickerActivity
10 | import com.jaiselrahman.filepicker.model.MediaFile
11 | import com.simform.videooperations.FFmpegQueryExtension
12 | import com.simform.videooperations.FileSelection
13 |
14 | /**
15 | * Created by Ashvin Vavaliya on 29,December,2020
16 | * Simform Solutions Pvt Ltd.
17 | */
18 | abstract class BaseActivity(view: Int, title: Int) : AppCompatActivity(), View.OnClickListener, FileSelection {
19 | private var layoutView = view
20 | var toolbarTitle: Int = title
21 | var height: Int? = 0
22 | var width: Int? = 0
23 | var mediaFiles: List? = null
24 | var retriever: MediaMetadataRetriever? = null
25 | val utils = Utils()
26 | val ffmpegQueryExtension = FFmpegQueryExtension()
27 |
28 | override fun onCreate(savedInstanceState: Bundle?) {
29 | super.onCreate(savedInstanceState)
30 | setContentView(layoutView)
31 | utils.addSupportActionBar(this, toolbarTitle)
32 | initialization()
33 | }
34 |
35 | protected abstract fun initialization()
36 |
37 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
38 | if (item.itemId == android.R.id.home) {
39 | finish()
40 | }
41 | return super.onOptionsItemSelected(item)
42 | }
43 |
44 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
45 | super.onActivityResult(requestCode, resultCode, data)
46 | if (resultCode == RESULT_OK && data != null) {
47 | mediaFiles = data.getParcelableArrayListExtra(FilePickerActivity.MEDIA_FILES)
48 | (this as FileSelection).selectedFiles(mediaFiles,requestCode)
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | id 'maven-publish'
5 | }
6 |
7 | afterEvaluate {
8 | publishing {
9 | publications {
10 | // Creates a Maven publication called "release".
11 | release(MavenPublication) {
12 | from components.release
13 | groupId = 'com.simform.videooperations'
14 | artifactId = 'videooperations'
15 | version = '1.0.8'
16 | }
17 | }
18 | }
19 | }
20 |
21 | android {
22 | compileSdkVersion 30
23 | buildToolsVersion "29.0.3"
24 |
25 | defaultConfig {
26 | minSdkVersion 24
27 | targetSdkVersion 30
28 |
29 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
30 | consumerProguardFiles "consumer-rules.pro"
31 | ndk {
32 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'
33 | }
34 | multiDexEnabled true
35 | }
36 |
37 | buildTypes {
38 | release {
39 | minifyEnabled false
40 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
41 | }
42 | }
43 | compileOptions {
44 | sourceCompatibility JavaVersion.VERSION_11
45 | targetCompatibility JavaVersion.VERSION_11
46 | }
47 | kotlinOptions {
48 | jvmTarget = '11'
49 | }
50 | packagingOptions {
51 | exclude 'META-INF/rxjava.properties'
52 | exclude 'META-INF/DEPENDENCIES'
53 | exclude 'META-INF/INDEX.LIST'
54 | exclude 'META-INF/LICENSE'
55 | exclude 'META-INF/LICENSE.txt'
56 | exclude 'META-INF/license.txt'
57 | exclude 'META-INF/NOTICE'
58 | exclude 'META-INF/NOTICE.txt'
59 | exclude 'META-INF/notice.txt'
60 | exclude 'META-INF/ASL2.0'
61 | }
62 | }
63 |
64 | dependencies {
65 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
66 | implementation 'androidx.core:core-ktx:1.8.0'
67 | implementation 'androidx.appcompat:appcompat:1.4.2'
68 | implementation 'com.google.android.material:material:1.6.1'
69 | implementation 'pub.devrel:easypermissions:3.0.0'
70 | implementation 'com.arthenica:mobile-ffmpeg-full:4.4'
71 | implementation 'com.github.jaiselrahman:FilePicker:1.3.2'
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/OtherFFMPEGProcessActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.middlewareActivity
2 |
3 | import android.view.View
4 | import com.simform.videoimageeditor.BaseActivity
5 | import com.simform.videoimageeditor.R
6 | import com.simform.videoimageeditor.otherFFMPEGProcessActivity.AudiosMergeActivity
7 | import com.simform.videoimageeditor.otherFFMPEGProcessActivity.ChangeAudioVolumeActivity
8 | import com.simform.videoimageeditor.otherFFMPEGProcessActivity.CompressAudioActivity
9 | import com.simform.videoimageeditor.otherFFMPEGProcessActivity.CropAudioActivity
10 | import com.simform.videoimageeditor.otherFFMPEGProcessActivity.FastAndSlowAudioActivity
11 | import com.simform.videoimageeditor.otherFFMPEGProcessActivity.MergeGIFActivity
12 | import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnAudiosVolumeUpdate
13 | import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnCompressAudio
14 | import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnCutAudio
15 | import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnFastAndSlowAudio
16 | import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnMergeAudios
17 | import kotlinx.android.synthetic.main.activity_other_ffmpeg_process.btnMergeGIF
18 |
19 | class OtherFFMPEGProcessActivity : BaseActivity(R.layout.activity_other_ffmpeg_process, R.string.other_ffmpeg_operations) {
20 | override fun initialization() {
21 | supportActionBar?.title = getString(R.string.other_ffmpeg_operations)
22 | btnMergeGIF.setOnClickListener(this)
23 | btnMergeAudios.setOnClickListener(this)
24 | btnAudiosVolumeUpdate.setOnClickListener(this)
25 | btnFastAndSlowAudio.setOnClickListener(this)
26 | btnCutAudio.setOnClickListener(this)
27 | btnCompressAudio.setOnClickListener(this)
28 | }
29 |
30 | override fun onClick(v: View?) {
31 | when (v?.id) {
32 | R.id.btnMergeGIF -> {
33 | utils.openActivity(this, MergeGIFActivity())
34 | }
35 | R.id.btnMergeAudios -> {
36 | utils.openActivity(this, AudiosMergeActivity())
37 | }
38 | R.id.btnAudiosVolumeUpdate -> {
39 | utils.openActivity(this, ChangeAudioVolumeActivity())
40 | }
41 | R.id.btnFastAndSlowAudio -> {
42 | utils.openActivity(this, FastAndSlowAudioActivity())
43 | }
44 | R.id.btnCutAudio -> {
45 | utils.openActivity(this, CropAudioActivity())
46 | }
47 | R.id.btnCompressAudio -> {
48 | utils.openActivity(this, CompressAudioActivity())
49 | }
50 | }
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/CallBackOfQuery.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import android.util.Log
6 | import com.arthenica.mobileffmpeg.Config
7 | import com.arthenica.mobileffmpeg.FFmpeg
8 | import java.util.concurrent.CyclicBarrier
9 |
10 | /**
11 | * Created by Ashvin Vavaliya on 22,January,2021
12 | * Simform Solutions Pvt Ltd.
13 | */
14 | class CallBackOfQuery {
15 | fun callQuery(query: Array, fFmpegCallBack: FFmpegCallBack) {
16 | val gate = CyclicBarrier(2)
17 | object : Thread() {
18 | override fun run() {
19 | gate.await()
20 | process(query, fFmpegCallBack)
21 | }
22 | }.start()
23 | gate.await()
24 | }
25 |
26 | fun cancelProcess(executionId: Long) {
27 | if (!executionId.equals(0)) {
28 | FFmpeg.cancel(executionId)
29 | } else {
30 | FFmpeg.cancel()
31 | }
32 | }
33 |
34 | fun cancelProcess() {
35 | FFmpeg.cancel()
36 | }
37 |
38 | private fun process(query: Array, ffmpegCallBack: FFmpegCallBack) {
39 | val processHandler = Handler(Looper.getMainLooper())
40 | Config.enableLogCallback { logMessage ->
41 | val logs = LogMessage(logMessage.executionId, logMessage.level, logMessage.text)
42 | processHandler.post {
43 | ffmpegCallBack.process(logs)
44 | }
45 | }
46 | Config.enableStatisticsCallback { statistics ->
47 | val statisticsLog =
48 | Statistics(
49 | statistics.executionId,
50 | statistics.videoFrameNumber,
51 | statistics.videoFps,
52 | statistics.videoQuality,
53 | statistics.size,
54 | statistics.time,
55 | statistics.bitrate,
56 | statistics.speed
57 | )
58 | processHandler.post {
59 | ffmpegCallBack.statisticsProcess(statisticsLog)
60 | }
61 | }
62 | when (FFmpeg.execute(query)) {
63 | Config.RETURN_CODE_SUCCESS -> {
64 | processHandler.post {
65 | ffmpegCallBack.success()
66 | }
67 | }
68 | Config.RETURN_CODE_CANCEL -> {
69 | processHandler.post {
70 | ffmpegCallBack.cancel()
71 | FFmpeg.cancel()
72 | }
73 | }
74 | else -> {
75 | processHandler.post {
76 | ffmpegCallBack.failed()
77 | Config.printLastCommandOutput(Log.INFO)
78 | }
79 | }
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_other_ffmpeg_process.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
13 |
14 |
22 |
23 |
30 |
31 |
38 |
39 |
46 |
47 |
54 |
55 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_combine_videos.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_audios_merge.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_compress_audio.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_extract_audio.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_extract_images.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_remove_audio_from_video.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_aspect_ratio.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_change_audio_valume.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_video_to_gif.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_video_fade_in_fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
60 |
61 |
69 |
--------------------------------------------------------------------------------
/SSffmpegVideoOperation/src/main/java/com/simform/videooperations/Statistics.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videooperations
2 |
3 | /**
4 | *
5 | * Statistics for running executions.
6 | *
7 | * @author Taner Sener
8 | * @since v2.1
9 | */
10 | class Statistics {
11 | var executionId: Long
12 | var videoFrameNumber: Int
13 | var videoFps: Float
14 | var videoQuality: Float
15 | var size: Long
16 | var time: Int
17 | var bitrate: Double
18 | var speed: Double
19 |
20 | constructor() {
21 | executionId = 0
22 | videoFrameNumber = 0
23 | videoFps = 0f
24 | videoQuality = 0f
25 | size = 0
26 | time = 0
27 | bitrate = 0.0
28 | speed = 0.0
29 | }
30 |
31 | constructor(executionId: Long, videoFrameNumber: Int, videoFps: Float, videoQuality: Float, size: Long, time: Int, bitrate: Double, speed: Double) {
32 | this.executionId = executionId
33 | this.videoFrameNumber = videoFrameNumber
34 | this.videoFps = videoFps
35 | this.videoQuality = videoQuality
36 | this.size = size
37 | this.time = time
38 | this.bitrate = bitrate
39 | this.speed = speed
40 | }
41 |
42 | fun update(newStatistics: Statistics?) {
43 | if (newStatistics != null) {
44 | executionId = newStatistics.executionId
45 | if (newStatistics.videoFrameNumber > 0) {
46 | videoFrameNumber = newStatistics.videoFrameNumber
47 | }
48 | if (newStatistics.videoFps > 0) {
49 | videoFps = newStatistics.videoFps
50 | }
51 | if (newStatistics.videoQuality > 0) {
52 | videoQuality = newStatistics.videoQuality
53 | }
54 | if (newStatistics.size > 0) {
55 | size = newStatistics.size
56 | }
57 | if (newStatistics.time > 0) {
58 | time = newStatistics.time
59 | }
60 | if (newStatistics.bitrate > 0) {
61 | bitrate = newStatistics.bitrate
62 | }
63 | if (newStatistics.speed > 0) {
64 | speed = newStatistics.speed
65 | }
66 | }
67 | }
68 |
69 | override fun toString(): String {
70 | val stringBuilder = StringBuilder()
71 | stringBuilder.append("Statistics{")
72 | stringBuilder.append("executionId=")
73 | stringBuilder.append(executionId)
74 | stringBuilder.append(", videoFrameNumber=")
75 | stringBuilder.append(videoFrameNumber)
76 | stringBuilder.append(", videoFps=")
77 | stringBuilder.append(videoFps)
78 | stringBuilder.append(", videoQuality=")
79 | stringBuilder.append(videoQuality)
80 | stringBuilder.append(", size=")
81 | stringBuilder.append(size)
82 | stringBuilder.append(", time=")
83 | stringBuilder.append(time)
84 | stringBuilder.append(", bitrate=")
85 | stringBuilder.append(bitrate)
86 | stringBuilder.append(", speed=")
87 | stringBuilder.append(speed)
88 | stringBuilder.append('}')
89 | return stringBuilder.toString()
90 | }
91 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_image_to_video_convert.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
59 |
60 |
72 |
73 |
81 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at ashvin.v@simformsolutions.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_combine_images.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
59 |
60 |
72 |
73 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_compress_video.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
48 |
49 |
60 |
61 |
73 |
74 |
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractAudioActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.view.View
4 | import android.widget.Toast
5 | import com.jaiselrahman.filepicker.model.MediaFile
6 | import com.simform.videoimageeditor.BaseActivity
7 | import com.simform.videoimageeditor.R
8 | import com.simform.videooperations.CallBackOfQuery
9 | import com.simform.videooperations.Common
10 | import com.simform.videooperations.FFmpegCallBack
11 | import com.simform.videooperations.FFmpegQueryExtension
12 | import com.simform.videooperations.LogMessage
13 | import kotlinx.android.synthetic.main.activity_extract_audio.btnExtract
14 | import kotlinx.android.synthetic.main.activity_extract_audio.btnVideoPath
15 | import kotlinx.android.synthetic.main.activity_extract_audio.mProgressView
16 | import kotlinx.android.synthetic.main.activity_extract_audio.tvInputPathVideo
17 | import kotlinx.android.synthetic.main.activity_extract_audio.tvOutputPath
18 |
19 | class ExtractAudioActivity : BaseActivity(R.layout.activity_extract_audio, R.string.extract_audio_from_video) {
20 | private var isInputVideoSelected: Boolean = false
21 | override fun initialization() {
22 | btnVideoPath.setOnClickListener(this)
23 | btnExtract.setOnClickListener(this)
24 | }
25 |
26 | override fun onClick(v: View?) {
27 | when (v?.id) {
28 | R.id.btnVideoPath -> {
29 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
30 | }
31 | R.id.btnExtract -> {
32 | when {
33 | !isInputVideoSelected -> {
34 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
35 | }
36 | else -> {
37 | processStart()
38 | extractProcess()
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
45 | private fun extractProcess() {
46 | val outputPath = Common.getFilePath(this, Common.MP3)
47 | val query = ffmpegQueryExtension.extractAudio(tvInputPathVideo.text.toString(), outputPath)
48 |
49 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
50 | override fun process(logMessage: LogMessage) {
51 | tvOutputPath.text = logMessage.text
52 | }
53 |
54 | override fun success() {
55 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
56 | processStop()
57 | }
58 |
59 | override fun cancel() {
60 | processStop()
61 | }
62 |
63 | override fun failed() {
64 | processStop()
65 | }
66 | })
67 | }
68 |
69 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
70 | when (requestCode) {
71 | Common.VIDEO_FILE_REQUEST_CODE -> {
72 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
73 | tvInputPathVideo.text = mediaFiles[0].path
74 | isInputVideoSelected = true
75 | } else {
76 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
77 | }
78 | }
79 | }
80 | }
81 |
82 | private fun processStop() {
83 | btnVideoPath.isEnabled = true
84 | btnExtract.isEnabled = true
85 | mProgressView.visibility = View.GONE
86 | }
87 |
88 | private fun processStart() {
89 | btnVideoPath.isEnabled = false
90 | btnExtract.isEnabled = false
91 | mProgressView.visibility = View.VISIBLE
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ReverseVideoActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.view.View
4 | import android.widget.Toast
5 | import com.jaiselrahman.filepicker.model.MediaFile
6 | import com.simform.videoimageeditor.BaseActivity
7 | import com.simform.videoimageeditor.R
8 | import com.simform.videooperations.CallBackOfQuery
9 | import com.simform.videooperations.Common
10 | import com.simform.videooperations.FFmpegCallBack
11 | import com.simform.videooperations.FFmpegQueryExtension
12 | import com.simform.videooperations.LogMessage
13 | import kotlinx.android.synthetic.main.activity_reverse.btnMotion
14 | import kotlinx.android.synthetic.main.activity_reverse.btnVideoPath
15 | import kotlinx.android.synthetic.main.activity_reverse.isWithAudioSwitch
16 | import kotlinx.android.synthetic.main.activity_reverse.mProgressView
17 | import kotlinx.android.synthetic.main.activity_reverse.tvInputPathVideo
18 | import kotlinx.android.synthetic.main.activity_reverse.tvOutputPath
19 |
20 | class ReverseVideoActivity : BaseActivity(R.layout.activity_reverse, R.string.reverse_video) {
21 | private var isInputVideoSelected: Boolean = false
22 | override fun initialization() {
23 | btnVideoPath.setOnClickListener(this)
24 | btnMotion.setOnClickListener(this)
25 | }
26 |
27 | override fun onClick(v: View?) {
28 | when (v?.id) {
29 | R.id.btnVideoPath -> {
30 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
31 | }
32 | R.id.btnMotion -> {
33 | when {
34 | !isInputVideoSelected -> {
35 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
36 | }
37 | else -> {
38 | processStart()
39 | reverseProcess()
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
46 | private fun reverseProcess() {
47 | val outputPath = Common.getFilePath(this, Common.VIDEO)
48 | val query = ffmpegQueryExtension.videoReverse(tvInputPathVideo.text.toString(), isWithAudioSwitch.isChecked, outputPath)
49 |
50 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
51 | override fun process(logMessage: LogMessage) {
52 | tvOutputPath.text = logMessage.text
53 | }
54 |
55 | override fun success() {
56 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
57 | processStop()
58 | }
59 |
60 | override fun cancel() {
61 | processStop()
62 | }
63 |
64 | override fun failed() {
65 | processStop()
66 | }
67 |
68 | })
69 | }
70 |
71 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
72 | when (requestCode) {
73 | Common.VIDEO_FILE_REQUEST_CODE -> {
74 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
75 | tvInputPathVideo.text = mediaFiles[0].path
76 | isInputVideoSelected = true
77 | } else {
78 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
79 | }
80 | }
81 | }
82 | }
83 |
84 | private fun processStop() {
85 | btnVideoPath.isEnabled = true
86 | btnMotion.isEnabled = true
87 | mProgressView.visibility = View.GONE
88 | }
89 |
90 | private fun processStart() {
91 | btnVideoPath.isEnabled = false
92 | btnMotion.isEnabled = false
93 | mProgressView.visibility = View.VISIBLE
94 | }
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/ChangeAudioVolumeActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.otherFFMPEGProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.jaiselrahman.filepicker.model.MediaFile
7 | import com.simform.videoimageeditor.BaseActivity
8 | import com.simform.videoimageeditor.R
9 | import com.simform.videooperations.CallBackOfQuery
10 | import com.simform.videooperations.Common
11 | import com.simform.videooperations.FFmpegCallBack
12 | import com.simform.videooperations.FFmpegQueryExtension
13 | import com.simform.videooperations.LogMessage
14 | import kotlinx.android.synthetic.main.activity_change_audio_valume.btnAudioPath
15 | import kotlinx.android.synthetic.main.activity_change_audio_valume.btnChange
16 | import kotlinx.android.synthetic.main.activity_change_audio_valume.mProgressView
17 | import kotlinx.android.synthetic.main.activity_change_audio_valume.tvInputPathAudio
18 | import kotlinx.android.synthetic.main.activity_change_audio_valume.tvOutputPath
19 |
20 | class ChangeAudioVolumeActivity : BaseActivity(R.layout.activity_change_audio_valume, R.string.change_audio_volume) {
21 | private var isInputAudioSelected: Boolean = false
22 | override fun initialization() {
23 | btnAudioPath.setOnClickListener(this)
24 | btnChange.setOnClickListener(this)
25 | }
26 |
27 | override fun onClick(v: View?) {
28 | when (v?.id) {
29 | R.id.btnAudioPath -> {
30 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
31 | }
32 | R.id.btnChange -> {
33 | mediaFiles?.size?.let {
34 | if (!isInputAudioSelected) {
35 | Toast.makeText(this, getString(R.string.validation_of_audio), Toast.LENGTH_SHORT).show()
36 | return
37 | }
38 | }
39 | processStart()
40 | mergeAudioProcess()
41 | }
42 | }
43 | }
44 |
45 | private fun mergeAudioProcess() {
46 | val outputPath = Common.getFilePath(this, Common.MP3)
47 | val query = ffmpegQueryExtension.audioVolumeUpdate(tvInputPathAudio.text.toString(), volume = 0.1f, output = outputPath)
48 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
49 | override fun process(logMessage: LogMessage) {
50 | tvOutputPath.text = logMessage.text
51 | }
52 |
53 | override fun success() {
54 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
55 | processStop()
56 | }
57 |
58 | override fun cancel() {
59 | processStop()
60 | }
61 |
62 | override fun failed() {
63 | processStop()
64 | }
65 | })
66 | }
67 |
68 | private fun processStop() {
69 | btnAudioPath.isEnabled = true
70 | btnChange.isEnabled = true
71 | mProgressView.visibility = View.GONE
72 | }
73 |
74 | private fun processStart() {
75 | btnAudioPath.isEnabled = false
76 | btnChange.isEnabled = false
77 | mProgressView.visibility = View.VISIBLE
78 | }
79 |
80 | @SuppressLint("NewApi")
81 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
82 | when (requestCode) {
83 | Common.AUDIO_FILE_REQUEST_CODE -> {
84 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
85 | tvInputPathAudio.text = mediaFiles[0].path
86 | isInputAudioSelected = true
87 | } else {
88 | Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
89 | }
90 | }
91 | }
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoToGifActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.jaiselrahman.filepicker.model.MediaFile
7 | import com.simform.videoimageeditor.BaseActivity
8 | import com.simform.videoimageeditor.R
9 | import com.simform.videooperations.CallBackOfQuery
10 | import com.simform.videooperations.Common
11 | import com.simform.videooperations.FFmpegCallBack
12 | import com.simform.videooperations.FFmpegQueryExtension
13 | import com.simform.videooperations.LogMessage
14 | import java.util.concurrent.CyclicBarrier
15 | import kotlinx.android.synthetic.main.activity_video_to_gif.btnConvertIntoGif
16 | import kotlinx.android.synthetic.main.activity_video_to_gif.btnVideoPath
17 | import kotlinx.android.synthetic.main.activity_video_to_gif.mProgressView
18 | import kotlinx.android.synthetic.main.activity_video_to_gif.tvInputPathVideo
19 | import kotlinx.android.synthetic.main.activity_video_to_gif.tvOutputPath
20 |
21 | class VideoToGifActivity : BaseActivity(R.layout.activity_video_to_gif, R.string.video_to_gif) {
22 | private var isInputVideoSelected: Boolean = false
23 | override fun initialization() {
24 | btnVideoPath.setOnClickListener(this)
25 | btnConvertIntoGif.setOnClickListener(this)
26 | }
27 |
28 | override fun onClick(v: View?) {
29 | when (v?.id) {
30 | R.id.btnVideoPath -> {
31 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
32 | }
33 | R.id.btnConvertIntoGif -> {
34 | when {
35 | !isInputVideoSelected -> {
36 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
37 | }
38 | else -> {
39 | processStart()
40 | convertProcess()
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 | private fun convertProcess() {
48 | val outputPath = Common.getFilePath(this, Common.GIF)
49 | val query = ffmpegQueryExtension.convertVideoToGIF(tvInputPathVideo.text.toString(), outputPath)
50 |
51 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
52 | override fun process(logMessage: LogMessage) {
53 | tvOutputPath.text = logMessage.text
54 | }
55 |
56 | override fun success() {
57 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
58 | processStop()
59 | }
60 |
61 | override fun cancel() {
62 | processStop()
63 | }
64 |
65 | override fun failed() {
66 | processStop()
67 | }
68 |
69 | })
70 | }
71 |
72 | @SuppressLint("NewApi")
73 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
74 | when (requestCode) {
75 | Common.VIDEO_FILE_REQUEST_CODE -> {
76 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
77 | tvInputPathVideo.text = mediaFiles[0].path
78 | isInputVideoSelected = true
79 | } else {
80 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
81 | }
82 | }
83 | }
84 | }
85 |
86 | private fun processStop() {
87 | btnVideoPath.isEnabled = true
88 | btnConvertIntoGif.isEnabled = true
89 | mProgressView.visibility = View.GONE
90 | }
91 |
92 | private fun processStart() {
93 | btnVideoPath.isEnabled = false
94 | btnConvertIntoGif.isEnabled = false
95 | mProgressView.visibility = View.VISIBLE
96 | }
97 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/CompressAudioActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.otherFFMPEGProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.jaiselrahman.filepicker.model.MediaFile
7 | import com.simform.videoimageeditor.BaseActivity
8 | import com.simform.videoimageeditor.R
9 | import com.simform.videooperations.CallBackOfQuery
10 | import com.simform.videooperations.Common
11 | import com.simform.videooperations.Common.BITRATE_128
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.LogMessage
15 | import kotlinx.android.synthetic.main.activity_compress_audio.btnAudioPath
16 | import kotlinx.android.synthetic.main.activity_compress_audio.btnChange
17 | import kotlinx.android.synthetic.main.activity_compress_audio.mProgressView
18 | import kotlinx.android.synthetic.main.activity_compress_audio.tvInputPathAudio
19 | import kotlinx.android.synthetic.main.activity_compress_audio.tvOutputPath
20 |
21 | class CompressAudioActivity : BaseActivity(R.layout.activity_compress_audio, R.string.compress_audio) {
22 | private var isInputAudioSelected: Boolean = false
23 | override fun initialization() {
24 | btnAudioPath.setOnClickListener(this)
25 | btnChange.setOnClickListener(this)
26 | }
27 |
28 | override fun onClick(v: View?) {
29 | when (v?.id) {
30 | R.id.btnAudioPath -> {
31 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
32 | }
33 | R.id.btnChange -> {
34 | mediaFiles?.size?.let {
35 | if (!isInputAudioSelected) {
36 | Toast.makeText(this, getString(R.string.validation_of_audio), Toast.LENGTH_SHORT).show()
37 | return
38 | }
39 | }
40 | processStart()
41 | compressAudioProcess()
42 | }
43 | }
44 | }
45 |
46 | private fun compressAudioProcess() {
47 | val outputPath = Common.getFilePath(this, Common.MP3)
48 | val query = ffmpegQueryExtension.compressAudio(inputAudioPath = tvInputPathAudio.text.toString(), bitrate = BITRATE_128, output = outputPath)
49 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
50 | override fun process(logMessage: LogMessage) {
51 | tvOutputPath.text = logMessage.text
52 | }
53 |
54 | override fun success() {
55 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
56 | processStop()
57 | }
58 |
59 | override fun cancel() {
60 | processStop()
61 | }
62 |
63 | override fun failed() {
64 | processStop()
65 | }
66 | })
67 | }
68 |
69 | private fun processStop() {
70 | btnAudioPath.isEnabled = true
71 | btnChange.isEnabled = true
72 | mProgressView.visibility = View.GONE
73 | }
74 |
75 | private fun processStart() {
76 | btnAudioPath.isEnabled = false
77 | btnChange.isEnabled = false
78 | mProgressView.visibility = View.VISIBLE
79 | }
80 |
81 | @SuppressLint("NewApi")
82 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
83 | when (requestCode) {
84 | Common.AUDIO_FILE_REQUEST_CODE -> {
85 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
86 | tvInputPathAudio.text = mediaFiles[0].path
87 | isInputAudioSelected = true
88 | } else {
89 | Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
90 | }
91 | }
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/AspectRatioActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.jaiselrahman.filepicker.model.MediaFile
7 | import com.simform.videoimageeditor.BaseActivity
8 | import com.simform.videoimageeditor.R
9 | import com.simform.videooperations.CallBackOfQuery
10 | import com.simform.videooperations.Common
11 | import com.simform.videooperations.Common.RATIO_1
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.LogMessage
15 | import kotlinx.android.synthetic.main.activity_aspect_ratio.btnAspectRatio
16 | import kotlinx.android.synthetic.main.activity_aspect_ratio.btnVideoPath
17 | import kotlinx.android.synthetic.main.activity_aspect_ratio.mProgressView
18 | import kotlinx.android.synthetic.main.activity_aspect_ratio.tvInputPathVideo
19 | import kotlinx.android.synthetic.main.activity_aspect_ratio.tvOutputPath
20 |
21 | class AspectRatioActivity : BaseActivity(R.layout.activity_aspect_ratio, R.string.apply_aspect_ratio) {
22 | private var isInputVideoSelected: Boolean = false
23 | override fun initialization() {
24 | btnVideoPath.setOnClickListener(this)
25 | btnAspectRatio.setOnClickListener(this)
26 | }
27 |
28 | override fun onClick(v: View?) {
29 | when (v?.id) {
30 | R.id.btnVideoPath -> {
31 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
32 | }
33 | R.id.btnAspectRatio -> {
34 | when {
35 | !isInputVideoSelected -> {
36 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
37 | }
38 | else -> {
39 | processStart()
40 | applyRatioProcess()
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 | private fun applyRatioProcess() {
48 | val outputPath = Common.getFilePath(this, Common.VIDEO)
49 | val query = ffmpegQueryExtension.applyRatio(tvInputPathVideo.text.toString(), RATIO_1, outputPath)
50 |
51 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
52 | override fun process(logMessage: LogMessage) {
53 | tvOutputPath.text = logMessage.text
54 | }
55 |
56 | override fun success() {
57 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
58 | processStop()
59 | }
60 |
61 | override fun cancel() {
62 | processStop()
63 | }
64 |
65 | override fun failed() {
66 | processStop()
67 | }
68 |
69 | })
70 | }
71 |
72 | @SuppressLint("NewApi")
73 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
74 | when (requestCode) {
75 | Common.VIDEO_FILE_REQUEST_CODE -> {
76 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
77 | tvInputPathVideo.text = mediaFiles[0].path
78 | isInputVideoSelected = true
79 | } else {
80 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
81 | }
82 | }
83 | }
84 | }
85 |
86 | private fun processStop() {
87 | btnVideoPath.isEnabled = true
88 | btnAspectRatio.isEnabled = true
89 | mProgressView.visibility = View.GONE
90 | }
91 |
92 | private fun processStart() {
93 | btnVideoPath.isEnabled = false
94 | btnAspectRatio.isEnabled = false
95 | mProgressView.visibility = View.VISIBLE
96 | }
97 |
98 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/RemoveAudioFromVideoActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.jaiselrahman.filepicker.model.MediaFile
7 | import com.simform.videoimageeditor.BaseActivity
8 | import com.simform.videoimageeditor.R
9 | import com.simform.videooperations.CallBackOfQuery
10 | import com.simform.videooperations.Common
11 | import com.simform.videooperations.FFmpegCallBack
12 | import com.simform.videooperations.FFmpegQueryExtension
13 | import com.simform.videooperations.LogMessage
14 | import kotlinx.android.synthetic.main.activity_remove_audio_from_video.btnRemove
15 | import kotlinx.android.synthetic.main.activity_remove_audio_from_video.btnVideoPath
16 | import kotlinx.android.synthetic.main.activity_remove_audio_from_video.mProgressView
17 | import kotlinx.android.synthetic.main.activity_remove_audio_from_video.tvInputPathVideo
18 | import kotlinx.android.synthetic.main.activity_remove_audio_from_video.tvOutputPath
19 |
20 | class RemoveAudioFromVideoActivity : BaseActivity(R.layout.activity_remove_audio_from_video, R.string.audio_remove_from_video) {
21 | private var isInputVideoSelected: Boolean = false
22 | override fun initialization() {
23 | btnVideoPath.setOnClickListener(this)
24 | btnRemove.setOnClickListener(this)
25 | }
26 |
27 | override fun onClick(v: View?) {
28 | when (v?.id) {
29 | R.id.btnVideoPath -> {
30 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
31 | }
32 | R.id.btnRemove -> {
33 | when {
34 | !isInputVideoSelected -> {
35 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
36 | }
37 | else -> {
38 | processStart()
39 | removeAudioProcess()
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
46 | private fun removeAudioProcess() {
47 | val outputPath = Common.getFilePath(this, Common.VIDEO)
48 | val query = ffmpegQueryExtension.removeAudioFromVideo(tvInputPathVideo.text.toString(), outputPath)
49 |
50 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
51 | override fun process(logMessage: LogMessage) {
52 | tvOutputPath.text = logMessage.text
53 | }
54 |
55 | override fun success() {
56 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
57 | processStop()
58 | }
59 |
60 | override fun cancel() {
61 | processStop()
62 | }
63 |
64 | override fun failed() {
65 | processStop()
66 | }
67 |
68 | })
69 | }
70 |
71 | @SuppressLint("NewApi")
72 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
73 | when (requestCode) {
74 | Common.VIDEO_FILE_REQUEST_CODE -> {
75 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
76 | tvInputPathVideo.text = mediaFiles[0].path
77 | isInputVideoSelected = true
78 | } else {
79 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
80 | }
81 | }
82 | }
83 | }
84 |
85 | private fun processStop() {
86 | btnVideoPath.isEnabled = true
87 | btnRemove.isEnabled = true
88 | mProgressView.visibility = View.GONE
89 | }
90 |
91 | private fun processStart() {
92 | btnVideoPath.isEnabled = false
93 | btnRemove.isEnabled = false
94 | mProgressView.visibility = View.VISIBLE
95 | }
96 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/FastAndSlowAudioActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.otherFFMPEGProcessActivity
2 |
3 | import android.view.View
4 | import android.widget.Toast
5 | import com.jaiselrahman.filepicker.model.MediaFile
6 | import com.simform.videoimageeditor.BaseActivity
7 | import com.simform.videoimageeditor.R
8 | import com.simform.videooperations.CallBackOfQuery
9 | import com.simform.videooperations.Common
10 | import com.simform.videooperations.FFmpegCallBack
11 | import com.simform.videooperations.FFmpegQueryExtension
12 | import com.simform.videooperations.LogMessage
13 | import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.btnAudioPath
14 | import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.btnMotion
15 | import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.mProgressView
16 | import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.motionType
17 | import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.tvInputPathAudio
18 | import kotlinx.android.synthetic.main.activity_fast_and_slow_audio.tvOutputPath
19 |
20 | class FastAndSlowAudioActivity : BaseActivity(R.layout.activity_fast_and_slow_audio, R.string.fast_slow_motion_video) {
21 | private var isInputAudioSelected: Boolean = false
22 | override fun initialization() {
23 | btnAudioPath.setOnClickListener(this)
24 | btnMotion.setOnClickListener(this)
25 | }
26 |
27 | override fun onClick(v: View?) {
28 | when (v?.id) {
29 | R.id.btnAudioPath -> {
30 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
31 | }
32 | R.id.btnMotion -> {
33 | when {
34 | !isInputAudioSelected -> {
35 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
36 | }
37 | else -> {
38 | processStart()
39 | motionProcess()
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
46 | private fun motionProcess() {
47 | val outputPath = Common.getFilePath(this, Common.MP3)
48 | var atempo = 2.0
49 | if (!motionType.isChecked) {
50 | atempo = 0.5
51 | }
52 | val query = ffmpegQueryExtension.audioMotion(tvInputPathAudio.text.toString(), outputPath, atempo)
53 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
54 | override fun process(logMessage: LogMessage) {
55 | tvOutputPath.text = logMessage.text
56 | }
57 |
58 | override fun success() {
59 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
60 | processStop()
61 | }
62 |
63 | override fun cancel() {
64 | processStop()
65 | }
66 |
67 | override fun failed() {
68 | processStop()
69 | }
70 | })
71 | }
72 |
73 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
74 | when (requestCode) {
75 | Common.AUDIO_FILE_REQUEST_CODE -> {
76 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
77 | tvInputPathAudio.text = mediaFiles[0].path
78 | isInputAudioSelected = true
79 | } else {
80 | Toast.makeText(this, getString(R.string.audio_not_selected_toast_message), Toast.LENGTH_SHORT).show()
81 | }
82 | }
83 | }
84 | }
85 |
86 | private fun processStop() {
87 | btnAudioPath.isEnabled = true
88 | btnMotion.isEnabled = true
89 | mProgressView.visibility = View.GONE
90 | }
91 |
92 | private fun processStart() {
93 | btnAudioPath.isEnabled = false
94 | btnMotion.isEnabled = false
95 | mProgressView.visibility = View.VISIBLE
96 | }
97 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/FastAndSlowVideoMotionActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.view.View
4 | import android.widget.Toast
5 | import com.jaiselrahman.filepicker.model.MediaFile
6 | import com.simform.videoimageeditor.BaseActivity
7 | import com.simform.videoimageeditor.R
8 | import com.simform.videooperations.CallBackOfQuery
9 | import com.simform.videooperations.Common
10 | import com.simform.videooperations.FFmpegCallBack
11 | import com.simform.videooperations.FFmpegQueryExtension
12 | import com.simform.videooperations.LogMessage
13 | import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.btnMotion
14 | import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.btnVideoPath
15 | import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.mProgressView
16 | import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.motionType
17 | import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.tvInputPathVideo
18 | import kotlinx.android.synthetic.main.activity_fast_and_slow_video_motion.tvOutputPath
19 |
20 | class FastAndSlowVideoMotionActivity : BaseActivity(R.layout.activity_fast_and_slow_video_motion, R.string.fast_slow_motion_video) {
21 | private var isInputVideoSelected: Boolean = false
22 | override fun initialization() {
23 | btnVideoPath.setOnClickListener(this)
24 | btnMotion.setOnClickListener(this)
25 | }
26 |
27 | override fun onClick(v: View?) {
28 | when (v?.id) {
29 | R.id.btnVideoPath -> {
30 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
31 | }
32 | R.id.btnMotion -> {
33 | when {
34 | !isInputVideoSelected -> {
35 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
36 | }
37 | else -> {
38 | processStart()
39 | motionProcess()
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
46 | private fun motionProcess() {
47 | val outputPath = Common.getFilePath(this, Common.VIDEO)
48 | var setpts = 0.5
49 | var atempo = 2.0
50 | if (!motionType.isChecked) {
51 | setpts = 2.0
52 | atempo = 0.5
53 | }
54 | val query = ffmpegQueryExtension.videoMotion(tvInputPathVideo.text.toString(), outputPath, setpts, atempo)
55 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
56 | override fun process(logMessage: LogMessage) {
57 | tvOutputPath.text = logMessage.text
58 | }
59 |
60 | override fun success() {
61 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
62 | processStop()
63 | }
64 |
65 | override fun cancel() {
66 | processStop()
67 | }
68 |
69 | override fun failed() {
70 | processStop()
71 | }
72 | })
73 | }
74 |
75 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
76 | when (requestCode) {
77 | Common.VIDEO_FILE_REQUEST_CODE -> {
78 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
79 | tvInputPathVideo.text = mediaFiles[0].path
80 | isInputVideoSelected = true
81 | } else {
82 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
83 | }
84 | }
85 | }
86 | }
87 |
88 | private fun processStop() {
89 | btnVideoPath.isEnabled = true
90 | btnMotion.isEnabled = true
91 | mProgressView.visibility = View.GONE
92 | }
93 |
94 | private fun processStart() {
95 | btnVideoPath.isEnabled = false
96 | btnMotion.isEnabled = false
97 | mProgressView.visibility = View.VISIBLE
98 | }
99 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ExtractImagesActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.jaiselrahman.filepicker.model.MediaFile
7 | import com.simform.videoimageeditor.BaseActivity
8 | import com.simform.videoimageeditor.R
9 | import com.simform.videooperations.CallBackOfQuery
10 | import com.simform.videooperations.Common
11 | import com.simform.videooperations.FFmpegCallBack
12 | import com.simform.videooperations.FFmpegQueryExtension
13 | import com.simform.videooperations.Statistics
14 | import java.io.File
15 | import kotlinx.android.synthetic.main.activity_extract_images.btnExtract
16 | import kotlinx.android.synthetic.main.activity_extract_images.btnVideoPath
17 | import kotlinx.android.synthetic.main.activity_extract_images.mProgressView
18 | import kotlinx.android.synthetic.main.activity_extract_images.tvInputPathVideo
19 | import kotlinx.android.synthetic.main.activity_extract_images.tvOutputPath
20 |
21 | class ExtractImagesActivity : BaseActivity(R.layout.activity_extract_images, R.string.extract_frame_from_video) {
22 | private var isInputVideoSelected: Boolean = false
23 | override fun initialization() {
24 | btnVideoPath.setOnClickListener(this)
25 | btnExtract.setOnClickListener(this)
26 | }
27 |
28 | override fun onClick(v: View?) {
29 | when (v?.id) {
30 | R.id.btnVideoPath -> {
31 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
32 | }
33 | R.id.btnExtract -> {
34 | when {
35 | !isInputVideoSelected -> {
36 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
37 | }
38 | else -> {
39 | processStart()
40 | extractProcess()
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 | @SuppressLint("NewApi")
48 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
49 | when (requestCode) {
50 | Common.VIDEO_FILE_REQUEST_CODE -> {
51 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
52 | tvInputPathVideo.text = mediaFiles[0].path
53 | isInputVideoSelected = true
54 | } else {
55 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
56 | }
57 | }
58 | }
59 | }
60 |
61 | @SuppressLint("SetTextI18n")
62 | private fun extractProcess() {
63 | val outputPath = Common.getFilePath(this, Common.IMAGE)
64 | val query = ffmpegQueryExtension.extractImages(tvInputPathVideo.text.toString(), outputPath, spaceOfFrame = 4f)
65 | var totalFramesExtracted = 0
66 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
67 | override fun statisticsProcess(statistics: Statistics) {
68 | totalFramesExtracted = statistics.videoFrameNumber
69 | tvOutputPath.text = "Frames : ${statistics.videoFrameNumber}"
70 | }
71 |
72 | override fun success() {
73 | tvOutputPath.text = "Output Directory : \n${File(getExternalFilesDir(Common.OUT_PUT_DIR).toString()).absolutePath} \n\nTotal Frames Extracted: $totalFramesExtracted"
74 | processStop()
75 | }
76 |
77 | override fun cancel() {
78 | processStop()
79 | }
80 |
81 | override fun failed() {
82 | processStop()
83 | }
84 | })
85 | }
86 |
87 | private fun processStop() {
88 | btnVideoPath.isEnabled = true
89 | btnExtract.isEnabled = true
90 | mProgressView.visibility = View.GONE
91 | }
92 |
93 | private fun processStart() {
94 | btnVideoPath.isEnabled = false
95 | btnExtract.isEnabled = false
96 | mProgressView.visibility = View.VISIBLE
97 | }
98 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_reverse.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
46 |
47 |
59 |
60 |
71 |
72 |
84 |
85 |
93 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fast_and_slow_video_motion.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
45 |
46 |
58 |
59 |
70 |
71 |
83 |
84 |
92 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fast_and_slow_audio.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
45 |
46 |
58 |
59 |
70 |
71 |
83 |
84 |
92 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_merge_audio_video.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
49 |
50 |
62 |
63 |
64 |
75 |
76 |
88 |
89 |
97 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_merge_image_and_mp3.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
49 |
50 |
62 |
63 |
64 |
75 |
76 |
88 |
89 |
97 |
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/middlewareActivity/VideoProcessActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.middlewareActivity
2 |
3 | import android.view.View
4 | import com.simform.videoimageeditor.BaseActivity
5 | import com.simform.videoimageeditor.R
6 | import com.simform.videoimageeditor.videoProcessActivity.*
7 | import kotlinx.android.synthetic.main.activity_video_process.*
8 |
9 | /**
10 | * Created by Ashvin Vavaliya on 29,December,2020
11 | * Simform Solutions Pvt Ltd.
12 | */
13 | class VideoProcessActivity : BaseActivity(R.layout.activity_video_process, R.string.video_operations) {
14 | override fun initialization() {
15 | supportActionBar?.title = getString(R.string.video_operations)
16 | btnCutVideo.setOnClickListener(this)
17 | btnImageToVideo.setOnClickListener(this)
18 | btnAddWaterMarkOnVideo.setOnClickListener(this)
19 | btnCombineImageVideo.setOnClickListener(this)
20 | btnCombineImages.setOnClickListener(this)
21 | btnCombineVideos.setOnClickListener(this)
22 | btnCompressVideo.setOnClickListener(this)
23 | btnExtractVideo.setOnClickListener(this)
24 | btnExtractAudio.setOnClickListener(this)
25 | btnMotion.setOnClickListener(this)
26 | btnReverseVideo.setOnClickListener(this)
27 | btnFadeInFadeOutVideo.setOnClickListener(this)
28 | btnVideoConvertIntoGIF.setOnClickListener(this)
29 | btnVideoRotateFlip.setOnClickListener(this)
30 | btnMergeVideoAndAudio.setOnClickListener(this)
31 | btnAddTextOnVideo.setOnClickListener(this)
32 | btnRemoveAudioFromVideo.setOnClickListener(this)
33 | btnMergeImageAndAudio.setOnClickListener(this)
34 | btnSetAspectRatio.setOnClickListener(this)
35 | }
36 |
37 | override fun onClick(v: View?) {
38 | when (v?.id) {
39 | R.id.btnCutVideo -> {
40 | utils.openActivity(this, CutVideoUsingTimeActivity())
41 | }
42 | R.id.btnImageToVideo -> {
43 | utils.openActivity(this, ImageToVideoConvertActivity())
44 | }
45 | R.id.btnAddWaterMarkOnVideo -> {
46 | utils.openActivity(this, AddWaterMarkOnVideoActivity())
47 | }
48 | R.id.btnCombineImageVideo -> {
49 | utils.openActivity(this, CombineImageAndVideoActivity())
50 | }
51 | R.id.btnCombineImages -> {
52 | utils.openActivity(this, CombineImagesActivity())
53 | }
54 | R.id.btnCombineVideos -> {
55 | utils.openActivity(this, CombineVideosActivity())
56 | }
57 | R.id.btnCompressVideo -> {
58 | utils.openActivity(this, CompressVideoActivity())
59 | }
60 | R.id.btnExtractVideo -> {
61 | utils.openActivity(this, ExtractImagesActivity())
62 | }
63 | R.id.btnExtractAudio -> {
64 | utils.openActivity(this, ExtractAudioActivity())
65 | }
66 | R.id.btnMotion -> {
67 | utils.openActivity(this, FastAndSlowVideoMotionActivity())
68 | }
69 | R.id.btnReverseVideo -> {
70 | utils.openActivity(this, ReverseVideoActivity())
71 | }
72 | R.id.btnFadeInFadeOutVideo -> {
73 | utils.openActivity(this, VideoFadeInFadeOutActivity())
74 | }
75 | R.id.btnVideoConvertIntoGIF -> {
76 | utils.openActivity(this, VideoToGifActivity())
77 | }
78 | R.id.btnVideoRotateFlip -> {
79 | utils.openActivity(this, VideoRotateFlipActivity())
80 | }
81 | R.id.btnMergeVideoAndAudio -> {
82 | utils.openActivity(this, MergeAudioVideoActivity())
83 | }
84 | R.id.btnAddTextOnVideo -> {
85 | utils.openActivity(this, AddTextOnVideoActivity())
86 | }
87 | R.id.btnRemoveAudioFromVideo -> {
88 | utils.openActivity(this, RemoveAudioFromVideoActivity())
89 | }
90 | R.id.btnMergeImageAndAudio -> {
91 | utils.openActivity(this, MergeImageAndMP3Activity())
92 | }
93 | R.id.btnSetAspectRatio -> {
94 | utils.openActivity(this, AspectRatioActivity())
95 | }
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/ImageToVideoConvertActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.text.TextUtils
5 | import android.view.View
6 | import android.widget.Toast
7 | import com.jaiselrahman.filepicker.model.MediaFile
8 | import com.simform.videoimageeditor.BaseActivity
9 | import com.simform.videoimageeditor.R
10 | import com.simform.videooperations.CallBackOfQuery
11 | import com.simform.videooperations.Common
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.ISize
15 | import com.simform.videooperations.LogMessage
16 | import com.simform.videooperations.SizeOfImage
17 | import kotlinx.android.synthetic.main.activity_image_to_video_convert.btnConvert
18 | import kotlinx.android.synthetic.main.activity_image_to_video_convert.btnImagePath
19 | import kotlinx.android.synthetic.main.activity_image_to_video_convert.edtSecond
20 | import kotlinx.android.synthetic.main.activity_image_to_video_convert.mProgressView
21 | import kotlinx.android.synthetic.main.activity_image_to_video_convert.tvInputPath
22 | import kotlinx.android.synthetic.main.activity_image_to_video_convert.tvOutputPath
23 |
24 | class ImageToVideoConvertActivity : BaseActivity(R.layout.activity_image_to_video_convert, R.string.image_to_video) {
25 | private var isFileSelected: Boolean = false
26 | override fun initialization() {
27 | btnImagePath.setOnClickListener(this)
28 | btnConvert.setOnClickListener(this)
29 | }
30 |
31 | override fun onClick(v: View?) {
32 | when (v?.id) {
33 | R.id.btnImagePath -> {
34 | Common.selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
35 | }
36 | R.id.btnConvert -> {
37 | when {
38 | !isFileSelected -> {
39 | Toast.makeText(this, getString(R.string.input_image_validate_message), Toast.LENGTH_SHORT).show()
40 | }
41 | TextUtils.isEmpty(edtSecond.text.toString().trim()) || edtSecond.text.toString().trim().toInt() == 0 -> {
42 | Toast.makeText(this, getString(R.string.please_enter_second), Toast.LENGTH_SHORT).show()
43 | }
44 | else -> {
45 | processStart()
46 | createVideo()
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
53 | @SuppressLint("NewApi")
54 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
55 | if (requestCode == Common.IMAGE_FILE_REQUEST_CODE) {
56 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
57 | isFileSelected = true
58 | tvInputPath.text = mediaFiles[0].path
59 | } else {
60 | isFileSelected = false
61 | Toast.makeText(this, getString(R.string.image_not_selected_toast_message), Toast.LENGTH_SHORT).show()
62 | }
63 | }
64 | }
65 |
66 | private fun createVideo() {
67 | val outputPath = Common.getFilePath(this, Common.VIDEO)
68 | val size: ISize = SizeOfImage(tvInputPath.text.toString())
69 | val query = ffmpegQueryExtension.imageToVideo(tvInputPath.text.toString(), outputPath, edtSecond.text.toString().toInt(), size.width(), size.height())
70 |
71 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
72 | override fun process(logMessage: LogMessage) {
73 | tvOutputPath.text = logMessage.text
74 | }
75 |
76 | override fun success() {
77 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
78 | processStop()
79 | }
80 |
81 | override fun cancel() {
82 | processStop()
83 | }
84 |
85 | override fun failed() {
86 | processStop()
87 | }
88 |
89 | })
90 | }
91 |
92 | private fun processStop() {
93 | btnImagePath.isEnabled = true
94 | btnConvert.isEnabled = true
95 | mProgressView.visibility = View.GONE
96 | }
97 |
98 | private fun processStart() {
99 | btnImagePath.isEnabled = false
100 | btnConvert.isEnabled = false
101 | mProgressView.visibility = View.VISIBLE
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/otherFFMPEGProcessActivity/AudiosMergeActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.otherFFMPEGProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.jaiselrahman.filepicker.model.MediaFile
7 | import com.simform.videoimageeditor.BaseActivity
8 | import com.simform.videoimageeditor.R
9 | import com.simform.videooperations.CallBackOfQuery
10 | import com.simform.videooperations.Common
11 | import com.simform.videooperations.Common.DURATION_FIRST
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.LogMessage
15 | import com.simform.videooperations.Paths
16 | import kotlinx.android.synthetic.main.activity_audios_merge.btnAudioPath
17 | import kotlinx.android.synthetic.main.activity_audios_merge.btnMerge
18 | import kotlinx.android.synthetic.main.activity_audios_merge.mProgressView
19 | import kotlinx.android.synthetic.main.activity_audios_merge.tvInputPathAudio
20 | import kotlinx.android.synthetic.main.activity_audios_merge.tvOutputPath
21 |
22 | class AudiosMergeActivity : BaseActivity(R.layout.activity_audios_merge, R.string.merge_audios) {
23 | private var isInputAudioSelected: Boolean = false
24 | override fun initialization() {
25 | btnAudioPath.setOnClickListener(this)
26 | btnMerge.setOnClickListener(this)
27 | }
28 |
29 | override fun onClick(v: View?) {
30 | when (v?.id) {
31 | R.id.btnAudioPath -> {
32 | Common.selectFile(this, maxSelection = 10, isImageSelection = false, isAudioSelection = true)
33 | }
34 | R.id.btnMerge -> {
35 | mediaFiles?.size?.let {
36 | if (it < 2 || !isInputAudioSelected) {
37 | Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
38 | return
39 | }
40 | }
41 | processStart()
42 | mergeAudioProcess()
43 | }
44 | }
45 | }
46 |
47 | private fun mergeAudioProcess() {
48 | val outputPath = Common.getFilePath(this, Common.MP3)
49 | val pathsList = ArrayList()
50 | mediaFiles?.let {
51 | for (element in it) {
52 | val paths = Paths()
53 | paths.filePath = element.path
54 | paths.isImageFile = true
55 | pathsList.add(paths)
56 | }
57 |
58 | val query = ffmpegQueryExtension.mergeAudios(pathsList, DURATION_FIRST, outputPath)
59 |
60 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
61 | override fun process(logMessage: LogMessage) {
62 | tvOutputPath.text = logMessage.text
63 | }
64 |
65 | override fun success() {
66 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
67 | processStop()
68 | }
69 |
70 | override fun cancel() {
71 | processStop()
72 | }
73 |
74 | override fun failed() {
75 | processStop()
76 | }
77 | })
78 | }
79 | }
80 |
81 | private fun processStop() {
82 | btnAudioPath.isEnabled = true
83 | btnMerge.isEnabled = true
84 | mProgressView.visibility = View.GONE
85 | }
86 |
87 | private fun processStart() {
88 | btnAudioPath.isEnabled = false
89 | btnMerge.isEnabled = false
90 | mProgressView.visibility = View.VISIBLE
91 | }
92 |
93 | @SuppressLint("NewApi")
94 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
95 | when (requestCode) {
96 | Common.AUDIO_FILE_REQUEST_CODE -> {
97 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
98 | val size: Int = mediaFiles.size
99 | if (size > 1) {
100 | tvInputPathAudio.text = "$size Audio selected"
101 | isInputAudioSelected = true
102 | } else {
103 | Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
104 | }
105 | } else {
106 | Toast.makeText(this, getString(R.string.min_audio_selection_validation), Toast.LENGTH_SHORT).show()
107 | }
108 | }
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CompressVideoActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.media.MediaMetadataRetriever
5 | import android.view.View
6 | import android.widget.Toast
7 | import com.jaiselrahman.filepicker.model.MediaFile
8 | import com.simform.videoimageeditor.BaseActivity
9 | import com.simform.videoimageeditor.R
10 | import com.simform.videooperations.CallBackOfQuery
11 | import com.simform.videooperations.Common
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.LogMessage
15 | import java.io.File
16 | import java.util.concurrent.CompletableFuture
17 | import kotlinx.android.synthetic.main.activity_compress_video.btnCompress
18 | import kotlinx.android.synthetic.main.activity_compress_video.btnVideoPath
19 | import kotlinx.android.synthetic.main.activity_compress_video.inputFileSize
20 | import kotlinx.android.synthetic.main.activity_compress_video.mProgressView
21 | import kotlinx.android.synthetic.main.activity_compress_video.tvInputPathVideo
22 | import kotlinx.android.synthetic.main.activity_compress_video.tvOutputPath
23 |
24 | class CompressVideoActivity : BaseActivity(R.layout.activity_compress_video, R.string.compress_a_video) {
25 | private var isInputVideoSelected: Boolean = false
26 |
27 | override fun initialization() {
28 | btnVideoPath.setOnClickListener(this)
29 | btnCompress.setOnClickListener(this)
30 | }
31 |
32 | override fun onClick(v: View?) {
33 | when (v?.id) {
34 | R.id.btnVideoPath -> {
35 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
36 | }
37 | R.id.btnCompress -> {
38 | when {
39 | !isInputVideoSelected -> {
40 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
41 | }
42 | else -> {
43 | processStart()
44 | compressProcess()
45 | }
46 | }
47 | }
48 | }
49 | }
50 |
51 | @SuppressLint("NewApi")
52 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
53 | when (requestCode) {
54 | Common.VIDEO_FILE_REQUEST_CODE -> {
55 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
56 | tvInputPathVideo.text = mediaFiles[0].path
57 | isInputVideoSelected = true
58 | CompletableFuture.runAsync {
59 | retriever = MediaMetadataRetriever()
60 | retriever?.setDataSource(tvInputPathVideo.text.toString())
61 | val bit = retriever?.frameAtTime
62 | width = bit?.width
63 | height = bit?.height
64 | }
65 | inputFileSize.text = "Input file Size : ${Common.getFileSize(File(tvInputPathVideo.text.toString()))}"
66 | } else {
67 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
68 | }
69 | }
70 | }
71 | }
72 |
73 | private fun compressProcess() {
74 | val outputPath = Common.getFilePath(this, Common.VIDEO)
75 | val query = ffmpegQueryExtension.compressor(tvInputPathVideo.text.toString(), width, height, outputPath)
76 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
77 | override fun process(logMessage: LogMessage) {
78 | tvOutputPath.text = logMessage.text
79 | }
80 |
81 | @SuppressLint("SetTextI18n")
82 | override fun success() {
83 | tvOutputPath.text = String.format(getString(R.string.output_path_with_size), outputPath, Common.getFileSize(File(outputPath)))
84 | processStop()
85 | }
86 |
87 | override fun cancel() {
88 | processStop()
89 | }
90 |
91 | override fun failed() {
92 | processStop()
93 | }
94 | })
95 | }
96 |
97 | private fun processStop() {
98 | btnVideoPath.isEnabled = true
99 | btnCompress.isEnabled = true
100 | mProgressView.visibility = View.GONE
101 | }
102 |
103 | private fun processStart() {
104 | btnVideoPath.isEnabled = false
105 | btnCompress.isEnabled = false
106 | mProgressView.visibility = View.VISIBLE
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_merge_image_and_video.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
49 |
50 |
61 |
62 |
74 |
75 |
86 |
87 |
99 |
100 |
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/VideoFadeInFadeOutActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.media.MediaMetadataRetriever
5 | import android.view.View
6 | import android.widget.Toast
7 | import com.jaiselrahman.filepicker.model.MediaFile
8 | import com.simform.videoimageeditor.BaseActivity
9 | import com.simform.videoimageeditor.R
10 | import com.simform.videooperations.CallBackOfQuery
11 | import com.simform.videooperations.Common
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.LogMessage
15 | import java.util.concurrent.CompletableFuture
16 | import java.util.concurrent.TimeUnit
17 | import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.btnApplyFadeInFadeOut
18 | import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.btnVideoPath
19 | import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.mProgressView
20 | import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.tvInputPathVideo
21 | import kotlinx.android.synthetic.main.activity_video_fade_in_fade_out.tvOutputPath
22 |
23 | class VideoFadeInFadeOutActivity : BaseActivity(R.layout.activity_video_fade_in_fade_out, R.string.video_fade_in_and_fade_out) {
24 | private var isInputVideoSelected: Boolean = false
25 | private var selectedVideoDurationInSecond = 0L
26 | override fun initialization() {
27 | btnVideoPath.setOnClickListener(this)
28 | btnApplyFadeInFadeOut.setOnClickListener(this)
29 | }
30 |
31 | override fun onClick(v: View?) {
32 | when (v?.id) {
33 | R.id.btnVideoPath -> {
34 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
35 | }
36 | R.id.btnApplyFadeInFadeOut -> {
37 | when {
38 | !isInputVideoSelected -> {
39 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
40 | }
41 | else -> {
42 | processStart()
43 | fadeInFadeOutProcess()
44 | }
45 | }
46 | }
47 | }
48 | }
49 |
50 | private fun fadeInFadeOutProcess() {
51 | val outputPath = Common.getFilePath(this, Common.VIDEO)
52 | val query = ffmpegQueryExtension.videoFadeInFadeOut(tvInputPathVideo.text.toString(), selectedVideoDurationInSecond, fadeInEndSeconds = 3, fadeOutStartSeconds = 3, output = outputPath)
53 |
54 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
55 | override fun process(logMessage: LogMessage) {
56 | tvOutputPath.text = logMessage.text
57 | }
58 |
59 | override fun success() {
60 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
61 | processStop()
62 | }
63 |
64 | override fun cancel() {
65 | processStop()
66 | }
67 |
68 | override fun failed() {
69 | processStop()
70 | }
71 |
72 | })
73 | }
74 |
75 | @SuppressLint("NewApi")
76 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
77 | when (requestCode) {
78 | Common.VIDEO_FILE_REQUEST_CODE -> {
79 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
80 | tvInputPathVideo.text = mediaFiles[0].path
81 | isInputVideoSelected = true
82 | CompletableFuture.runAsync {
83 | retriever = MediaMetadataRetriever()
84 | retriever?.setDataSource(tvInputPathVideo.text.toString())
85 | val time = retriever?.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
86 | time?.toLong()?.let {
87 | selectedVideoDurationInSecond = TimeUnit.MILLISECONDS.toSeconds(it)
88 | }
89 | }
90 | } else {
91 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
92 | }
93 | }
94 | }
95 | }
96 |
97 | private fun processStop() {
98 | btnVideoPath.isEnabled = true
99 | btnApplyFadeInFadeOut.isEnabled = true
100 | mProgressView.visibility = View.GONE
101 | }
102 |
103 | private fun processStart() {
104 | btnVideoPath.isEnabled = false
105 | btnApplyFadeInFadeOut.isEnabled = false
106 | mProgressView.visibility = View.VISIBLE
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineImagesActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.text.TextUtils
5 | import android.view.View
6 | import android.widget.Toast
7 | import com.jaiselrahman.filepicker.model.MediaFile
8 | import com.simform.videoimageeditor.BaseActivity
9 | import com.simform.videoimageeditor.R
10 | import com.simform.videooperations.CallBackOfQuery
11 | import com.simform.videooperations.Common
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.LogMessage
15 | import com.simform.videooperations.Paths
16 | import kotlinx.android.synthetic.main.activity_combine_images.btnCombine
17 | import kotlinx.android.synthetic.main.activity_combine_images.btnImagePath
18 | import kotlinx.android.synthetic.main.activity_combine_images.edtSecond
19 | import kotlinx.android.synthetic.main.activity_combine_images.mProgressView
20 | import kotlinx.android.synthetic.main.activity_combine_images.tvInputPathImage
21 | import kotlinx.android.synthetic.main.activity_combine_images.tvOutputPath
22 |
23 | class CombineImagesActivity : BaseActivity(R.layout.activity_combine_images, R.string.merge_images) {
24 | private var isImageSelected: Boolean = false
25 | override fun initialization() {
26 | btnImagePath.setOnClickListener(this)
27 | btnCombine.setOnClickListener(this)
28 | }
29 |
30 | override fun onClick(v: View?) {
31 | when (v?.id) {
32 | R.id.btnImagePath -> {
33 | Common.selectFile(this, maxSelection = 25, isImageSelection = true, isAudioSelection = false)
34 | }
35 | R.id.btnCombine -> {
36 | when {
37 | !isImageSelected -> {
38 | Toast.makeText(this, getString(R.string.input_image_validate_message), Toast.LENGTH_SHORT).show()
39 | }
40 | TextUtils.isEmpty(edtSecond.text.toString().trim()) || edtSecond.text.toString().trim().toInt() == 0 -> {
41 | Toast.makeText(this, getString(R.string.please_enter_second), Toast.LENGTH_SHORT).show()
42 | }
43 | else -> {
44 | processStart()
45 | combineImagesProcess()
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 | @SuppressLint("NewApi", "SetTextI18n")
53 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
54 | when (requestCode) {
55 | Common.IMAGE_FILE_REQUEST_CODE -> {
56 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
57 | val size: Int = mediaFiles.size
58 | tvInputPathImage.text = "$size" + (if (size == 1) " Image " else " Images ") + "selected"
59 | isImageSelected = true
60 | } else {
61 | Toast.makeText(this, getString(R.string.image_not_selected_toast_message), Toast.LENGTH_SHORT).show()
62 | }
63 | }
64 | }
65 | }
66 |
67 | private fun processStop() {
68 | btnImagePath.isEnabled = true
69 | btnCombine.isEnabled = true
70 | mProgressView.visibility = View.GONE
71 | }
72 |
73 | private fun processStart() {
74 | btnImagePath.isEnabled = false
75 | btnCombine.isEnabled = false
76 | mProgressView.visibility = View.VISIBLE
77 | }
78 |
79 | private fun combineImagesProcess() {
80 | val outputPath = Common.getFilePath(this, Common.VIDEO)
81 | val pathsList = ArrayList()
82 | mediaFiles?.let {
83 | for (element in it) {
84 | val paths = Paths()
85 | paths.filePath = element.path
86 | paths.isImageFile = true
87 | pathsList.add(paths)
88 | }
89 |
90 | val query = ffmpegQueryExtension.combineImagesAndVideos(pathsList, 640, 480, edtSecond.text.toString(), outputPath)
91 |
92 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
93 | override fun process(logMessage: LogMessage) {
94 | tvOutputPath.text = logMessage.text
95 | }
96 |
97 | override fun success() {
98 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
99 | processStop()
100 | }
101 |
102 | override fun cancel() {
103 | processStop()
104 | }
105 |
106 | override fun failed() {
107 | processStop()
108 | }
109 | })
110 | }
111 | }
112 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeAudioVideoActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.view.View
4 | import android.widget.Toast
5 | import com.jaiselrahman.filepicker.model.MediaFile
6 | import com.simform.videoimageeditor.BaseActivity
7 | import com.simform.videoimageeditor.R
8 | import com.simform.videooperations.CallBackOfQuery
9 | import com.simform.videooperations.Common
10 | import com.simform.videooperations.FFmpegCallBack
11 | import com.simform.videooperations.FFmpegQueryExtension
12 | import com.simform.videooperations.LogMessage
13 | import kotlinx.android.synthetic.main.activity_merge_audio_video.btnMerge
14 | import kotlinx.android.synthetic.main.activity_merge_audio_video.btnMp3Path
15 | import kotlinx.android.synthetic.main.activity_merge_audio_video.btnVideoPath
16 | import kotlinx.android.synthetic.main.activity_merge_audio_video.mProgressView
17 | import kotlinx.android.synthetic.main.activity_merge_audio_video.tvInputPathAudio
18 | import kotlinx.android.synthetic.main.activity_merge_audio_video.tvInputPathVideo
19 | import kotlinx.android.synthetic.main.activity_merge_audio_video.tvOutputPath
20 |
21 | class MergeAudioVideoActivity : BaseActivity(R.layout.activity_merge_audio_video, R.string.merge_video_and_audio) {
22 | private var isInputVideoSelected: Boolean = false
23 | private var isInputAudioSelected: Boolean = false
24 | override fun initialization() {
25 | btnVideoPath.setOnClickListener(this)
26 | btnMp3Path.setOnClickListener(this)
27 | btnMerge.setOnClickListener(this)
28 | }
29 |
30 | override fun onClick(v: View?) {
31 | when (v?.id) {
32 | R.id.btnVideoPath -> {
33 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = false)
34 | }
35 | R.id.btnMp3Path -> {
36 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
37 | }
38 | R.id.btnMerge -> {
39 | when {
40 | !isInputVideoSelected -> {
41 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
42 | }
43 | !isInputAudioSelected -> {
44 | Toast.makeText(this, getString(R.string.please_select_input_audio), Toast.LENGTH_SHORT).show()
45 | }
46 | else -> {
47 | processStart()
48 | mergeProcess()
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | private fun mergeProcess() {
56 | val outputPath = Common.getFilePath(this, Common.VIDEO)
57 | val query = ffmpegQueryExtension.mergeAudioVideo(tvInputPathVideo.text.toString(), tvInputPathAudio.text.toString(), outputPath)
58 |
59 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
60 | override fun process(logMessage: LogMessage) {
61 | tvOutputPath.text = logMessage.text
62 | }
63 |
64 | override fun success() {
65 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
66 | processStop()
67 | }
68 |
69 | override fun cancel() {
70 | processStop()
71 | }
72 |
73 | override fun failed() {
74 | processStop()
75 | }
76 |
77 | })
78 | }
79 |
80 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
81 | when (requestCode) {
82 | Common.VIDEO_FILE_REQUEST_CODE -> {
83 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
84 | tvInputPathVideo.text = mediaFiles[0].path
85 | isInputVideoSelected = true
86 | } else {
87 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
88 | }
89 | }
90 | Common.AUDIO_FILE_REQUEST_CODE -> {
91 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
92 | tvInputPathAudio.text = mediaFiles[0].path
93 | isInputAudioSelected = true
94 | } else {
95 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
96 | }
97 | }
98 | }
99 | }
100 |
101 | private fun processStop() {
102 | btnVideoPath.isEnabled = true
103 | btnMp3Path.isEnabled = true
104 | btnMerge.isEnabled = true
105 | mProgressView.visibility = View.GONE
106 | }
107 |
108 | private fun processStart() {
109 | btnVideoPath.isEnabled = false
110 | btnMp3Path.isEnabled = false
111 | btnMerge.isEnabled = false
112 | mProgressView.visibility = View.VISIBLE
113 | }
114 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/MergeImageAndMP3Activity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.view.View
4 | import android.widget.Toast
5 | import com.jaiselrahman.filepicker.model.MediaFile
6 | import com.simform.videoimageeditor.BaseActivity
7 | import com.simform.videoimageeditor.R
8 | import com.simform.videooperations.CallBackOfQuery
9 | import com.simform.videooperations.Common
10 | import com.simform.videooperations.FFmpegCallBack
11 | import com.simform.videooperations.FFmpegQueryExtension
12 | import com.simform.videooperations.LogMessage
13 | import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.btnImagePath
14 | import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.btnMerge
15 | import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.btnMp3Path
16 | import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.mProgressView
17 | import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.tvInputPathAudio
18 | import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.tvInputPathImage
19 | import kotlinx.android.synthetic.main.activity_merge_image_and_mp3.tvOutputPath
20 |
21 | class MergeImageAndMP3Activity : BaseActivity(R.layout.activity_merge_image_and_mp3, R.string.merge_image_and_audio) {
22 | private var isInputImageSelected: Boolean = false
23 | private var isInputMP3Selected: Boolean = false
24 | override fun initialization() {
25 | btnImagePath.setOnClickListener(this)
26 | btnMp3Path.setOnClickListener(this)
27 | btnMerge.setOnClickListener(this)
28 | }
29 |
30 | override fun onClick(v: View?) {
31 | when (v?.id) {
32 | R.id.btnImagePath -> {
33 | Common.selectFile(this, maxSelection = 1, isImageSelection = true, isAudioSelection = false)
34 | }
35 | R.id.btnMp3Path -> {
36 | Common.selectFile(this, maxSelection = 1, isImageSelection = false, isAudioSelection = true)
37 | }
38 | R.id.btnMerge -> {
39 | when {
40 | !isInputImageSelected -> {
41 | Toast.makeText(this, getString(R.string.please_select_input_image), Toast.LENGTH_SHORT).show()
42 | }
43 | !isInputMP3Selected -> {
44 | Toast.makeText(this, getString(R.string.please_select_input_audio), Toast.LENGTH_SHORT).show()
45 | }
46 | else -> {
47 | processStart()
48 | mergeProcess()
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | private fun mergeProcess() {
56 | val outputPath = Common.getFilePath(this, Common.VIDEO)
57 | val query = ffmpegQueryExtension.mergeImageAndAudio(tvInputPathImage.text.toString(), tvInputPathAudio.text.toString(), outputPath)
58 |
59 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
60 | override fun process(logMessage: LogMessage) {
61 | tvOutputPath.text = logMessage.text
62 | }
63 |
64 | override fun success() {
65 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
66 | processStop()
67 | }
68 |
69 | override fun cancel() {
70 | processStop()
71 | }
72 |
73 | override fun failed() {
74 | processStop()
75 | }
76 |
77 | })
78 | }
79 |
80 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
81 | when (requestCode) {
82 | Common.IMAGE_FILE_REQUEST_CODE -> {
83 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
84 | tvInputPathImage.text = mediaFiles[0].path
85 | isInputImageSelected = true
86 | } else {
87 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
88 | }
89 | }
90 | Common.AUDIO_FILE_REQUEST_CODE -> {
91 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
92 | tvInputPathAudio.text = mediaFiles[0].path
93 | isInputMP3Selected = true
94 | } else {
95 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
96 | }
97 | }
98 | }
99 | }
100 |
101 | private fun processStop() {
102 | btnImagePath.isEnabled = true
103 | btnMp3Path.isEnabled = true
104 | btnMerge.isEnabled = true
105 | mProgressView.visibility = View.GONE
106 | }
107 |
108 | private fun processStart() {
109 | btnImagePath.isEnabled = false
110 | btnMp3Path.isEnabled = false
111 | btnMerge.isEnabled = false
112 | mProgressView.visibility = View.VISIBLE
113 | }
114 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/simform/videoimageeditor/videoProcessActivity/CombineVideosActivity.kt:
--------------------------------------------------------------------------------
1 | package com.simform.videoimageeditor.videoProcessActivity
2 |
3 | import android.annotation.SuppressLint
4 | import android.media.MediaMetadataRetriever
5 | import android.view.View
6 | import android.widget.Toast
7 | import com.jaiselrahman.filepicker.model.MediaFile
8 | import com.simform.videoimageeditor.BaseActivity
9 | import com.simform.videoimageeditor.R
10 | import com.simform.videooperations.CallBackOfQuery
11 | import com.simform.videooperations.Common
12 | import com.simform.videooperations.FFmpegCallBack
13 | import com.simform.videooperations.FFmpegQueryExtension
14 | import com.simform.videooperations.LogMessage
15 | import com.simform.videooperations.Paths
16 | import java.util.concurrent.CompletableFuture
17 | import kotlinx.android.synthetic.main.activity_combine_videos.btnCombine
18 | import kotlinx.android.synthetic.main.activity_combine_videos.btnVideoPath
19 | import kotlinx.android.synthetic.main.activity_combine_videos.mProgressView
20 | import kotlinx.android.synthetic.main.activity_combine_videos.tvInputPathImage
21 | import kotlinx.android.synthetic.main.activity_combine_videos.tvOutputPath
22 | import kotlinx.android.synthetic.main.activity_merge_image_and_video.tvInputPathVideo
23 |
24 | class CombineVideosActivity : BaseActivity(R.layout.activity_combine_videos, R.string.merge_videos) {
25 | private var isVideoSelected: Boolean = false
26 |
27 | override fun initialization() {
28 | btnVideoPath.setOnClickListener(this)
29 | btnCombine.setOnClickListener(this)
30 | }
31 |
32 | override fun onClick(v: View?) {
33 | when (v?.id) {
34 | R.id.btnVideoPath -> {
35 | Common.selectFile(this, maxSelection = 5, isImageSelection = false, isAudioSelection = false)
36 | }
37 | R.id.btnCombine -> {
38 | when {
39 | !isVideoSelected -> {
40 | Toast.makeText(this, getString(R.string.input_video_validate_message), Toast.LENGTH_SHORT).show()
41 | }
42 | else -> {
43 | processStart()
44 | combineVideosProcess()
45 | }
46 | }
47 | }
48 | }
49 | }
50 |
51 | @SuppressLint("NewApi", "SetTextI18n")
52 | override fun selectedFiles(mediaFiles: List?, requestCode: Int) {
53 | when (requestCode) {
54 | Common.VIDEO_FILE_REQUEST_CODE -> {
55 | if (mediaFiles != null && mediaFiles.isNotEmpty()) {
56 | val size: Int = mediaFiles.size
57 | tvInputPathImage.text = "$size" + (if (size == 1) " Video " else " Videos ") + "selected"
58 | isVideoSelected = true
59 | CompletableFuture.runAsync {
60 | retriever = MediaMetadataRetriever()
61 | retriever?.setDataSource(tvInputPathVideo.text.toString())
62 | val bit = retriever?.frameAtTime
63 | if (bit != null) {
64 | width = bit.width
65 | height = bit.height
66 | }
67 | }
68 | } else {
69 | Toast.makeText(this, getString(R.string.video_not_selected_toast_message), Toast.LENGTH_SHORT).show()
70 | }
71 | }
72 | }
73 | }
74 |
75 | private fun processStop() {
76 | btnVideoPath.isEnabled = true
77 | btnCombine.isEnabled = true
78 | mProgressView.visibility = View.GONE
79 | }
80 |
81 | private fun processStart() {
82 | btnVideoPath.isEnabled = false
83 | btnCombine.isEnabled = false
84 | mProgressView.visibility = View.VISIBLE
85 | }
86 |
87 | private fun combineVideosProcess() {
88 | val outputPath = Common.getFilePath(this, Common.VIDEO)
89 | val pathsList = ArrayList()
90 | mediaFiles?.let {
91 | for (element in it) {
92 | val paths = Paths()
93 | paths.filePath = element.path
94 | paths.isImageFile = false
95 | pathsList.add(paths)
96 | }
97 |
98 | val query = ffmpegQueryExtension.combineVideos(
99 | pathsList,
100 | width,
101 | height,
102 | outputPath
103 | )
104 | CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
105 | override fun process(logMessage: LogMessage) {
106 | tvOutputPath.text = logMessage.text
107 | }
108 |
109 | override fun success() {
110 | tvOutputPath.text = String.format(getString(R.string.output_path), outputPath)
111 | processStop()
112 | }
113 |
114 | override fun cancel() {
115 | processStop()
116 | }
117 |
118 | override fun failed() {
119 | processStop()
120 | }
121 | })
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_add_text_on_video.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
48 |
60 |
61 |
73 |
74 |
86 |
97 |
98 |
110 |
111 |
119 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_crop_audio.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
46 |
47 |
57 |
58 |
68 |
69 |
78 |
79 |
90 |
91 |
102 |
103 |
115 |
116 |
124 |
125 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
20 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
43 |
46 |
49 |
52 |
55 |
58 |
61 |
64 |
67 |
70 |
73 |
76 |
79 |
82 |
85 |
88 |
91 |
94 |
97 |
100 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_cut_video_using_time.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
46 |
47 |
57 |
58 |
68 |
69 |
78 |
79 |
90 |
91 |
102 |
103 |
115 |
116 |
124 |
125 |
--------------------------------------------------------------------------------