Skip to content

Commit f74462b

Browse files
authored
Merge pull request #4 from israel-fl/staging/v2.0.0
Staging/v2.0.0
2 parents 57bbd00 + a453cf3 commit f74462b

29 files changed

+1004
-818
lines changed

README.md

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
# bitmap2video
2-
Generate video from Bitmaps or a Canvas in Android.
2+
![](bitmap2video.gif)
3+
4+
Generate video from a Bitmap, Canvas, or resource drawable in Android.
35

46
Create mp4 video from Bitmaps or anything you can draw to a hardware accelerated Canvas. Pure, simple Android MediaCodec implementation. Requires no third party libs or NDK.
57

6-
Currently supports the MP4 container and both AVC/H264 and HEVC/H265. Easily extensable to other supported formats.
8+
Currently supports the MP4 container and both AVC/H264 and HEVC/H265. Easily extensible to other
9+
supported formats.
710

8-
Run the sample app or check out [CreateRunnable](app/src/main/java/com/homesoft/bitmap2video/CreateRunnable.java) for an example.
11+
Run the sample app or check out
12+
and [MainActivity](app/src/main/java/com/homesoft/bitmap2video/MainActivity.java)
13+
for an example.
914

15+
# Dependencies
1016
Add it in your root build.gradle at the end of repositories:
1117

1218
allprojects {
@@ -16,8 +22,113 @@ Add it in your root build.gradle at the end of repositories:
1622
}
1723
}
1824

19-
Add to your app dependancies:
25+
Add to your app dependencies:
2026

2127
dependencies {
22-
implementation 'com.github.dburckh:bitmap2video:1.0.0'
28+
implementation 'com.github.israel-fl:bitmap2video:2.0.0'
29+
}
30+
31+
32+
# Initialize library
33+
Simply create a `Muxer` object
34+
35+
```kotlin
36+
val muxer = Muxer(this@MainActivity, "/files/video.mp4")
37+
// and mux
38+
muxer.mux(imageArray)
39+
```
40+
41+
Use callbacks to listen for video completion:
42+
```kotlin
43+
muxer.setOnMuxingCompletedListener(object : MuxingCompletionListener {
44+
override fun onVideoSuccessful(file: File) {
45+
Log.d(TAG, "Video muxed - file path: ${file.absolutePath}")
46+
}
47+
48+
override fun onVideoError(error: Throwable) {
49+
Log.e(TAG, "There was an error muxing the video")
50+
}
51+
})
52+
53+
Thread(Runnable {
54+
muxer.mux(imageArray, R.raw.bensound_happyrock)
55+
}).start()
56+
```
57+
58+
Or use a co-routine by calling `muxAsync`:
59+
60+
```kotlin
61+
scope.launch {
62+
when (val result = muxer.muxAsync(imageArray, R.raw.bensound_happyrock)) {
63+
is MuxingSuccess -> {
64+
Log.i(TAG, "Video muxed - file path: ${result.file.absolutePath}")
65+
onMuxerCompleted()
66+
}
67+
is MuxingError -> {
68+
Log.e(TAG, "There was an error muxing the video")
69+
bt_make.isEnabled = true
70+
}
2371
}
72+
}
73+
```
74+
75+
### Passing a custom configuration object
76+
```kotlin
77+
val muxerConfig = MuxerConfig(this, 600, 600, 'video/avc', 3, 1F, 1500000)
78+
val muxer = Muxer(this@MainActivity, muxerConfig!!)
79+
// or
80+
muxer.setMuxerConfig(muxerConfig)
81+
```
82+
83+
#### Supported configuration
84+
- File object
85+
- video width
86+
- video height
87+
- Mimetype
88+
- Frames per image (how many seconds to display each image)
89+
- Frames per second
90+
- Bitrate
91+
- FrameMuxer (only MP4 included currently)
92+
- IFrame Interval
93+
94+
### Acceptable media types:
95+
The library currently supports `Bitmap`, `Canvas`, and drawable resources (`R.drawable.image1`)
96+
97+
### Adding Audio
98+
In order to add audio, pass in an audio track to the `mux` method.
99+
```kotlin
100+
muxer.mux(imageArray, R.raw.bensound_happyrock)
101+
```
102+
103+
### Convenience utility functions
104+
We provide a few functions to simplify a couple of tasks. These can be
105+
found as static methods under `FileUtils`
106+
107+
##### Get an `AssetFileDescriptor` from a raw resource
108+
`getFileDescriptor(Context context, int R.raw.sound_file)`
109+
110+
##### Get a `File` object for your video
111+
`getVideoFile(final Context context, final String fileName)`
112+
113+
##### Export the created file to other applications
114+
`shareVideo(Context context, File file, String mimeType)`
115+
116+
##### Note
117+
These utility functions use the library's `ContentProvider` with
118+
declared authorities and specified paths.
119+
120+
#### Declare your own ContentProvider
121+
You can change which provider is being used and specify your paths if
122+
you register your own against the manifest and create your own
123+
resources. You must then pass in the appropriate paths to the functions.
124+
See the library's
125+
[AndroidManifest](app/src/main/java/com/homesoft/bitmap2video/library/src/main/AndroidManifest.xml)
126+
for an example.
127+
128+
```java
129+
getVideoFile(Context context, String fileDir, String fileName)
130+
```
131+
and
132+
```java
133+
shareVideo(Context context, File file, String mimeType, String fileAuthority)
134+
```

app/build.gradle

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
apply plugin: 'com.android.application'
2+
apply plugin: 'kotlin-android'
3+
apply plugin: 'kotlin-android-extensions'
24

35
android {
46
compileSdkVersion 29
5-
buildToolsVersion "29.0.2"
7+
buildToolsVersion "29.0.3"
68
defaultConfig {
7-
applicationId "com.homesoft.bitmap2video"
8-
minSdkVersion 19
9+
applicationId "com.israelfl.bitmap2video"
10+
minSdkVersion 21
911
targetSdkVersion 29
1012
versionCode 1
1113
versionName "1.0"
@@ -20,6 +22,15 @@ android {
2022
}
2123

2224
dependencies {
23-
implementation project(path: ':library')
2425
implementation 'androidx.appcompat:appcompat:1.1.0'
26+
implementation (project(path: ':library')) {
27+
transitive = true
28+
}
29+
implementation 'androidx.core:core-ktx:1.3.0'
30+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
31+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
32+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1"
33+
}
34+
repositories {
35+
mavenCentral()
2536
}

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
34
package="com.homesoft.bitmap2video">
45

56
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
@@ -11,7 +12,8 @@
1112
android:roundIcon="@mipmap/ic_launcher_round"
1213
android:supportsRtl="true"
1314
android:theme="@style/AppTheme"
14-
>
15+
tools:ignore="GoogleAppIndexingWarning">
16+
1517
<activity android:name=".MainActivity">
1618
<intent-filter>
1719
<action android:name="android.intent.action.MAIN" />
@@ -21,4 +23,4 @@
2123
</activity>
2224
</application>
2325

24-
</manifest>
26+
</manifest>

app/src/main/java/com/homesoft/bitmap2video/CreateRunnable.java

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.homesoft.bitmap2video
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import android.content.res.AssetFileDescriptor
6+
import android.util.Log
7+
import androidx.annotation.RawRes
8+
import androidx.core.content.FileProvider
9+
import java.io.File
10+
11+
/*
12+
* Copyright (C) 2019 Israel Flores
13+
*
14+
* Licensed under the Apache License, Version 2.0 (the "License");
15+
* you may not use this file except in compliance with the License.
16+
* You may obtain a copy of the License at
17+
*
18+
* http://www.apache.org/licenses/LICENSE-2.0
19+
*
20+
* Unless required by applicable law or agreed to in writing, software
21+
* distributed under the License is distributed on an "AS IS" BASIS,
22+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23+
* See the License for the specific language governing permissions and
24+
* limitations under the License.
25+
*/
26+
27+
object FileUtils {
28+
private val TAG = FileUtils::class.java.simpleName
29+
private const val MEDIA_FILE_PATH = "media"
30+
private const val FILE_AUTHORITY = "com.homesoft.fileprovider"
31+
32+
/**
33+
* Get a [AssetFileDescriptor] from a raw resource
34+
*
35+
* @param context - activity or application context
36+
* @param rawAudioResource - resource from `R.raw`
37+
* @return - AssetFileDescriptor from resource
38+
*/
39+
fun getFileDescriptor(context: Context,
40+
@RawRes rawAudioResource: Int): AssetFileDescriptor {
41+
return context.resources.openRawResourceFd(rawAudioResource)
42+
}
43+
44+
/**
45+
* Get a File object where we will be storing the video
46+
*
47+
* @param context - Activity or application context
48+
* @param fileName - name of the file
49+
* @return - created file object at media/fileName
50+
*/
51+
fun getVideoFile(context: Context, fileName: String): File {
52+
return getVideoFile(context, MEDIA_FILE_PATH, fileName)
53+
}
54+
55+
/**
56+
* Get a File object where we will be storing the video
57+
*
58+
* @param context - Activity or application context
59+
* @param fileDir - name of directory where video file is stored
60+
* IMPORTANT: if setting this, you must provide the appropriate `paths`
61+
* in your xml resources {@see file_paths.xml}, as well as set up your
62+
* provider with the appropriate file paths.
63+
* @param fileName - name of the file
64+
* @return - created file object at fileDir/fileName
65+
*/
66+
fun getVideoFile(context: Context, fileDir: String,
67+
fileName: String): File {
68+
val mediaFolder = File(context.filesDir, fileDir)
69+
// Create the directory if it does not exist
70+
if (!mediaFolder.exists()) mediaFolder.mkdirs()
71+
Log.d(TAG, "Got folder at: " + mediaFolder.absolutePath)
72+
val file = File(mediaFolder, fileName)
73+
Log.d(TAG, "Got file at: " + file.absolutePath)
74+
return file
75+
}
76+
77+
/**
78+
* Creates an implicit intent to share the video file with any apps that accept it
79+
*
80+
* @param context - Activity or application context
81+
* @param file - File object (where video was saved)
82+
* @param mimeType - mime type of video as a string, can be retrieved from encoder
83+
* @return true if sharing was successful, false otherwise
84+
*/
85+
@JvmOverloads
86+
fun shareVideo(context: Context, file: File,
87+
mimeType: String, fileAuthority: String = FILE_AUTHORITY): Boolean {
88+
if (!file.exists()) {
89+
return false
90+
}
91+
Log.d(TAG, "Found file at " + file.absolutePath)
92+
val uri = FileProvider.getUriForFile(context, fileAuthority, file)
93+
val intent = Intent(Intent.ACTION_SEND)
94+
intent.putExtra(Intent.EXTRA_STREAM, uri)
95+
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
96+
intent.type = mimeType
97+
context.startActivity(intent)
98+
return true
99+
}
100+
}

0 commit comments

Comments
 (0)