These are the answers to the most popular questions we are asked about the Banuba AI Video Editor SDK
- How do I start/stop recording with a tap?
- How do I add an AR mask to the app without using the AR cloud?
- I want to turn off animations from slideshow
- I want to start VideoEditor with a preselected audio track
- How do I add LUTs to the app?
- How do I change the order of LUTs
- I want to control visibility of debug info on camera and editor screens
- I want to customize gallery icon
- How does video editor work when token expires?
- I want to change the font in Video Editor
- How to obtain GIF preview image of exported video
- How to export in Background
- How to change drafts configuration
- How to add other text fonts that are used in the editor screen
- How to change exported video codec configuration
- How can I get a track data of the audio used in my video after export?
- Optimizing app size
- How do I specify the video file saving directory?
- How do I change the duration of the image display in a slideshow?
- How do I change the launguage (how do I add new locale support)
By default, the user must hold the “record” button to film and release it to stop filming.
To change that, simply set the takePhotoOnTap property to false.
"takePhotoOnTap":false
If you don’t want to pull the masks from the backend, you can include them in the app itself.
To do so, name the folder with the mask files the way you want to call the mask, and place it into assets/bnb-resources/effects directory in the module containing the Video Editor SDK (Example).
Make sure that you include the preview.png file in the mask folder. It serves as an icon for the mask within the app.
Slideshow is created either by selecting pictures from gallery or by making a photo on Video Editor camera screen.
Every slide within slideshow can appeare with or without animations. This behavior is configured within videoeditor.json config file under slideshow
section:
"slideshow": {
/* other slideshow related settings */
"animate": true,
"animateTakenPhotos": true
}
Here animate
parameter is applicable to the slideshow created by selecting pictures from the gallery and animateTakenPhotos
is for making photo use case.
To turn off animations just setup false for both fields.
You should create an intent using the method VideoCreationActivity.buildIntent() where audioTrackData is preselected audio track.
startActivity(
VideoCreationActivity.buildIntent(
context,
cameraMode,
audioTrackData
)
)
audioTrackData is an object of TrackData class
data class TrackData(
val id: UUID,
val title: String,
val localUri: Uri
)
Color filters are located in the assets/bnb-resources/luts directory in the module with the AI Video Editor SDK. To add your own, place the files in this folder and create a drawable resource that will be used as an icon for this particular LUT. The name of the drawable resource must be the same as the graphic file in the filter’s directory.
For example, this is the bubblegum LUT file, and this is its drawable resource.
By default, the filters are listed in alphabetical order.
To change it, use the implementation of the OrderProvider
interface.
class CustomColorFilterOrderProvider : OrderProvider {
override fun provide(): List<String> = listOf("egypt", "byers")
}
This will return the list of color filters with the required order. Note: The name of color filter is a name of an appropriate file located in assets/bnb-resources/luts directory. Example.
The final step is to pass your custom CustomColorFilterOrderProvider
implementation in the DI to override the default implementation:
override val colorFilterOrderProvider: BeanDefinition<OrderProvider> =
single(named("colorFilterOrderProvider"), override = true) {
CustomColorFilterOrderProvider()
}
You can control visibility of camera config information and camera preview params(FPS, ISO). Change the following properties in camera.json config file to control:
"showDebugViews": false,
"showConfig": false
You can control visibility of editor config. Change the following properties in videoeditor.json config file to control:
"showConfig": false
Set false to hide info, set true to show.
Gallery icon is represented by AppCompatImageView. Its style placed into galleryImageViewStyle
attribute of the main theme (example)
Drawable resource of the gallery icon may vary depending on the use case:
- in case of the very first launch, if the user did not grant permission to read external storage, or if the gallery on the device is empty, the drawable resource defined in
icon_empty_gallery
attribute of theCameraOverlayStyle
(example) will be used - in other cases you can select what to show as a gallery icon:
- the last media file from the device
- custom drawable resource
By default the last media file is used as a drawable. You have an option to put background to the gallery icon by changing icon_gallery_background
attribute of CameraOverlayStyle
and you can add rounded corners to the gallery icon by changing icon_gallery_radius
attribute of this style (example).
To setup custom drawable resource instead of the last media file you have to put custom style into galleryImageViewStyle
attribute of the main theme (example) and set there use_custom_image
to true
and put your drawable as android:src
attribute value
Token provided by sales managers has an expiration term to protect Video Editor SDK from malicious access. When the token expires the following happens:
- video resolution will be lowered to 360p on camera, after trimmer and after export
- Banuba watermark is applied to every exported video
Also FaceAR SDK you may expect the following actions if the token expires:
- on the first expired month a watermark with "Powered by Banuba" label will be added on the top of both recorded and exported videos
- after the first month the camera screen will be blurred and a full-screen watermark will be displayed
Please keep your licence up to date to avoid unwanted behavior.
All text view styles in Video Editor SDK are inherited from the Text
style, thus the font set in this style will be applied to all text views in Video Editor.
To apply customFont
to Video Editor just override this style:
<style name="Text">
<item name="android:fontFamily">@font/customFont</item>
</style>
Using this approach you don't have to repeatedly set the font to any other styles while customizing the Video Editor.
Video Editor SDK allows to obtain exported video preview as a gif image.
Inside your ExportParamsProvider
implementation create a GifMaker.Params
:
data class Params(
val destFile: File,
val sourceVideoRangeMs: LongRange = 0..1000L,
val fps: Int = 15,
val width: Int = 240,
val useDithering: Boolean = true,
val reverse: Boolean = true
)
Where
destFile
- is a file where gif will be stored after exportsourceVideoRangeMs
- is a range of exported video that will be used to create gif imagefps
- frames per second within gif imagewidth
- is a width of gif image in pixelsuseDithering
- is a flag that apply or remove dithering effect (in simple words make an image of better quality)reverse
- is a flag to reverse playback inside gif
This object should be passed into interactivePreview()
method of ExportManager.Params.Builder
along with the rest export data:
ExportManager.Params.Builder(
mediaResolutionProvider.provideOptimalExportVideoSize())
.effects(effects)
.fileName("export_video")
.videoList(videoList)
.destDir(exportSessionDir)
.musicEffects(musicEffects)
.extraAudioFile(extraSoundtrackUri)
.volumeVideo(videoVolume)
+ .interactivePreview(gifPreviewParams)
.build()
-
Provide
BackgroundExportFlowManager
through DI:override val exportFlowManager: BeanDefinition<ExportFlowManager> = single(override = true) { BackgroundExportFlowManager( exportDataProvider = get(), editorSessionHelper = get(), draftManager = get(), exportNotificationManager = get(), exportDir = get(named("exportDir")), shouldClearSessionOnFinish = true, publishManager = get() ) }
-
Add android activity that you want to open after export into
AndroidManifest.xml
file with special intent action filter. We suggest to use applicationId as a part of intent action name to make it unique among other possible intent actions:<activity android:name=".CustomActivity"> <intent-filter> <action android:name="${applicationId}.ShowExportResult" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
-
Provide
ExportResultHandler
implementation.doAction
method should start activity mentioned in step 2 with the same intent action name as mentioned in AndroidManifest:class CustomExportResultHandler : ExportResultHandler { override fun doAction(activity: AppCompatActivity, result: ExportResult.Success?) { val intent = Intent("${activity.packageName}.ShowExportResult").apply { result?.let { putExtra(EXTRA_EXPORTED_SUCCESS, it) } addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } activity.startActivity(intent) } }
Note: action that is passed into
Intent
constructor must be the same as the action name from activity intent filter from step 2. -
Inside activity from step 2 add
ExportFlowManager
koin injection:class CustomActivity: AppCompatActivity() { private val exportFlowManager: ExportFlowManager by inject() }
-
Inside
onCreate()
method start to observeresultData
LiveData
fromExportFlowManager
to receive export states inside your activity:override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) exportFlowManager.resultData.nonNull().observe(this) { exportResult -> //todo: Do your stuff with different ExportResult objects } }
Note:
ExportResult
is a sealed class:sealed class ExportResult { object Inactive : ExportResult() object Stopped : ExportResult() data class Progress( val preview: Uri ) : ExportResult() @Parcelize data class Success( val message: String, val videoList: List<ExportedVideo>, val preview: Uri, val metaUri: Uri, val additionalExportData: Parcelable? = null ) : ExportResult(), Parcelable @Parcelize data class Error(val message: String) : ExportResult(), Parcelable }
-
Optional. Provide
ExportNotificationManager
to manage notifications about exporting process state.Here you have 3 options:
-
Remove notifications by providing implementation with stubbed methods:
class EmptyExportNotificationManger() : ExportNotificationManager { fun showExportStartedNotification(){} fun showSuccessfulExportNotification(result: ExportResult.Success){} fun showFailedExportExportNotification(){} }
Do not forget to provide this implementation through DI:
val exportNotificationManager: BeanDefinition<ExportNotificationManager> = single(override = true) { EmptyExportNotificationManager() }
-
Configure provided default implementation.
In this case you just need to override or translate following android resources:
- R.string.export.notification_started - for message about started export
- R.string.export_notification_success - for message about succeeded export
- R.string.export_notification_fail - for message about failed export
- R.drawable.ic_export_notification - for notification icon in system bar
-
Provide custom implementation with your own logic through DI:
val exportNotificationManager: BeanDefinition<ExportNotificationManager> = single(override = true) { CustomExportNotificationManger() }
-
By default drafts enabled, asks the user to save a draft before leave any VideoEditor screen. If you need to change drafts configuration you should add the code below in the VideoEditorKoinModule
:
override val draftConfig: BeanDefinition<DraftConfig> = factory(override = true) {
DraftConfig.ENABLED_ASK_TO_SAVE
}
You can choose one of these options:
ENABLED_ASK_TO_SAVE
- drafts enabled, asks the user to save a draftENABLED_ASK_IF_SAVE_NOT_EXPORT
- drafts enabled, asks the user to save a draft without exportENABLED_SAVE_BY_DEFAULT
- drafts enabled, saved by default without asking the userDISABLED
- disabled drafts
To add other text fonts that are used in the editor screen follow the next steps:
-
Add font files to the
app/src/main/res/font/
directory; -
Add fonts names to the strings.xml resource file:
<string name="font_1_title" translatable="false">Font 1 Title</string> <string name="font_N_title" translatable="false">Font N Title</string>
-
Add
font_resources.xml
with fonts array declaration to theapp/src/main/res/values/
directory. The format offont_resources.xml
should be the next one:<?xml version="1.0" encoding="utf-8"?> <resources> <array name="font_resources"> <item>@array/font_1_resource</item> <!-- link to the font description array --> <item>@array/font_N_resource</item> </array> <array name="font_1_resource"> <item>@string/font_1_title</item> <!-- font name --> <item>@font/font_1</item> <!-- link to the font file --> </array> <array name="font_N_resource"> <item>@string/font_N_title</item> <item>@font/font_N</item> </array> </resources>
-
The final step is to pass your custom
font_resources
id to theResourcesTextOnVideoTypefaceProvider
in the DI to override the default implementation:override val textOnVideoTypefaceProvider: BeanDefinition<TextOnVideoTypefaceProvider> = single(override = true) { ResourcesTextOnVideoTypefaceProvider( fontsArrayResId = R.array.font_resources, context = get() ) }
By default for exported video the HEVC
codec is enabled. If you need to change codec configuration you should add the code below in the VideoEditorKoinModule
:
val codecConfiguration: BeanDefinition<CodecConfiguration> = single(override = true) {
CodecConfiguration.AVC_PROFILES
}
You can choose one of these options:
HEVC
- H265 codec enabledAVC_PROFILES
- H264 codec with profiles enabledBASELINE
- H264 codec without profiles enabled
You can get track data after export using getExportedMusicEffect
method of ExportBundleHelper
object. Just pass into the getExportedMusicEffect
the additionalExportData
paramete of ExportResult.Success
object. In the result you get List<MusicEffectExportData>
where MusicEffectExportData
is:
@Parcelize
data class MusicEffectExportData(
val title: String,
val type: MusicEffectType,
val uri: Uri
) : Parcelable
MusicEffectType
contains next values:
TRACK
- audio tracks that were added on theEditor
screenVOICE
- voice record track that was added on theEditor
screenCAMERA_TRACK
- an audio track that was added on theCamera
screen
The easiest way to gain immediate app size savings when publishing to Google Play is by uploading your app as an Android App Bundle, which is a new upload format that includes all your app’s compiled code and resources. Google Play’s new app serving model then uses your app bundle to generate and serve optimized APKs for each user’s device configuration, so they download only the code and resources they need to run your app.
As a result, the final size of our library for one of the platform types (armeabi-v7a
, arm64-v8a
, x86
, x86_64
) will be 24-26 MB less than indicated in the documentation
By default, they are placed in the "export" directory of external storage. To change the target folder, you should provide a custom Uri instance named exportDir through DI. For example, to change the title of destination directory to "my_awesome_directory", provide the Uri instance below:
single(named("exportDir"), override = true) {
get<Context>().getExternalFilesDir("")?.toUri()
?.buildUpon()
?.appendPath("my_awesome_directory")
?.build() ?: throw NullPointerException("exportDir should not be null!")
}
Use the slideShowSourceDurationMs
parameter in videoeditor.json file:
{
"slideshow": {
/* other slideshow related settings */
"slideShowSourceDurationMs": 3000
}
}
There is no special language switching mechanism in the Video Editor SDK (VE SDK).
Out of the box, the VE SDK includes support for two locales: English (default) and Russian. If you need to support any other locales, you can do it according to the standard Android way. See how Create locale directories and resource files for more details. After adding a new locale resource file into your application with integrated VE SDK, you need to re-define the VE SDK strings keys with new locale string values.
To do that you need to add all needed string keys in the new locale strings.xml
file. You can find the main VE SDK string keys you need in the Configure screens doc page. E.g. string keys of the Editor screen you can find here.
The newly added locale will be applied after the device language is changed by system settings.
If you need to change language programmatically in your application, see the next links how it can be done: one, two