# Banuba Video and Photo Editor SDKs Documentation > Docs provides full information about Banuba Video and Photo Editor SDKs, integration and customization guides. This file contains all documentation content in a single document following the llmstxt.org standard. ## Overview Video Editor SDK on Android Banuba Video Editor SDK has built in UI/UX experience and provides a number of customizations you can use to meet your requirements. **AVAILABLE** :white_check_mark: Use your branded icons, colors, and text styles. [See details](ve-faq.md) :white_check_mark: Localize and change text resources. Default locale is :us: :white_check_mark: Make content you want i.e. a number of video with different resolutions and durations, an audio file. :white_check_mark: Masks and filters order. [See details](ve-faq.md) **NOT AVAILABLE** :x: Change layout or order of screens after entry point You can, however, [ask](https://www.banuba.com/support) us to customize the mobile video editor UI as a separate contract. --- ## AI Clipping on Android The [AI Clipping](https://www.banuba.com/ai-sdk) feature automates the video creation process by leveraging the power of artificial intelligence. The neural network transforms raw input into ready-to-post videos with various transitions, effects, and a precise music match. :::important The ```AI Clipping``` is based on the [Banuba Face AR SDK](https://www.banuba.com/facear-sdk/face-filters) product. Since this feature uses additional services, it is disabled by default. Please contact Banuba representatives to know more about using this feature. ::: Here is how users can interact with it: 1. User selects clips and music. People can choose their desired clips and accompanying music within the app. 2. AI trims the videos. The AI algorithm intelligently trims the selected clips to match the tempo and rhythm of the chosen track. 3. AI adds various effects. AI Clipping enhances the content by adding a variety of effects to create visually stunning content. 4. User exports, edits, or regenerates the clip. Upon completion, users have the flexibility to export the video as is, make further edits, or regenerate the clip for a fresh perspective. Regardless of the user’s editing skills, with the help of AI Clipping, everyone can get a stunning video within seconds.   ## Supported Music Providers - [Banuba Music](guide_audio_content.md#connect-banuba-music) - [Soundstripe](guide_audio_content.md#connect-soundstripe) ## Integration ### Setup configuration Add ```Banuba Face AR SDK``` module by following the [Face AR instruction](guide_far_arcloud.md#integrate-face-ar). Set up ```AiClippingConfig```, ```AutoCutTrackLoader``` and ```ContentFeatureProvider``` dependencies in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L51) :::important Contact Banuba representative to get trial keys for ```audioDataUrl``` and ```audioTracksUrl``` ::: ### Config with Banuba Music provider ```kotlin factory { AiClippingConfig( audioDataUrl = "https://d27n29bgbvbeer.cloudfront.net/index-staging.zip", audioTracksUrl = "" ) } factory { AiClippingBanubaMusicTrackLoader(contentProvider = get()) } factory>( named("recommendedSoundsMusicTrackProvider") ) { AiClippingRecommendedSoundProvider() } ``` ### Config with Soundstripe provider ```kotlin factory { AiClippingConfig( audioDataUrl = "...", audioTracksUrl = "..." ) } factory { AiClippingSoundstripeTrackLoader(soundstripeApi = get()) } factory>( named("recommendedSoundsMusicTrackProvider") ) { AiClippingRecommendedSoundProvider() } ``` ### Launch AI Clipping :::info Video creation with AI Clipping is available on Gallery screen by default. ::: For better experience we added new entry point to `VideoCreationActivity` for opening AI Clipping as a separate mode. In this scenario the user starts from the gallery screen and is taken to AI Clipping screen after selecting media. Use the `Intent` to start new mode. ```kotlin val intent = VideoCreationActivity.startFromAiClipping(Context) ``` --- ## Closed Captions on Android Closed captions(CC) are a textual representation of the audio within a media file. :::important The Close Captions feature is disabled by default. Please contact Banuba representatives to know more about using this feature. ::: Over 80% of videos played on mobile devices don’t have sound turned on. This means many forms of content (e.g. skits, monologues, educational clips, etc.) will be skipped if there are no subtitles. But making captions by hand is tedious. AI-generated subtitles solve this issue, as they are created and placed automatically. The users can then edit the text as well as change its style and color.       [AWS Transcribe service](https://docs.aws.amazon.com/transcribe/) is used to generate captions. ## Supported languages - Arabic - English - Mandarin - Spanish - Portuguese ## Integration Create ```Bundle``` with Closed Captions configuration and pass ```extras``` to any Video Editor start method. ### Closed Captions V2 (Recommended) :::important Request API V2 key from Banuba representatives. ::: ```kotlin val extras = bundleOf( CaptionsApiService.ARG_API_KEY_V2 to "...", ) ``` ### Closed Captions V1 :::important Request keys from Banuba representatives. ::: ```kotlin val extras = bundleOf( CaptionsApiService.ARG_CAPTIONS_UPLOAD_URL to "...", CaptionsApiService.ARG_CAPTIONS_TRANSCRIBE_URL to "...", CaptionsApiService.ARG_API_KEY to "...", ) ``` Use ```extras``` in any start method of the Video Editor SDK ```kotlin VideoCreationActivity.startFromCamera( context = applicationContext, pictureInPictureConfig = PipConfig( video = pipVideo, openPipSettings = editorSettingsProvider.openPipSettings ), audioTrackData = initialTrackData, extras = extras ) ``` --- ## Dependencies and Licenses on Android Lists used dependencies and licenses in the SDK. ## Dependencies | Name | Version | | --------- |---------| | androidx.activity:activity-ktx | 1.10.1 | | androidx.annotation:annotation | 1.9.1 | | androidx.appcompat:appcompat | 1.7.0 | | androidx.constraintlayout:constraintlayout | 2.1.4 | | androidx.core:core-ktx | 1.15.0 | | androidx.exifinterface:exifinterface | 1.4.0 | | androidx.fragment:fragment-ktx | 1.8.6 | | androidx.lifecycle:lifecycle-livedata-ktx | 2.8.7 | | androidx.lifecycle:lifecycle-runtime-ktx | 2.8.7 | | androidx.lifecycle:lifecycle-service | 2.8.7 | | androidx.lifecycle:lifecycle-viewmodel-ktx | 2.8.7 | | androidx.localbroadcastmanager:localbroadcastmanager | 1.0.0 | | androidx.recyclerview:recyclerview | 1.4.0 | | com.airbnb.android:lottie | 3.5.0 | | com.github.bumptech.glide:glide | 4.15.0 | | androidx.media3:media3-exoplayer | 1.6.0 | | com.google.android.material:material | 1.12.0 | | com.squareup.moshi:moshi-kotlin | 1.15.2 | | com.squareup.moshi:moshi-kotlin-codegen | 1.15.2 | | com.squareup.okhttp3:logging-interceptor | 4.12.0 | | com.squareup.okhttp3:okhttp | 4.12.0 | | com.squareup.retrofit2:converter-moshi | 2.11.0 | | com.squareup.retrofit2:retrofit | 2.11.0 | | io.insert-koin:koin-android | 3.5.6 | | org.apache.commons:commons-math3 | 3.6.1 | | org.jetbrains.kotlin:kotlin-stdlib-jdk7 | 2.1.0 | | org.jetbrains.kotlin:kotlin-reflect | 2.1.0 | | org.jetbrains.kotlinx:kotlinx-coroutines-android | 1.10.1 | | org.jetbrains.kotlinx:kotlinx-coroutines-core | 1.10.1 | ## 3rd party licenses ### **GNU Lesser General Public Licence version 3.0 or later** This library is free software and is governed by GNU Lesser General Public License, version 3.0, available at https://www.gnu.org/licenses/lgpl-3.0.en.html. | Name | Link | Copyright info | | --- | --- | --- | | FFmpeg | [https://github.com/tanersener/mobile-ffmpeg](https://github.com/tanersener/mobile-ffmpeg) | Copyright (c) ffmpeg Authors | ## **MIT License** "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." | Name | Link | Copyright info | | --- | --- | --- | | json | [https://github.com/nlohmann/json](https://github.com/nlohmann/json) | Copyright (c) 2013-2019 Niels Lohmann | | Android-gif-drawable | [https://github.com/koral--/android-gif-drawable](https://github.com/koral--/android-gif-drawable) | Copyright (c) 2013 - present Karol Wrótniak, Droids on Roids LLC | ## **Apache License 2.0** "Licensed under the Apache License, Version 2.0 (the ""License""); you may not use this file except in compliance with the License. You may obtain a copy of the License at: [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an ""AS IS"" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License." | Name | Link | Copyright info | |-------------------| --- | --- | | Media 3 ExoPlayer | [https://developer.android.com/media/media3/exoplayer](https://developer.android.com/media/media3/exoplayer) | | | OkHttp | [https://github.com/square/okhttp](https://github.com/square/okhttp) | Copyright 2019 Square, Inc. | | Retrofit | [https://github.com/square/retrofit](https://github.com/square/retrofit) | Copyright 2013 Square, Inc. | | Okio | [https://github.com/square/okio](https://github.com/square/okio) | Copyright 2013 Square, Inc. | | Koin | [https://github.com/InsertKoinIO/koin](https://github.com/InsertKoinIO/koin) | Copyright 2017-2021 Arnaud GIULIANI, Laurent BARESSE | | Moshi | [https://github.com/square/moshi](https://github.com/square/moshi) | Copyright 2015 Square, Inc. | | Lottie | [https://github.com/airbnb/lottie-android](https://github.com/airbnb/lottie-android) | Copyright 2018 Airbnb, Inc. | ## **3-clause BSD License** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " | Name | Link | Copyright info | | --- | --- | --- | | Libyuv | [https://chromium.googlesource.com/libyuv/libyuv](https://chromium.googlesource.com/libyuv/libyuv) | Copyright 2011 The LibYuv Project Authors | | Glide | [https://github.com/bumptech/glide](https://github.com/bumptech/glide) | Copyright 2014 Google, Inc. All rights reserved. | ## Face AR SDK [View](https://docs.banuba.com/face-ar-sdk/overview/3rd_licenses) third party libraries for Banuba Face AR SDK --- ## Use of FFmpeg Banuba Video Editor SDK uses FFmpeg version ```5.3.0 ``` ## Guidelines to use FFmpeg dependency in your app: Add Banuba FFmpeg dependency from gradle file. ```groovy implementation "com.banuba.sdk:ffmpeg:5.3.0" ``` Add the following code in your Activity to check whether everything is correct: ```kotlin com.banuba.sdk.ve.processing.FFmpeg(context = this).execute(emptyArray()).run { waitFor() Log.d("FFmpeg", errorStream.reader().readText()) } ``` The output should contain information about version of FFmpeg libraries. We recommend using our FFMpeg functionality, as it offers a very wide range of tools. --- ## Audio content on Android Guide to integrating and customizing audio providers in Video Editor SDK. ## Overview Audio content is a key part of making awesome video. Video Editor SDK can play, trim, merge and add audio content to a video. :::info 1. Banuba does not deliver audio content for Video Editor SDK. 2. Video Editor can apply audio file stored on the device. The SDK is not responsible for downloading audio content except [Soundstripe](https://www.soundstripe.com/) and [Banuba Music](#connect-banuba-music) 3. Video Editor SDK supports only one music provider per launch. ::: There are 2 approaches of using audio content: 1. ```AudioBrowser``` - specific module and a set of screens that includes built in support of browsing and applying audio content within video editor. The user does not leave the sdk while using audio. 2. ```External API``` - the client implements specific API for managing audio content. The user leaves the SDK and is taken to an app screen when audio is requested. ## Audio Browser Audio Browser is a specific Android module that allows to browse, play and apply audio content within video editor. It supports 3 sources for audio content: 1. ```Banuba Music``` - includes build in integration with Banuba Music, 2. ```Soundstripe``` - includes built in integration with [Soundstripe](https://www.soundstripe.com/) API, 3. ```My Library``` - includes audio content available on the user's device. Add the dependency ```kotlin implementation "com.banuba.sdk:ve-audio-browser-sdk:${version}" ``` to your [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L77) file and specify ```AudioBrowserKoinModule``` Koin module in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L57) ```diff startKoin { ... modules( // highlight-add-next-line + AudioBrowserKoinModule().module, VideoEditorKoinModule().module ) } ``` to integrate ```AudioBrowser```. ## Connect Banuba Music Over 35 GB of royalty-free tracks available from within the Video Editor SDK. Your users could check them out through an inbuilt music browser and legally include them in their content. :::info The feature is not activated by default. Please contact Banuba representatives to know more about using this feature. :::   Use ```BanubaMusicProvider``` implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L64) ```kotlin single>(named("musicTrackProvider")){ BanubaMusicProvider() } ``` ## Connect Soundstripe [Soundstripe](https://www.soundstripe.com/) is a service for providing the best audio tracks for creating video content. Your users will be able to add audio tracks while recording or editing video content. :::info The feature is not activated by default. Please contact Banuba representatives to know more about using this feature. :::     Use ```SoundstripeProvider``` implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L64) ```kotlin single>(named("musicTrackProvider")){ SoundstripeProvider() } ``` ## Connect My Library ```My Library``` is a default implementation in ```AudioBrowser``` . It allows the user to apply audio that is available on a device. Use ```AudioBrowserMusicProvider``` implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L75) to enable ```My Library```. ```kotlin single>(named("musicTrackProvider")) { AudioBrowserMusicProvider() } ``` ## Integrate Client Audio provider on Android Video Editor includes special API for integrating your custom audio content provider and applying this content in video editor. The user will be taken to your app specific screen when audio is requested on video editor screen i.e. camera or editor. Next, once the user picks audio content on your app screen you need to follow API and return the user to video editor. Any audio file should stored on the device before applying. Below is a guide of using API to provide your audio to video editor. First, create new Activity ```CustomAudioContentActivity``` that will handle API and create new method to start it from video editor. This Activity you can use to implement any API for downloading audio content and showing your beautiful UI to your users. ```kotlin class CustomAudioContentActivity : AppCompatActivity() { ... companion object { fun buildPickMusicResourceIntent( context: Context, extras: Bundle ) = Intent(context, AwesomeAudioContentActivity::class.java).apply { putExtras(extras) } } } ``` where ```extras``` includes a data that can be used in the Activity. 1. ```ProvideTrackContract.EXTRA_LAST_PROVIDED_TRACK``` of TrackData. Can be null. ```null``` is used to dismiss audio. 2. ```ProvideTrackContract.EXTRA_TRACK_TYPE``` of TrackType. Next, create ```CustomActivityMusicProvider``` and implement ```ContentFeatureProvider```. ```kotlin class CustomActivityMusicProvider : ContentFeatureProvider { private var activityResultLauncher: ActivityResultLauncher? = null private val activityResultCallback: (TrackData?) -> Unit = { activityResultCallbackInternal(it) } private var activityResultCallbackInternal: (TrackData?) -> Unit = {} override fun init(hostFragment: WeakReference) { activityResultLauncher = hostFragment.get()?.registerForActivityResult( ProvideTrackContract(), activityResultCallback ) } override fun requestContent( context: Context, extras: Bundle ): ContentFeatureProvider.Result = ContentFeatureProvider.Result.RequestUi( intent = CustomAudioContentActivity.buildPickMusicResourceIntent( context, extras ) ) override fun handleResult( hostFragment: WeakReference, intent: Intent, block: (TrackData?) -> Unit ) { activityResultCallbackInternal = block activityResultLauncher?.launch(intent) } } ``` And set ```CustomActivityMusicProvider``` to [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L75) ```kotlin single>(named("musicTrackProvider"), override = true) { CustomActivityMusicProvider() } ``` Please keep in mind that only one instance of ```musicTrackProvider``` can exist either your custom of```External API``` or ```Audio Browser```. Finally, pass audio content to apply audio in video editor. Instance of ```TrackData``` is used for passing in ```Intent``` to video editor. The audio should be stored on the device. ```kotlin val trackData = TrackData( UUID.randomUUID(), "My awesome track", audioTrackUri, // Uri of the audio track on local storage // file:///data/user/0//files//awesome.wav "Awesome Artist" ) ``` To pass ```TrackData``` to video editor your need to use ```setResult``` with ```Intent``` and finish current Activity. ```kotlin val trackToApply: TrackData = ... val resultIntent = Intent().apply { putExtra(ProvideTrackContract.EXTRA_RESULT_TRACK_DATA, trackToApply) } setResult(Activity.RESULT_OK, resultIntent) finish() ``` To dismiss previously selected audio track you can pass ```null``` for ```TrackData```. ## Resources The following string resources are used in ```AudioBrowser```. | ResourceId | Value | | ------------- | :----------- | | apply_track | Use | | remove_track| Stop\nusing | | track_loading_failed | Sorry, audio content is temporarily unavailable | | track_search_cancel | Cancel | | audio_browser_title_library | My library | | audio_browser_title_category | Music | | audio_browser_title_empty_category | Music | | audio_browser_load_more | Show more | | audio_browser_error_tracks_not_found | No tracks found | | audio_browser_error_categories_not_found | No categories found | | audio_browser_error_empty_library | No tracks yet | | audio_browser_error_license_not_active | The license is not active | | audio_browser_error_license_expired | The license expired | | audio_browser_error_license_access | Access denied, check license access type | | audio_browser_error_license_api_version | Access denied, check license API version | | audio_browser_error_license_wrong_key | Mubert key is missing. In order to get it contact Banuba rep. | | audio_browser_hint_search_categories | Search by categories | | audio_browser_hint_search_sub_categories | Search by groups | | audio_browser_hint_search_tracks | Search by tracks | | audio_browser_error_dialog_title | Oops, something went wrong… | | audio_browser_error_dialog_description | Please, try again later. | | audio_browser_error_dialog_retry | Retry | | audio_browser_error_dialog_close | Close | | permission_library_description_message | Allow to access to your storage to select an audio tracks from your device. | | audio_browser_connection_error_title | No internet connection | | audio_browser_connection_error_message | Please, check your connection and try again. | | audio_browser_connection_error_btn | Retry | | audio_browser_connection_error_toast | No internet connection | | action_add_music_track | Tracks | | action_add_voice_recording | Record | | action_effects | Effects | | action_edit | Edit | | action_delete | Delete | | edit_track_volume_title | Volume | | edit_track_volume_percent | %1$d%% | | edit_track_audio_duration | Audio duration | | edit_track_duration_error | Audio should be longer than %1$.1f sec | | error_voice_recording_start | Error on voice recording start | | error_invalid_duration_voice_recording | Min voice recording duration - %1$.1f sec | | error_invalid_duration_music_track | Min music track duration - %1$.1f sec | | error_voice_recording_delete_file | Internal error when try to delete voice recording file | | error_track_limit | Max available tracks - %1$d | | error_no_space | No space | --- ## Camera screen on Android Guide to modifying camera UI elements and screen configuration. ## Change icons You can use your app specific icons in Video Editor SDK. Add icon files to Android ```drawable``` folder with predefined names. :::info We strongly recommend using vector assets (.svg files). :::   Below is a list of icons. Each icon has a few states - on, off and modes. Specific file name should be assigned to each state. The filename on the right. Example, ```ic_camera_switch_on_new.xml``` file is used in the SDK to denote the state when a flipper is on. 1. Flip 1. **on** - ```ic_camera_switch_on_new``` 2. **off** - ```ic_camera_switch_off_new``` 2. Flash 1. **on** - ```ic_flashlight_on_new``` 2. **off** - ```ic_flashlight_off_new``` 3. Speed 1. **x0.5** - ```ic_recording_speed_x0_5_new``` 2. **x1** - ```ic_recording_speed_x1_new``` 3. **x2** - ```ic_recording_speed_x2_new``` 4. **x3** - ```ic_recording_speed_x3_new``` 5. **x0.5 selected** - ```ic_recording_speed_x0_5_selected_new``` 6. **x1 selected** - ```ic_recording_speed_x1_selected_new``` 7. **x2 selected** - ```ic_recording_speed_x2_selected_new``` 8. **x3 selected** - ```ic_recording_speed_x3_selected_new``` 4. Mic 1. **on** - ```ic_mute_mic_on_new``` 2. **off** - ```ic_mute_mic_off_new``` 5. Beauty 1. **on** - ```ic_beauty_on_new``` 2. **off** - ```ic_beauty_off_new``` 6. Filter 1. **on** - ```ic_color_on_new``` 2. **off** - ```ic_color_off_new``` 7. PIP 1. **on** - ```ic_pip_on_new``` 2. **off** - ```ic_pip_off_new``` 8. Mask 1. **on** - ```ic_mask_on_new``` 2. **off** - ```ic_mask_off_new``` 9. Music 1. **on** - ```ic_music_on_white``` 2. **off** - ```ic_music_on_white``` 10. Timer 1. ```ic_timer_new``` --- ## Cover image on Android Cover image screen allows users to pick any frame of video as image or choose an image from gallery. ```CoverProvider``` supports 2 modes - ```EXTENDED``` - enables the screen - ```NONE``` - disables the screen. The user is taken to export screen. ```EXTENDED``` is **default** mode in the SDK. You can change the mode in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L75) ``` kotlin single(override = true) { CoverProvider.EXTENDED } ``` ## Resources You can override the following string resources in your app. | ResourceId | Value | |--------------| :----------- | | cover_image_text | Choose cover | | cover_progress_text | Please, wait | | err_cover_image | Failed to create cover image | --- ## Drafts screen on Android Guide to integrating and customizing drafts screen on Video Editor SDK. ## Configuration Drafts are enabled by default, 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 [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L60): ```kotlin override val draftConfig: BeanDefinition = factory(override = true) { DraftConfig.ENABLED_ASK_TO_SAVE } ``` You can choose one of these options: 1. `ENABLED_ASK_TO_SAVE` - drafts enabled, asks the user to save a draft 2. `ENABLED_ASK_IF_SAVE_NOT_EXPORT` - drafts enabled, asks the user to save a draft without export 3. `ENABLED_SAVE_BY_DEFAULT` - drafts enabled, saved by default without asking the user 4. `DISABLED` - disabled drafts ## Draft Helper ```DraftsHelper``` interface is used for managing drafts. ``` kotlin interface DraftsHelper { val allDrafts: StateFlow> fun delete(draft: Draft) fun deleteAll() fun openDraft(draft: Draft): Intent fun openLastDraft(): Intent } ``` To get the instance of ```DraftsHelper``` use the following in your Fragment or Activity. ``` kotlin val draftsHelper: DraftsHelper by inject() ``` ### Used string resources | ResourceId | Value | |:----------:|:-----:| |drafts_title|Drafts| |drafts_empty_description|No Drafts| |drafts_options_edit|Edit| |drafts_options_delete|Delete| |editor_trim_video|Adjust Clips| |editor_discard_changes|Discard changes| |editor_update_draft|Update draft| --- ## Editor screen on Android Guide to integrating and customizing Editor screen. ## Change icons You can use your app specific icons in Video Editor SDK. Add icon files to Android ```drawable``` folder with predefined names. :::info We strongly recommend using vector assets (.svg files). :::   Below is a list of icons. Each icon has a few states - on, off, disabled. Specific file name should be assigned to each state. The filename on the right. Example, ```ic_gif_on.xml``` file is used in the SDK to denote the state when a sticker is added to the video. 1. Stickers 1. **on** - ```ic_gif_on``` 2. **off** - ```ic_gif_off``` 2. Text 1. **on** - ```ic_text_effect_on``` 2. **off** - ```ic_text_effect_off``` 3. Captions 1. **on** - ```ic_captions_on``` 2. **off** - ```ic_captions_off``` 3. **disabled** - ```ic_captions_disabled``` 4. Effects 1. **on** - ```ic_visual_effect_on``` 2. **off** - ```ic_visual_effect_off``` 5. Masks 1. **on** - ```ic_mask_on``` 2. **off** - ```ic_mask_off``` 6. Music 1. **on** - ```ic_music_on``` 2. **off** - ```ic_music_off``` 7. Time 1. **on** - ```ic_time_effect_on``` 2. **off** - ```ic_time_effect_off``` 8. Filters 1. **on** - ```ic_lut_on``` 2. **off** - ```ic_lut_off``` 9. Blur 1. **on** - ```ic_blur_on``` 2. **off** - ```ic_blur_off``` 10. AI Clipping 1. **on** - ```ic_autocut_on``` 2. **off** - ```ic_autocut_off``` --- ## NEW Editor screen on Android Guide to integrating and customizing New Editor screen. ## Overview With the new interface, better controls, and additional quality of life improvements, making stunning videos is easier and more fun than ever. Design and user experience principles are constantly evolving. To keep up with the latest developments and best practices, our team has completely redesigned the Video Editor SDK to be as convenient and enjoyable as possible.       ## Integration Create ```Bundle``` with Editor UI V2 configuration and pass ```extras``` to any Video Editor start method. ```kotlin val extras = bundleOf( "EXTRA_USE_EDITOR_V2" to true ) ``` ```kotlin VideoCreationActivity.startFromCamera( context = applicationContext, pictureInPictureConfig = PipConfig( video = pipVideo, openPipSettings = false ), audioTrackData = initialTrackData, extras = extras ) ``` --- ## Export media on Android Video Editor SDK allows to export a number of media files i.e. video and audio with various resolutions and other configurations. Video is exported as ```.mp4``` file. :::info Export is a very heavy computational task that takes time and the user has to wait. Execution time depends on 1. Video duration - the longer video the longer execution time. 2. Number of video sources - the many sources the longer execution time. 3. Number of effects and their usage in video - the more effects and their usage the longer execution time. 4. Number of exported video - the more video and audio you want to export the longer execution time. 5. Device hardware - the most powerful devices can execute export much quicker and with higher resolution. ::: Export supports 2 modes: - ```Foreground``` - the user has to wait on progress screen until processing is done. **Default** - ```Background``` - the user can close the editor and open an app specific screen. A certain notification is sent when processing is done. ```ForegroundExportFlowManager``` and ```BackgroundExportFlowManager``` are corresponding implementations. Here is a screen that is shown in ```Foreground``` mode. ## Video quality Video Editor supports video codec options: 1. ```HEVC``` - H265 codec. **Default** 2. ```AVC_PROFILES``` - H264 codec with profiles 3. ```BASELINE``` - H264 codec without profiles The following table presents list of video resolution and bitrate values for codec ```H264(AVC_PROFILES)```. | 240p(240x426) | 360p(360x640) | 480p(480x854) | QHD540(540x960) | HD(720x1280) | FHD(1080x1920) | QHD(1440x2560) | UHD(2160x3840) | |---------------|------------|---------------|-----------------|--------------|----------------|----------------|----------------| | 1000 kb/s | 1200 kb/s | 2000 kb/s | 2400 kb/s | 3600 kb/s | 5800 kb/s | 10000 kb/s | 20000 kb/s | :::info Video Editor includes built-in feature for detecting device performance capabilities and finding ```auto``` video quality for exported video. ::: ## Export storage All exported media files are stored in ```export``` directory on the device storage. You can specify another directory by overriding ```exportDir``` dependency in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L51). Default implementation is ```kotlin single(named("exportDir")) { get().getExternalFilesDir("")?.toUri() ?.buildUpon() ?.appendPath("export") ?.build() ?: throw NullPointerException("exportDir cannot be null!") } ``` ## Implement export flow :::info Default implementation exports single video file with auto quality(based on device hardware capabilities). ::: You can create your own export flow to meet your requirements. First, specify list of media files to export. Create new class ```CustomExportParamsProvider``` and implement ```ExportParamsProvider```. Method ```provideExportParams``` returns ```List``` which is a list of media files to export. The following implementation exports single video with ```HD``` and ```auto``` quality ```kotlin class CustomExportParamsProvider( private val exportDir: Uri, private val watermarkBuilder: WatermarkBuilder ) : ExportParamsProvider { override fun provideExportParams( effects: Effects, videoRangeList: VideoRangeList, musicEffects: List, videoVolume: Float ): List { val exportSessionDir = exportDir.toFile().apply { deleteRecursively() mkdirs() } // Video is in HD resolution val exportVideoHD = ExportParams.Builder(VideoResolution.Exact.HD) .effects(effects) .fileName("export_video_hd") .videoRangeList(videoRangeList) .destDir(exportSessionDir) .musicEffects(musicEffects) .volumeVideo(videoVolume) .build() // Video is in auto resolution val exportVideoAuto = ExportParams.Builder() .effects(effects) .fileName("export_video_auto") .videoRangeList(videoRangeList) .destDir(exportSessionDir) .musicEffects(musicEffects) .volumeVideo(videoVolume) .build() return listOf(exportVideoHD, exportVideoAuto) } } ``` Use constructor ```ExportParams.Builder()``` or specific method ```videoResolution()```to set up custom video quality. ```diff val exportVideoHD = // highlight-add-next-line + ExportParams.Builder(VideoResolution.Exact.HD) .effects(effects) .fileName("export_video_hd") .videoRangeList(videoRangeList) .destDir(exportSessionDir) .musicEffects(musicEffects) .volumeVideo(videoVolume) .build() ``` :::warning Please keep in mind that low level devices might not be able to export video with high quality. Our recommendations 1. Use ```HD``` or ```VGA480``` resolutions with codec ```H264(AVC_PROFILES)``` 2. Do not specify certain video quality. In this case ```auto``` quality will be used. ::: Next, specify ```CustomExportParamsProvider``` implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L51) ```kotlin factory { CustomExportParamsProvider( exportDir = get(named("exportDir")), watermarkBuilder = get() ) } ``` Finally, use the most suitable export mode for your application in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L51). ```ForegroundExportFlowManager``` is used by default. ## Use AVC codec The SDK prefers ```H265(HEVC)``` codec by default for exporting video file. You can specify ```H264(AVC_PROFILES)``` by setting ```false``` to ```useHevcIfPossible```. :::info ```H264(AVC_PROFILES)``` is recommended for low level devices ::: ```diff ExportParams.Builder(VideoResolution.Exact.HD) ... // highlight-add-next-line + useHevcIfPossible(false) .build(), ``` ## Add watermark Watermark is not added to exported video by default. Create new class ```CustomWatermarkProvider``` and implement ```WatermarkProvider``` to use your custom watermark image. ```kotlin private class CustomWatermarkProvider(private val context: Context) : WatermarkProvider { override fun getWatermarkBitmap(): Bitmap? { val watermarkDrawableRes = ... // R.drawable. val watermark = BitmapFactory.decodeResource( context.resources, watermarkDrawableRes ) return watermark } } ``` and specify it in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L51) ```kotlin factory { CustomWatermarkProvider() } ``` Use extension method ```Effects.withWatermark``` for adding a watermark to ```ExportParams.Builder```. ```diff ExportParams.Builder(VideoResolution.Exact.HD) // highlight-add-next-line + .effects(effects.withWatermark(watermarkBuilder, WatermarkAlignment.BottomRight(marginRightPx = 16.toPx))) ... .build(), ``` where ```WatermarkBuilder``` provides watermark drawable and ```alignment``` is used where to locate drawable ```kotlin WatermarkAlignment { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT } ``` ## Export soundtrack track You can export video and audio soundtrack file separately. For example, ```Kotlin val extraSoundtrackUri = Uri.parse(exportSessionDir.toString()).buildUpon() .appendPath("exported_soundtrack.${MediaFileNameHelper.DEFAULT_SOUND_FORMAT}") .build() val exportVideoAndSoundtrack = ExportParams.Builder(VideoResolution.Exact.HD) .fileName("export_extra_soundtrack") .videoRangeList(videoRangeList) .destDir(exportSessionDir) .musicEffects(musicEffects) .extraAudioFile(extraSoundtrackUri) .volumeVideo(videoVolume) .build() ``` and add ```exportVideoAndSoundtrack``` to the list of exported video files in your custom ```ExportParamsProvider```. ## Handle export result The result is returned to your controller in [registerForActivityResult](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/MainActivity.kt#L27) method as an instance of ```ExportResult```. ```kotlin private val createVideoRequest = registerForActivityResult(CustomExportResultVideoContract()) { exportResult -> exportResult?.let { if (exportResult is ExportResult.Success) { // Get uri of first exported video file val videoUri = exportResult.videoList.getOrNull(0) ... } } } ``` ```ExportResult``` class ```kotlin sealed class ExportResult { object Inactive : ExportResult() object Stopped : ExportResult() data class Progress( val preview: Uri ) : ExportResult() @Parcelize data class Success( val videoList: List, val preview: Uri, val metaUri: Uri, val additionalExportData: Parcelable? = null ) : ExportResult(), Parcelable @Parcelize data class Error(val type: ExportError) : ExportResult(), Parcelable } ``` Instance of ```ExportResult.Success``` is returned with all export data when export finishes successfully. ```ExportResult.Error``` is returned when an error is occurred while exporting media content. ## Export in background If you want to export media in the background and avoid showing progress screen to your user you can use ```BackgroundExportFlowManager``` implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L51) and provide your custom implementations. ```kotlin single { BackgroundExportFlowManager( exportDataProvider = get(), exportSessionHelper = get(), exportNotificationManager = get(), exportDir = get(named("exportDir")), shouldClearSessionOnFinish = true, publishManager = get(), errorParser = get(), exportBundleProvider = get(), eventConverter = get() ) } ``` Next, specify Android ```CustomActivity``` that opens after export in ```AndroidManifest.xml``` file and set special ``` ``` . Please use ```applicationId``` as a part of intent action name to make it unique among other possible intent actions ```kotlin ``` Create new class ```CustomExportResultHandler``` and implement ```ExportResultHandler``` that will start your Activity mentioned above. ```kotlin 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 above. Next, add ```ExportFlowManager``` dependency using Koin and observe for ```ExportResult``` in ```CustomActivity``` ```kotlin class CustomActivity: AppCompatActivity() { private val exportFlowManager: ExportFlowManager by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) exportFlowManager.resultData.nonNull().observe(this) { exportResult -> //... } } } ``` **Optional**. Provide ```ExportNotificationManager``` to manage notifications about exporting process state. There are 3 options: 1. Remove notifications all notifications ```kotlin class EmptyExportNotificationManger() : ExportNotificationManager { fun showExportStartedNotification(){} fun showSuccessfulExportNotification(result: ExportResult.Success){} fun showFailedExportExportNotification(){} } ``` and provide this implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L75): ```kotlin single { EmptyExportNotificationManger() } ``` 2. Customize default implementation You can override the 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 3. Provide custom implementation of ```ExportNotificationManager``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L75) ```kotlin single { CustomExportNotificationManger() } ``` ## Export GIF preview Video Editor allows to export preview of a video as a GIF file. Use ```GifMaker.Params``` in your ```CustomExportParamsProvider``` to create params for exporting GIF preview ```kotlin 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` - where to store the file - `sourceVideoRangeMs` - is a range of exported video that will be used to create gif image - `fps` - frames per second within gif image - `width` - width of gif image in pixels - `useDithering` - flag that apply or remove dithering effect (in simple words make an image of better quality) - `reverse` - flag to reverse playback inside gif Use ```interactivePreview``` method in ```ExportParams.Builder``` to enable exporting GIF ```diff ExportParams.Builder(sizeProvider.videoResolution) .effects(effects) .fileName("export_video") .videoRangeList(videoRangeList) .destDir(exportSessionDir) .musicEffects(musicEffects) .extraAudioFile(extraSoundtrackUri) .volumeVideo(videoVolume) // highlight-add-next-line + .interactivePreview(gifPreviewParams) .build() ``` ## Get audio used in export You can get all audio used in exported video when export finished successfully. ```ExportBundleHelper.getExportedMusicEffect``` requires ```additionalExportData``` value of ```ExportResult.Success```. The result is ```List``` where ```MusicEffectExportData``` is ```kotlin @Parcelize data class MusicEffectExportData( val title: String, val type: MusicEffectType, val uri: Uri ) : Parcelable ``` `MusicEffectType` contains next values: 1. `TRACK` - audio tracks that were added on the `Editor` screen 2. `VOICE` - voice record track that was added on the `Editor` screen 3. `CAMERA_TRACK` - audio track that was added on the `Camera` screen ## Export metadata analytics Video Editor generates simple metadata analytics while exporting media content that you can use to analyze what media content your users make. Metadata is a JSON string and can be returned from ```ExportResult.Success``` in this way ```kotlin //"exportResult" is an instance of ExportResult.Success object val outputBundle = exportResult.additionalExportData.getBundle(ExportBundleProvider.Keys.EXTRA_EXPORT_OUTPUT_INFO) val analytics = outputBundle?.getString(ExportBundleProvider.Keys.EXTRA_EXPORT_ANALYTICS_DATA) ``` ```ExportBundleProvider.Keys``` includes all constants you can use for parsing. Sample ```JSON { "export_success": true, // defines if the export finished succesffully "aspect_ratio": "original", // aspect ration used in exported video "video_resolutions": ["1080x1920"], // list of video resolutions used in export "camera_effects": [], // list of effects of features used on camera screen while recording video "ppt_effects": { "visual": 2, // num of visual effects i.e. Glitch, VHS used in exported video "speed": 1, // num of speed effects used in exported video "mask": 6, // num of AR masks used in exported video "color": 3, // num of color effects used in exported video "text": 1, // num of text effects used in exported video "sticker": 1, // num of sticker effects used in exported video "blur": 1 // num of blur effects used in exported video }, "sources": { "camera": 0, // num of video sources recorded on camera screen(not PIP) "gallery": 1, // num of video sources selected in the gallery "pip": 0, // num of video recorded with PIP "slideshow": 0, // num of video exported as slideshow "audio": 0 // num of audi tracks }, "export_duration": 12.645, // export processing duration "video_duration": 20.11, // exported video duration "video_count": 1, // num of exported video files "os_version": "11", // OS version "sdk_version": "1.26.6" // VE SDK version } ``` --- ## Face AR and AR Cloud products on Android [Banuba Face AR SDK](https://www.banuba.com/facear-sdk/face-filters) product is used on camera and editor screens for applying various AR effects while making video content. ## Overview Any Face AR effect is a folder that includes a number of files required for Face AR SDK to play this effect. :::tip Make sure ```preview.png``` file is included in effect folder. You can use this file as a preview for AR effect. ::: ## Integrate Face AR :::info ```Banuba Face AR SDK``` integration is included in the [Github sample](https://github.com/Banuba/ve-sdk-android-integration-sample). ::: Follow next steps to integrate ```Banuba Face AR SDK``` into your project. First, add Gradle ```com.banuba.sdk:effect-player-adapter``` dependency in [app gradle file](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L74). ```diff def banubaSdkVersion = '1.48.5' ... // highlight-add-next-line + implementation "com.banuba.sdk:effect-player-adapter:${banubaSdkVersion}" ... ``` Add ```BanubaEffectPlayerKoinModule().module``` in the [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L40) ```diff startKoin { androidContext(this@IntegrationApp) modules( ... // highlight-add-next-line + BanubaEffectPlayerKoinModule().module ) } ``` ## Add and Manage effects There are 3 options for adding and managing AR effects: 1. Store all effects in [assets/bnb-resources/effects](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/assets/bnb-resources/effects) folder in the app. 2. Store color effects in [assets/bnb-resources/luts](https://github.com/Banuba/ve-sdk-android-integration-sample/tree/main/app/src/main/assets/bnb-resources/luts) folder in the app. 3. Use [AR Cloud](https://www.banuba.com/faq/what-is-ar-cloud) for storing effects on a server. :::tip You can use both options i.e. store just a few AR effects in ```assets``` and 100 or more AR effects on ```AR Cloud```. ::: ## Integrate AR Cloud ```AR Cloud``` is a cloud solution for storing Banuba Face AR effects on the server and used by Face AR and Video Editor products. Any AR effect downloaded from ```AR Cloud``` is cached on the user's device. Follow next steps to integrate ```AR Cloud``` into your project. First, add Gradle ```com.banuba.sdk:ar-cloud``` dependency in [app gradle file](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle). ```diff def banubaSdkVersion = '1.48.5' ... // highlight-add-next-line + implementation "com.banuba.sdk:ar-cloud:${banubaSdkVersion}" ... ``` Next, add ```ArCloudKoinModule``` module to [Koin modules](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L67). ```diff startKoin { ... modules( VeSdkKoinModule().module, ... // highlight-add-next-line + ArCloudKoinModule().module, ... ) } ``` Next, override ```ArEffectsRepositoryProvider``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L91). ```kotlin single(createdAtStart = true) { ArEffectsRepositoryProvider( arEffectsRepository = get(named("backendArEffectsRepository")), ioDispatcher = get(named("ioDispatcher")) ) } ``` ## Change order effects By default, all AR effects are listed in alphabetical order. AR effects from ```assets``` are listed in order. Create new class ```CustomMaskOrderProvider``` and implement ```OrderProvider``` to provide custom order. ```kotlin class CustomMaskOrderProvider : OrderProvider { override fun provide(): List = listOf("Background", "HeadphoneMusic") } ``` :::info These are names of specific directories in ```assets/bnb-resources/effects``` or on ```AR Cloud```. ::: Next, use ```CustomMaskOrderProvider``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L75) ```kotlin single(named("maskOrderProvider")) { CustomMaskOrderProvider() } ``` ## Disable Face AR SDK Video Editor SDK can be used without Face AR SDK. Remove ```BanubaEffectPlayerKoinModule().module``` from [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L62) ```diff startKoin { androidContext(this@IntegrationApp) modules( ... // highlight-remove-next-line - BanubaEffectPlayerKoinModule().module ) } ``` and remove Gradle dependency ```com.banuba.sdk:effect-player-adapter``` ```diff ... // highlight-remove-next-line - implementation "com.banuba.sdk:effect-player-adapter:${banubaSdkVersion}" ... ``` --- ## Drawing on Android Lets your users draw freely on screen. It is a convenient tool for highlighting important objects in the video or spicing up the frame with funny doodles or stylish art. There are 5 line variants to choose from for more self-expression opportunities. :::important The Drawing feature is disabled by default. Please contact Banuba representatives to know more about using this feature. :::       --- ## Gallery screen on Android Video Editor SDK includes built in gallery functionality where the user can pick any video or image and use it while making video. :::info Gallery is integrated by default. ::: ## Integration The following guide will help you to integrate gallery to your project if it was not added before. Add module ```com.banuba.sdk:ve-gallery-sdk:1.48.5``` to [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L73) file and specify ```GalleryKoinModule``` module in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L39) ```diff startKoin { androidContext(this@IntegrationApp) modules( ... // highlight-add-next-line + GalleryKoinModule().module ) } ``` ## Customizations You can control options ```Videos``` and ```Photos``` by overriding instance of ```EditorConfig``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L51) . ```diff single { EditorConfig( // highlight-add-next-line + gallerySupportsVideo = ..., // true - show Videos, false - hide. Defaul - true // highlight-add-next-line + gallerySupportsImage = ..., // true - show Photos, false - hide. Default - true ) } ``` ## Implement custom gallery Video editor allows to replace default gallery with your custom. Please follow implementation guide. First, create ```CustomMediaContentProvider``` class that implements ```ContentFeatureProvider, Fragment>```. This class describes a contract between Video Editor and specific Fragment from your project for gallery. ```kotlin class CustomMediaContentProvider : ContentFeatureProvider, Fragment> { private var activityResultLauncher: ActivityResultLauncher? = null private val activityResultCallback: (List?) -> Unit = { activityResultCallbackInternal(it) } private var activityResultCallbackInternal: (List?) -> Unit = {} override fun init(hostComponent: WeakReference) { activityResultLauncher = hostComponent.get()?.registerForActivityResult( ProvideMediaContentContract(), activityResultCallback ) } override fun requestContent( context: Context, extras: Bundle ): ContentFeatureProvider.Result> = ContentFeatureProvider.Result.RequestUi( intent = SelectExternalContentActivity.newGetMediaIntent(context).apply { putExtras(extras) } ) override fun handleResult( hostComponent: WeakReference, intent: Intent, block: (List?) -> Unit ) { activityResultCallbackInternal = block activityResultLauncher?.launch(intent) } } ``` Method `init` is invoked in Video Editor to register `onActivityResult` callback and receive media content. `com.banuba.sdk.core.domain.ProvideMediaContentContract` class is used to manage data bundle that is passed between video editor and your custom media provider implementation. Method `requestContent` is used to create `Intent` for starting your custom Activity with gallery. `extras` argument contains metadata for media content selection and should be passed into your custom media provider. `handleResult` method connects `onActivityResult` callback within Video Editor with the media content provided by `CustomGalleryActivity`. Next, obtain media request params in your `CustomGalleryActivity.onCreate()` ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) intent.extras?.let { extras -> val params = ProvideMediaContentContract.obtainParams(extras) ... } ``` `Params` object contains data about request from video editor ```kotlin data class Params( val mode: OpenGalleryMode, val types: List, val maxCount: Int, val minCount: Int, val supportedFormats: List ) ``` `mode` - is a type of request (NORMAL, FEATURE_BACKGROUND, ADD_TO_TRIMMER) `types` - list of requested media types (Video, Image) `supportedFormats` - list of media file extensions that are supported by SDK Deliver media data from `CustomGalleryActivity` to Video Editor SDK ```kotlin val resultIntent = Intent().apply { putParcelableArrayListExtra( ProvideMediaContentContract.EXTRA_MEDIA_CONTENT_RESULT, ArrayList(selectedMedia) ) } setResult(Activity.RESULT_OK, resultIntent) ``` `selectedMedia` is a list of media Uris with **content://** scheme. Finally, provide `CustomMediaContentProvider` in `VideoEditorModule.kt` Koin module. ```kotlin factory, Fragment>>(named("mediaDataProvider"), override = true) { (external: Boolean?) -> CustomMediaContentProvider() } ``` and remove module `GalleryKoinModule` from the list of modules. ```diff startKoin { androidContext(applicationContext) allowOverride(true) modules( ... // highlight-remove-next-line - GalleryKoinModule().module, ... ) } ``` --- ## Green Screen on Android Video Editor SDK brings you real-time background subtraction technology for you to empower your users with the best quality virtual background service. Users can automatically remove, change or augment backgrounds. :::important The feature requires Face AR product with [Background Subtraction](https://www.banuba.com/technology/background-subtraction) feature. Please contact Banuba representatives to know more about using this feature. :::   ```Green Screen``` feature doesn't require physical green screens, the neural networks do all the work. --- ## Open Photo Editor SDK from Camera screen on Android This guide demonstrates how to open [Photo Editor SDK](https://www.banuba.com/photo-editor-sdk) just after taking a photo on Video Editor Camera screen. All recorded video or taken images on Camera screen can be handled using ```MediaNavigationProcessor```. Provide custom implementation of ```MediaNavigationProcessor``` in Koin module to override flow. ``` diff class SampleIntegrationKoinModule { val module = module { ... // highlight-add-next-line + single { object : MediaNavigationProcessor { override fun process(activity: Activity, mediaList: List): Boolean { // Filter media resources to find the target image for Photo Editor SDK val pngs = mediaList.filter { it.path?.contains(".png") ?: false } return if (pngs.isEmpty()) { true } else { // Create ExportResult and close Video Editor SDK. (activity as? VideoCreationActivity)?.closeWithResult( ExportResult.Success( emptyList(), pngs.first(), Uri.EMPTY, Bundle() ) ) false } } } } } ``` Handle received ```ExportResult``` in your ```registerForActivityResult``` or ```Activity.onActivityForResult``` method. ```diff private val createVideoRequest = registerForActivityResult(CustomExportResultVideoContract()) { exportResult -> exportResult?.let { if (exportResult is ExportResult.Success) { // Use exported preview file to open Photo Editor SDK // highlight-add-next-line photoEditorExportResult.launch( PhotoCreationActivity.startFromEditor( applicationContext, imageUri = exportResult.preview ) ) ... } } } private val photoEditorExportResult = registerForActivityResult(PhotoExportResultContract()) { uri -> // Handle exported image result } ``` --- ## Share video screen on Android Share video screen allows users to easily share an exported video using popular social media services and OS specific components. :::info This is an optional screen that you can add to your video editing flow. ::: ## Integration Create new layout ```activity_video_sharing.xml``` file in ```res/layout``` folder. This layout is required for integrating ```VideoSharingFragment``` from the SDK. ``` xml ``` Next, create new ```VideoSharingActivity``` that will handle social media keys and start video sharing screen. ``` kotlin class VideoSharingActivity : AppCompatActivity(R.layout.activity_video_sharing) { companion object { // Set up your Facebook app id const val FACEBOOK_APP_ID = "" } private val exportResult by lazy(LazyThreadSafetyMode.NONE) { intent?.getParcelableExtra(EXTRA_EXPORTED_SUCCESS) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val videoSharingFragment = VideoSharingFragment.newInstance( exportResult = exportResult, fbAppId = FACEBOOK_APP_ID ) supportFragmentManager.addFragment( videoSharingFragment, VideoSharingFragment.TAG, R.id.fragmentContainer, false ) } override fun onDestroy() { super.onDestroy() // SampleApp is an implementation available in main Video Editor Sample. // Release video editor dependencies when the user leaves this creen (application as? SampleApp)?.releaseVideoEditor() } } ``` and specify ```VideoSharingActivity``` in ```AndroidManifest.xml``` file. ``` xml ``` Finally, start ```VideoSharingActivity``` to open video sharing screen. ``` kotlin val intent = Intent(getString(com.banuba.sdk.export.R.string.export_action_name, packageName)).apply { putExtra(EXTRA_EXPORTED_SUCCESS, exportResult) } startActivity(intent) ``` --- ## Stickers on Android Guide to using stickers in Video Editor SDK on Android. ## Giphy Video Editor SDK has built in integration with [Giphy service](https://developers.giphy.com/docs/api/) for loading stickers. ```GIPHY``` doesn't charge for their content. The one thing they do require is attribution. Also, there is no commercial aspect to the current version of the product (no advertisements, etc.). Any sticker effect is a GIF file. To use stickers in your project you need to request personal [Giphy API key](https://support.giphy.com/hc/en-us/articles/360020283431-Request-A-GIPHY-API-Key). Override instance of ```GifPickerConfigurations``` and set GIPHY API key to ```giphyApiKey``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L53). ``` diff factory { GifPickerConfigurations( // highlight-add-next-line + giphyApiKey = .... ) } ``` Stickers will appear in the container on editor screen once ```giphyApiKey``` is set. --- ## Video recording integration guide on Android Guide to modifying video recording feature in Video Editor SDK. ## Quality details Subsequent table describes video quality details used for video recording in various resolutions. | Recording speed | 360p(360 x 640) | 480p(480 x 854) | HD(720 x 1280) | FHD(1080 x 1920) | | --------------- | --------------- | --------------- | -------------- | ---------------- | | 1x(Default) | 1200 | 2000 | 4000 | 6400 | | 0.5x | 900 | 1500 | 3000 | 4800 | | 2x | 1800 | 3000 | 6000 | 9600 | | 3x | 2400 | 4000 | 8000 | 12800 | ## Customize configurations ```CameraConfig``` is a main class used to customize features, behavior and user experience for video recording on camera screen i.e. set min/max recording duration, flashlight, etc. Video editor includes default implementation but you can provide your own implementation to meet your requirements in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt). For example, to sets max video recording duration to 30 seconds. ```kotlin single(override = true) { CameraConfig(maxRecordedTotalVideoDurationMs = 30_000) } ``` | Property | Values | Description | | ------------- |:--------------------------------------------------------------------------------------------:| :------------- | | minRecordedTotalVideoDurationMs | Number > 0; Default ```3000``` | minimum video recording duration *in milliseconds* required to proceed and open video editing screen (i.e. 3000 for 3 seconds) | maxRecordedTotalVideoDurationMs | Number > 0; Default ```120000``` | maximum video recording duration *in milliseconds* available to record | minRecordedChunkVideoDurationMs | Number > 1000; Default ```1000``` | minimum video recording duration *in milliseconds* that is allowed to record on camera | takePhotoOnTap | true/false; Default ```false``` | defines if it is available to take a photo on the camera screen by tap. ```true``` photo is taken by tap and video is recording by long press. | supportsMultiRecords | true/false; Default ```true``` | defines if the use can record multiple video subsequently. ```false``` when the first video recording is done the editing screen will be opened | supportsFlashlight | true/false; Default ```true``` | enables flashlight icon on the camera screen and possibility to take a photo with flashlight | supportsSpeedRecording | true/false; Default ```true``` | enables speed recording icon on the camera screen and possibility to select recording speed | supportsExternalMusic | true/false; Default ```true``` | enables the music icon on the camera screen and possibility to add music track playing over the video recording | supportsMuteMic | true/false; Default ```true``` | enables mute microphone icon on the camera screen and possibility to record video without capturing sound | switchFacingOnDoubleTap | true/false; Default ```true``` | ```true``` allows to switch between front and back camera by double tap | isStartFrontFacingFirst | true/false; Default ```true``` | ```true``` means that ```front``` camera facing is used on the first launch of the camera screen, ```false``` means that ```back``` camera facing is used on the first launch of the camera screen | isSaveLastCameraFacing | true/false; Default ```true``` | defines if the camera facing (```back``` or ```front```) is saved and restored | cameraFpsMode | CameraFpsMode enum values; Default ```CameraFpsMode.FIXED``` | ```CameraFpsMode.FIXED``` means that video recording quality can be degraded to maintain 30 FPS while applying "heavy" Face AR effects (*This behavior is recommended* and allows to reach seamless usage on wide range of devices). ```CameraFpsMode.ADAPTIVE``` means that FPS can be reduced in order to maintain video quality(not recommended). | showCameraInfoAndPerformance | true/false; Default ```false``` | enables debug views for showing camera system details such as current FPS, Iso etc. | supportsSwitchFacing | true/false; Default ```true``` | defines if camera facing switching is available. | supportsAudioRateEqualsVideoSpeed | true/false; Default ```false``` | determines if the audio playback speed is equal to the video recording speed. | supportsGallery | true/false; Default ```true``` | defines if there is an icon on the camera screen at the bottom-right to pick a content from gallery. | videoDurations | List<Long>; Default ```listOf(maxRecordedTotalVideoDurationMs, 60_000L, 30_000L, 15_000L)``` | defines the list of durations available to record. The user can see the option on the camera screen and pick new option. For example, ```60000L``` means that the user can record a number of video with total duration no more than 60 seconds. | supportsVideoDurationSwitcher | true/false; Default ```true``` | defines if video recording time interval swithced is enabled ## Configure microphone state Use ```CameraMuteMicConfig``` if you want to customize default state of microphone on the camera screen. Here is default implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt) ```kotlin factory { CameraMuteMicConfig( // If mic should be muted when open camera screen in normal mode muteInNormalMode = false, // If mic should be muted when open camera screen in picture in picture mode muteInPipMode = true, // If mic should be muted when open camera screen with passed audio track muteWithAudioTrack = true ) } ``` ## Configure recording modes Camera screen includes 3 modes for recording content implemented as ```RecordMode``` - ```Photo``` - ```Video``` - ```Photo``` and ```Video``` - default value Implement ```CameraRecordingModesProvider``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt) to customize mode that meets your requirements. Default implementation is ```kotlin single { object : CameraRecordingModesProvider { override var availableModes = setOf(RecordMode.Video, RecordMode.Photo) } } ``` :::danger ```availableModes``` must not be empty, otherwise a crash will happen. ::: ## Picture in picture Picture in Picture or ```PIP``` is video editing technique that lets you overlay two videos in the same video. The multi-layer editing effect is perfect for reaction videos, slideshows, product demos, and more. This feature is similar to TikTok duet feature.   :::info The feature is disabled by default and can be enabled if the license supports it. Please ask Banuba business representatives to include the feature in your license. ::: The subsequent guide explains how to start and customize ```PIP```. First, pass ```pictureInPictureConfig``` in [VideoCreationActivity.startFromCamera](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/MainActivity.kt#L179-L194) method ```kotlin val localVideoUri = ... VideoCreationActivity.startFromCamera( context = this, // set PiP video configuration pictureInPictureConfig = PipConfig( video = localVideoUri, openPipSettings = false // if you want to open pip settings at startup ) ) ``` ```PIP``` includes 4 modes that you can use. - ```Floating``` - ```TopBottom``` - ```React``` - ```LeftRight``` Implement ```PipLayoutProvider``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt) to customize the order of modes and other capabilities. ```kotlin single { object : PipLayoutProvider { override fun provide( insetsOffset: Int, screenSize: Size ): List { val context = androidContext() return listOf( EditorPipLayoutSettings.Floating( context = context, physicalScreenSize = screenSize, topOffsetPx = context.dimen(R.dimen.pip_floating_top_offset) + insetsOffset ), EditorPipLayoutSettings.TopBottom(), EditorPipLayoutSettings.React( context = context, physicalScreenSize = screenSize, topOffsetPx = context.dimen(R.dimen.pip_react_top_offset) + insetsOffset ), EditorPipLayoutSettings.LeftRight() ) } } } ``` Please do not forget to update [CameraMuteMicConfig](#configure-microphone-state) implementation if you want to change use of microphone in ```PIP```. You can even customize camera align for each mode and exclude actions for some modes: ```diff ... return listOf( EditorPipLayoutSettings.Floating( ..., excludeActions = listOf( EditorPipLayoutAction.SwitchVertical, EditorPipLayoutAction.Square, EditorPipLayoutAction.Round ), // highlight-add-next-line + isCameraAlignTop = false ), EditorPipLayoutSettings.TopBottom( excludeActions = listOf( EditorPipLayoutAction.SwitchVertical, EditorPipLayoutAction.Original, EditorPipLayoutAction.Centered ), // highlight-add-next-line + isCameraAlignTop = false ), EditorPipLayoutSettings.React( ..., excludeActions = listOf( EditorPipLayoutAction.SwitchVertical, EditorPipLayoutAction.Square, EditorPipLayoutAction.Round, EditorPipLayoutAction.Centered, EditorPipLayoutAction.Original ), isCameraMain = false ), EditorPipLayoutSettings.LeftRight( excludeActions = listOf( EditorPipLayoutAction.SwitchHorizontal, EditorPipLayoutAction.Original, EditorPipLayoutAction.Centered ), // highlight-add-next-line + isCameraAlignLeft = false ) ) ``` --- ## Weatherman on Android Weatherman mode is an improvement on top of Banuba's cutting-edge background replacement. It allows users to drag and drop themselves on any place on the screen to emulate the look of a TV presenter the feature is named after. Weatherman is useful for reactions, learning content, presentations, explainers and other similar videos thanks to the professional feel it creates. :::important The Weatherman feature is disabled by default. Please contact Banuba representatives to know more about using this feature. :::     --- ## Install Photo Editor on Android Guide to installing Photo Editor SDK on Android. SDK modules are stored on GitHub Packages. ## Add repositories Open your project [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/build.gradle#L21) file and add repositories to ```allprojects``` section. ```groovy allprojects { repositories { maven { name = "nexus" url = uri("https://nexus.banuba.net/repository/maven-releases") } } } ``` ## Add dependencies Specify dependencies in the app [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L83) file. ```groovy def banubaPESdkVersion = '1.2.24' implementation "com.banuba.sdk:pe-sdk:${banubaPESdkVersion}" def banubaSdkVersion = '1.48.5' implementation "com.banuba.sdk:core-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:core-ui-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-gallery-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:effect-player-adapter:${banubaSdkVersion}" ``` Add ```kotlin-parcelize``` plugin into plugins section of the [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L63) file. ```groovy plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-parcelize' } ``` --- ## Install Video Editor on Android Guide to installing Video Editor SDK on Android. SDK modules are stored on GitHub Packages. ## Add repositories Open your project [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/build.gradle#L21) file and add repositories to ```allprojects``` section. ```groovy allprojects { repositories { maven { name = "nexus" url = uri("https://nexus.banuba.net/repository/maven-releases") } ... } } ``` ## Add Packaging Options Settings Specify the following ```packaging options``` in your [build gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L46-L53) file: ```groovy android { ... packagingOptions { jniLibs { useLegacyPackaging = true } } ... } ``` ## Add dependencies Specify dependencies in the app [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L63) file. ```groovy def banubaSdkVersion = '1.48.5' implementation "com.banuba.sdk:ffmpeg:5.3.0" implementation "com.banuba.sdk:camera-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:camera-ui-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:core-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:core-ui-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-flow-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-ui-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-gallery-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-effects-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:effect-player-adapter:${banubaSdkVersion}" implementation "com.banuba.sdk:ar-cloud:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-audio-browser-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-export-sdk:${banubaSdkVersion}" implementation "com.banuba.sdk:ve-playback-sdk:${banubaSdkVersion}" ``` Add ```kotlin-parcelize``` plugin into plugins section of the [gradle](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/build.gradle#L63) file. ```groovy plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-parcelize' } ``` --- ## Launching Photo Editor on Android Guide to launching Photo Editor SDK. ## Prerequisites :exclamation: The license token **IS REQUIRED** to use Photo Editor SDK in your app. Please check [Requirements](requirements-pe.md#license-token) out guide if the license token is not set. ## Initialize SDK Create an instance of ```BanubaPhotoEditor``` by using the license token ``` kotlin val photoEditorSDK = BanubaPhotoEditor.initialize(LICENSE_TOKEN) ``` ```photoEditorSDK``` is ```null``` when the license token is incorrect i.e. empty, truncated. If ```photoEditorSDK``` is not ```null``` you can proceed and start video editor. Next, we strongly recommend [checking](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/MainActivity.kt#L110) your license state before staring video editor ```kotlin photoEditorSDK.getLicenseState { isValid -> if (isValid) { // ✅ License is active, all good // Start Photo Editor SDK } else { // ❌ Use of Photo Editor is restricted. License is revoked or expired. } } ``` :::danger Photo editing content unavailable screen will appear if the user starts Photo Editor SDK with revoked or expired license. ::: ## Start editor Import classes ```kotlin ``` Start Photo Editor SDK and handle exported results. [See example](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/MainActivity.kt#L79). ```kotlin val photoEditorExportResult = registerForActivityResult(PhotoExportResultContract()) { uri -> // Handle exported image result Log.d(TAG, "Image exported $uri") } // Start Photo Editor SDK photoEditorExportResult.launch(PhotoCreationActivity.startFromGallery(this)) ``` --- ## Launching Video Editor on Android Guide to launching Video Editor SDK on Android. ## Prerequisites :exclamation: The license token **IS REQUIRED** to use Video Editor SDK in your app. Please check [Requirements](requirements-ve.md#license-token) out guide if the license token is not set. ## Update AndroidManifest Add ```VideoCreationActivity``` in your [AndroidManifest.xml](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/AndroidManifest.xml#L27) file. ``` xml ``` ```VideoCreationActivity``` is used for brining together and managing Video Editor flow. Each screen is implemented as an Android [Fragment](https://developer.android.com/guide/fragments). Next, add permissions ```xml ``` Network is used for downloading AR effects from [AR Cloud](https://www.banuba.com/faq/what-is-ar-cloud) and stickers from [Giphy](https://giphy.com/). [CustomIntegrationAppTheme](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/res/values/themes.xml#L13) is the custom implementation of ```VideoCreationTheme``` and is required for running ```VideoCreationActivity```. Use this implementation for customizing visual appearance of Video Editor SDK i.e. colors, icons and more. ## Configuration Custom behavior of Video Editor SDK in your app is implemented by using Dependency Injection framework [Koin](https://insert-koin.io/). Create new Kotlin class ```VideoEditorModule``` for configuring Video Editor SDK. Next, add new class ```SampleIntegrationKoinModule``` for initializing and customizing Video Editor SDK features. ``` kotlin class VideoEditorModule { ... private class SampleIntegrationKoinModule { val module = module { ... } } } ``` Add [initialize](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L47) method in ```VideoEditorModule``` class for initializing Video Editor SDK and specify ```SampleIntegrationKoinModule``` in the list of modules. ```kotlin fun initialize(applicationContext: Context) { startKoin { androidContext(applicationContext) allowOverride(true) // pass the customized Koin module that implements required dependencies. Keep order of modules modules( VeSdkKoinModule().module, VeExportKoinModule().module, VePlaybackSdkKoinModule().module, AudioBrowserKoinModule().module, // use this module only if you bought it ArCloudKoinModule().module, VeUiSdkKoinModule().module, VeFlowKoinModule().module, GalleryKoinModule().module, BanubaEffectPlayerKoinModule().module, + SampleIntegrationKoinModule().module, ) } } ``` Finally, [initialize SDK](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/SampleApp.kt#L42) in your Android Application ```onCreate()``` method. ``` kotlin override fun onCreate() { super.onCreate() VideoEditorModule().initialize(this) ... } ``` ## Setup export Video Editor can export a number of media files to meet your requirements. Implement ```ExportParamsProvider``` and provide ```List``` where every ```ExportParams``` is a media file i.e. video or audio. Check [CustomExportParamsProvider](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L119) implementation. ## Initialize with license Create an instance of ```BanubaVideoEditor``` by using the license token ``` kotlin val editorSDK = BanubaVideoEditor.initialize(LICENSE_TOKEN) ``` ```editorSDK``` is ```null``` when the license token is incorrect i.e. empty, truncated. If ```editorSDK``` is not ```null``` you can proceed and start video editor. :::tip Share the same instance of ```BanubaVideoEditor``` for Video Editor and Photo Editor SDK. ::: Next, we strongly recommend [checking](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/MainActivity.kt#L110) your license state before staring video editor ```kotlin editorSDK.getLicenseState { isValid -> if (isValid) { // ✅ License is active, all good // Start Video Editor SDK } else { // ❌ Use of Video Editor is restricted. License is revoked or expired. } } ``` :::warning Video content unavailable screen will appear if the user starts Video Editor SDK with revoked or expired license. ::: ## Start editor Start Video Editor SDK and handle exported results. [See example](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/MainActivity.kt#L25). ```kotlin val createVideoRequest = registerForActivityResult(IntegrationAppExportVideoContract()) { exportResult -> exportResult?.let { //handle ExportResult object } } val intent = VideoCreationActivity.startFromCamera( context = this, // set PiP video configuration pictureInPictureConfig = null, // setup what kind of action you want to do with VideoCreationActivity // setup data that will be acceptable during export flow additionalExportData = null, // set TrackData object if you open VideoCreationActivity with preselected music track audioTrackData = null ) createVideoRequest.launch(intent) ``` --- ## LLM and Vibe Coding on Android Banuba Video and Photo Editor SDK documentation is available in LLMs.txt format, optimized for AI. - [llms.txt](../../llms.txt) - [llms-full.txt](../../llms-full.txt) --- ## Banuba Photo Editor SDK Requirements on Android Guide contains general requirements for using Photo Editor SDK on Android. ## License Token Before you commit to a license, you are free to test all the features of the SDK for free. The trial period lasts 14 days. Send us a message to start the [Photo Editor SDK trial](https://www.banuba.com/photo-editor-sdk#form). We will get back to you with the trial token. Feel free to contact us if you have any questions regarding [Photo Editor SDK](https://www.banuba.com/support). ## Project settings This is what you need to run the AI Video Editor SDK: - Kotlin 1.8+ or Java 17 - Android OS 6.0 or higher with Camera 2 API - OpenGL ES 3.0 (3.1 for Neural networks on GPU) - :white_check_mark: arm64-v8a , :white_check_mark: armv7, :exclamation: x86-64 limited support, :x: x86 - no support ## Supported media formats | Images | | ----------- | | .jpg, .gif, .heic, .png, .nef, .cr2, .jpeg, .raf, .bmp ## SDK size | Photo Editor | Total size | |:------------:|:----------:| |:white_check_mark:| 65 MB | --- ## Android Requirements and Installation Guide # Banuba Video Editor SDK Requirements on Android Learn about the Android requirements and features for the Banuba Video Editor SDK. See supported versions, compatible devices, & other details. ## SDK integration video You can watch a follow along video below demonstrating key steps of SDK integration in a project. ## License Token Before you commit to a license, you are free to test all the features of the SDK for free. The trial period lasts 14 days. Send us a message to start the [Video Editor SDK trial](https://www.banuba.com/video-editor-sdk#form). We will get back to you with the trial token. Feel free to contact us if you have any questions regarding [Video Editor SDK](https://www.banuba.com/support). ## Project settings This is what you need to run the AI Video Editor SDK: - Kotlin 2.1+ or Java 17 - Android OS 8.0 or higher with Camera 2 API - OpenGL ES 3.0 (3.1 for Neural networks on GPU) - :white_check_mark: arm64-v8a , :white_check_mark: armv7, :exclamation: x86-64 limited support, :x: x86 - no support ## Supported media formats | Audio | Video | Images | |-------------------------------------| --------- | ----------- | | .aac, .mp3, .wav, .ogg, .m4a, .flac |.mp4, .mov | .jpg, .gif, .heic, .png, .nef, .cr2, .jpeg, .raf, .bmp ## SDK size Banuba Video Editor SDK with all the core editing features only adds 25 Mb of space to your app’s download size. This parameter can change based on the number of features you want. |SDK| Total size | |:------------|:----------:| |Banuba Video Editor SDK (core editor)| 25 Mb | |Banuba Video Editor SDK (full bundle)| 45 Mb | |Video Editor with Photo Editor | 70 Mb | |Video Editor with face augmented reality features | 45 - 90 Mb | --- ## FAQ on Android These are the answers to the most common questions asked about our SDK. ### I want to start VideoEditor with a preselected audio track To open Video Editor SDK you should create an intent by utilizing any avilable function inside **VideoCreationActivity**: **startFromCamera()**, **startFromTrimmer()** or **startFromEditor()**. All these functions have an argument called **audioTrackData** where you should pass preselected audio track or null (by default). For example, to open an SDK from the camera screen with the track use the code snippet below: ```kotlin startActivity( VideoCreationActivity.startFromCamera( context = applicationContext, audioTrackData = preselectedTrackData ) ) ``` **audioTrackData** is an object of TrackData class ```kotlin data class TrackData( val id: UUID, val title: String, val localUri: Uri, val artist: String? = null ) ``` ### How to add other text fonts that are used in the editor screen To add other text fonts that are used in the editor screen follow the next steps: 1. Add font files to the `app/src/main/res/font/` directory; 2. Add fonts names to the ```strings.xml ``` resource file: ```xml Font 1 Title Font N Title ``` 3. Add `font_resources.xml` with fonts array declaration to the `app/src/main/res/values/` directory. The format of `font_resources.xml` should be the next one: ```xml @array/font_1_resource @array/font_N_resource @string/font_1_title @font/font_1 @string/font_N_title @font/font_N ``` 4. The final step is to pass your custom `font_resources` id to the `MainTextOnVideoTypefaceProvider` in the ```SampleIntegrationKoinModule``` to override the default implementation: ```kotlin single { MainTextOnVideoTypefaceProvider( context = get(), fontsArrayResId = R.array.font_resources ) } ``` ### Optimizing app size The easiest way to gain immediate app size savings when publishing to Google Play is by uploading your app as an [**Android App Bundle**](https://developer.android.com/guide/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 ### How do I change the language (how do I add new locale support)? There is no special language switching mechanism in the Video Editor SDK. Out of the box, the VE SDK supports `English` locale. 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](https://developer.android.com/training/basics/supporting-devices/languages#CreateDirs) 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. 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](https://www.geeksforgeeks.org/how-to-change-the-whole-app-language-in-android-programmatically/), [two](https://medium.com/swlh/android-app-specific-language-change-programmatically-using-kotlin-d650a5392220) ### How do I use the Video Editor several times from different entry points? Before you want to use VideoEditor again, you need to release ```Video Editor SDK```: ```kotlin private fun releaseVideoEditor() { releaseUtilityManager() stopKoin() videoEditor = null } private fun releaseUtilityManager() { val utilityManager = try { getKoin().getOrNull() } catch (e: InstanceCreationException) { Log.w(TAG, "EditorUtilityManager was not initialized!", e) null } utilityManager?.release() } ``` ```EditorUtilityManager``` is NULL when the token is expired or revoked. ### I want to change icons and name for effects. Effect customization is implemented by android resources with well-defined names which follow a strict scheme. | Customization | Resource type | Resource name template | Example | :---: | :---: | :---: | :---: | | Title | string | visual_effect_\{id\}, time_effect_\{id\} | visual_effect_flash | Icon | drawable | ic_visual_effect_\{id\}, ic_time_effect_\{id\} | ic_time_effect_rapid | Color | color | visual_effect_color_\{id\}, time_effect_color_\{id\} | visual_effect_color_vhs To change appearance o the effect, you should place any android resources named according to the scheme presented above into res folder of your app. The resource depends on the item you want to customize (string for the title, drawable for the icon, color for the color on the timeline). Effects identifiers are presented in the table below: | Effect | Type | String identifier | Default icon | | :---------- | :---:| :--------------: | :----------: | | Acid-whip | visual | acid_whip| | Cathode | visual | cathode | | Flash | visual | flash | | Glitch | visual | glitch | | Glitch 2 | visual | glitch2 | | Glitch 3 | visual | glitch3 | | Heat map | visual | heat_map | | DSLR Kaleidoscope | visual | dslr_kaleidoscope | | Kaleidoscope | visual | kaleidoscope | | Lumiere | visual | lumiere | | Pixel dynamic | visual | pixel_dynamic | | Pixel static | visual | pixel_static | | Polaroid | visual | polaroid | | Rave | visual | rave | | Soul | visual | soul | | Stars | visual | stars | | Transition 1 | visual | transition1 | | Transition 2 | visual | transition2 | | Transition 3 | visual | transition3 | | Transition 4 | visual | transition4 | | TV Foam | visual | tv_foam | | DV Cam | visual | dv_cam | | VHS | visual | vhs | | VHS 2 | visual | vhs2 | | Zoom | visual | zoom | | Zoom 2 | visual | zoom2 | | Slow mo | time | slow_motion | | Rapid | time | rapid | For example, to change the title of ```Flash``` visual effect to ```SuperFlash``` add it in ```strings.xml``` in your app. ```xml SuperFlash ``` You can change the color on the timeline for any visual effect. For example, for ```VHS``` you should add color resource with the name ```visual_effect_color_vhs``` into ```colors.xml``` file in your app. ### I want to change the order of masks, video effects or filters. The SDK allows to reorder masks and filters in the way you need. To achieve this, create a class ```CustomColorFilterOrderProvider``` and implement ```OrderProvider```: ```kotlin class CustomColorFilterOrderProvider : OrderProvider { override fun provide() = listOf( "egypt", "byers", "chile", "hyla", "new_zeland", "korben", "canada", "remy", "england", "retro", "norway", "neon", "japan", "instant", "lux", "sunset", "bubblegum", "chroma", "lilac", "pinkvine", "spark", "sunny", "vinyl", "glitch", "grunge" ) } ``` :::important These are names of specific color filters located in ```assets/bnb-resources/luts```. ::: Next, use ```CustomColorFilterOrderProvider``` in [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L60) ```kotlin single(named("colorFilterOrderProvider")) { CustomColorFilterOrderProvider() } ``` ### Is it possible to disable the transition effects? Transitions are visual effects applying to the segue between two videos. They are provided with the Banuba Video Editor SDK **by default**. To disable or enable transitions, set the `supportsTransitions` flag in the `EditorConfig` class to false or true respectively: ``` kotlin single { EditorConfig(supportsTransitions = false) } ``` :::important Transition effects are not being played if the closest video (either to the left or to the right of the transition icon) is very short. ::: ### How can I convert one or several still images to a video programmatically? If you would like to create a video from the images instances without opening the Video Editor, you can use the methods from the ```Utils``` object in the [GitHub Sample](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/Utils.kt#L72-L131). ### How to change the style appearance of the SDK? Extend ```VideoCreationTheme``` style to customize Video Editor appearance for your app. ```xml ``` Specify your style in ```AndroidManifest.xml``` file for ```VideoCreationActivity```. ```xml ``` [GitHub example](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/res/values/themes.xml) ### Is it possible to disable the Trim screen? It is possible to turn off the trimmer screen after the camera screen. The trimmer screen will still be accessible after importing media files from the gallery. To disable it, just change the `supportsTrimRecordedVideo` property to `false` in the `EditorConfig`: ``` kotlin single { EditorConfig(supportsTrimRecordedVideo = false) } ``` ### How to change the video scaling on the editor screen? Yon can change video scaling on the editor screen while playback by providing ```PlayerScaleType``` [VideoEditorModule](https://github.com/Banuba/ve-sdk-android-integration-sample/blob/main/app/src/main/java/com/banuba/example/integrationapp/VideoEditorModule.kt#L60). To ensure that the video will be fully shown - use ```CENTER_INSIDE``` (keep in mind that if device and video resolutions are different black lines will appear), to fill the screen - use ```FIT_SCREEN_HEIGHT``` (it fills the screen only if video has aspect ratio 9:16) ``` kotlin factory(named("editorVideoScaleType"), override = true) { PlayerScaleType.CENTER_INSIDE } ``` The default value is ```PlayerScaleType.FIT_SCREEN_HEIGHT```. ### How to collect logs when I encounter an issue? If you encounter a crash or other issue and are unsure how to provide logs to the support team, please refer to the official Android documentation on how to capture a bug report: [https://developer.android.com/studio/debug/bug-report](https://developer.android.com/studio/debug/bug-report) After following the instructions, please send the generated bug report file to the [support team](https://www.banuba.com/support) for further investigation. ### FFmpeg build issue Below are the steps to resolve the issue while building the project. 1. Add the ```android.bundle.enableUncompressedNativeLibs=false``` in the ```gradle.properties``` ``` properties android.bundle.enableUncompressedNativeLibs=false ``` 2. Add ```android:extractNativeLibs="true"``` in ```AndroidManifest.xml``` file ``` xml ``` ### How to integrate custom FFmpeg dependency. Check out [step-by-step guide](ffmpeg.md) to integrate custom FFmpeg dependency. --- ## Video Templates on Android Templates let users create stunning videos quickly and easily using predefined sets of effects, transitions, and music. All it takes to make a shareable piece is changing the placeholders. With templates, even people who are new to video editing or just lack time can make impressive content in minutes.       :::important The ```Video Templates``` is not enabled by default. Contact Banuba representatives to know more. ::: ## Launch Video Templates Use a new entry point in `VideoCreationActivity` to launch the Video Editor SDK from Video templates. ```kotlin val intent = VideoCreationActivity.startFromTemplates(Context) ``` --- ## Overview Video Editor SDK on iOS Banuba Video Editor SDK has built in UI/UX experience and provides a number of customizations you can use to meet your requirements. **AVAILABLE:** :white_check_mark: Use your branded icons, colors, and text styles. [See details](ve-faq.md#i-want-to-use-custom-icons) :white_check_mark: Localize and change text resources. Default locale is :us: :white_check_mark: Make content you want i.e. a number of videos with different resolutions and durations, an audio file. [See details](guide_export.md) :white_check_mark: Masks and filters order. [See details](ve-faq.md#i-want-to-change-the-order-of-masks-video-effects-or-filters) **NOT AVAILABLE:** :x: Change layout or order of the screens after the entry point. You can, however, [ask](https://www.banuba.com/support) us to customize the mobile Video Editor UI as a separate contract. --- ## AI Clipping on iOS The [AI Clipping](https://www.banuba.com/ai-sdk) feature automates the video creation process by leveraging the power of artificial intelligence. The neural network transforms raw input into ready-to-post videos with various transitions, effects, and a precise music match. :::important The ```AI Clipping``` is based on the [Banuba Face AR SDK](https://www.banuba.com/facear-sdk/face-filters) product. Since this feature uses additional services, it is disabled by default. Please contact Banuba representatives to know more about using this feature. ::: Here is how users can interact with it: 1. User selects clips and music. People can choose their desired clips and accompanying music within the app. 2. AI trims the videos. The AI algorithm intelligently trims the selected clips to match the tempo and rhythm of the chosen track. 3. AI adds various effects. AI Clipping enhances the content by adding a variety of effects to create visually stunning content. 4. User exports, edits, or regenerates the clip. Upon completion, users have the flexibility to export the video as is, make further edits, or regenerate the clip for a fresh perspective. Regardless of the user’s editing skills, with the help of AI Clipping, everyone can get a stunning video within seconds. ## Supported Music Providers - [Banuba Music](guide_audio_content.md#connect-banuba-music) - [Soundstripe](guide_audio_content.md#connect-soundstripe)   ## Integration ### Setup configuration Add ```Banuba Face AR SDK``` dependency by following the [Face AR instruction](guide_far_arcloud.md#integrate-face-ar). Set up ```embeddingsDownloadUrl``` and ```musicProvider``` values in [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L73) :::important Contact Banuba representative to get trial keys for ```tracksURL``` and ```musicProvider``` ::: #### Config with Banuba Music provider ```swift let videoEditorConfig = VideoEditorConfig() videoEditorConfig.aiClippingConfiguration.embeddingsDownloadUrl = "https://d27n29bgbvbeer.cloudfront.net/index-staging.zip" videoEditorConfig.aiClippingConfiguration.musicProvider = .banubaMusic(tracksURL: URL(string: "https://d27n29bgbvbeer.cloudfront.net/response.json")!) ``` #### Config with Soundstripe provider ```swift let videoEditorConfig = VideoEditorConfig() videoEditorConfig.aiClippingConfiguration.embeddingsDownloadUrl = "..." videoEditorConfig.aiClippingConfiguration.musicProvider = .soundstripe(tracksURL: URL(string: "...")!) ``` ### Launch AI Clipping :::info Video creation with AI Clipping is available on Gallery screen by default. ::: For better experience we added new entry point ```.aiClipping``` to ```VideoEditorLaunchConfig``` for opening AI Clipping as separate mode. In this scenario the user starts from the gallery screen and is taken to AI Clipping screen after selecting media. ```swift let launchConfiguration = VideoEditorLaunchConfig( entryPoint: .aiClipping, hostController: self, animated: true ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfiguration, completion: nil ) ``` --- ## Closed Captions on iOS Closed captions(CC) are a textual representation of the audio within a media file. :::important The Close Captions feature is disabled by default. Please contact Banuba representatives to know more about using this feature. ::: Over 80% of videos played on mobile devices don’t have sound turned on. This means many forms of content (e.g. skits, monologues, educational clips, etc.) will be skipped if there are no subtitles. But making captions by hand is tedious. AI-generated subtitles solve this issue, as they are created and placed automatically. The users can then edit the text as well as change its style and color.       [AWS Transcribe service](https://docs.aws.amazon.com/transcribe/) is used to generate captions. ## Supported languages - Arabic - English - Mandarin - Spanish - Portuguese ## Integration Set up ```captionsUploadUrl```, ```captionsTranscribeUrl``` and ```apiKey``` values in the VideoEditorModule. ### Closed Captions V2 (Recommended) :::important Request API V2 key from Banuba representatives. ::: ```swift config.captionsConfiguration.apiV2Key = "..." ``` ### Closed Captions V1 :::important Request keys from Banuba representatives. ::: ```swift config.captionsConfiguration.captionsUploadUrl = "..." config.captionsConfiguration.captionsTranscribeUrl = "..." config.captionsConfiguration.apiKey = "..." ``` --- ## Installation with CocoaPods Learn the [CocoaPods Getting Started Guide](https://guides.cocoapods.org/using/getting-started.html) if you are new to CocoaPods. :::info It is advised to use the latest version of CocoaPods. Please, check your version with ```pod --version``` and upgrade if necessary. ::: The List of the required Video Editor dependencies is present in the sample project's [Podfile](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Podfile). Complete the following steps to get the Video Editor SDK dependencies using CocoaPods: 1. install CocoaPods using Homebrew: ```sh brew install cocoapods ``` 2. initialize the pods in your project folder: ```sh pod init ``` 3. add the necessary frameworks and podspec sources to the Podfile of your project: ```ruby source 'https://cdn.cocoapods.org/' source 'https://github.com/Banuba/specs.git' source 'https://github.com/sdk-banuba/banuba-sdk-podspecs.git' banuba_sdk_version = '1.48.2' pod 'BanubaVideoEditorSDK', banuba_sdk_version pod 'BanubaSDKSimple', banuba_sdk_version pod 'BanubaSDK', banuba_sdk_version pod 'BanubaARCloudSDK', banuba_sdk_version # optional pod 'BanubaAudioBrowserSDK', banuba_sdk_version # optional ``` 4. install the Video Editor SDK pods: ```sh pod install --repo-update ``` 5. open ```Example.xcworkspace``` in Xcode and run the project. --- ## Audio content on iOS Guide to integrating and customizing audio providers in Video Editor SDK. ## Overview Audio content is the key part of making an awesome video. The Video Editor SDK can play, trim, merge and add audio content to a video. :::info 1. Banuba does not deliver audio content for the Video Editor SDK. 2. The Video Editor can apply audio files stored on the device. The SDK is not responsible for downloading audio content except for [Soundstripe](https://www.soundstripe.com/) and [Banuba Music](#connect-banuba-music). 3. Video Editor SDK supports only one music provider per launch. ::: There are 2 approaches to using audio content: 1. ```AudioBrowser``` - a specific module and set of screens that include the built-in support of browsing and applying audio content within the Video Editor. The user does not leave the SDK while using audio. 2. ```External API``` - the client implements a specific API for managing audio content. The user leaves the SDK and is taken to an app screen when audio is requested. ## Audio Browser Audio Browser is a specific iOS module that allows to browse, play and apply audio content within the Video Editor. It supports 3 sources for audio content: 1. ```Banuba Music``` - includes build in integration with Banuba Music, 2. ```Soundstripe``` - includes the built-in integration with the [Soundstripe](https://www.soundstripe.com/) API, 3. ```My Library``` - includes audio content available on the user's device. Add the dependency below into your [Podfile](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Podfile#L16) to integrate ```AudioBrowser```: ```swift pod 'BanubaAudioBrowserSDK', banuba_sdk_version ``` If you are using Swift Package Manager, please, visit the [SPM integration page](spm-installation.md). ## Connect Banuba Music Over 35 GB of royalty-free tracks available from within the Video Editor SDK. Your users could check them out through an inbuilt music browser and legally include them in their content. :::info The feature is not activated by default. Please contact Banuba representatives to know more about using this feature. :::   Use ```BanubaMusicProvider``` implementation in [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L81): ```Swift AudioBrowserConfig.shared.musicSource = .banubaMusic ``` ## Connect Soundstripe [Soundstripe](https://www.soundstripe.com/) is a service for providing the best audio tracks for creating video content. Your users will be able to add audio tracks while recording or editing video content. :::info The feature is not activated by default. Please, contact Banuba representatives to know more about using this feature. :::     Set the ```.soundstripe``` source in the ```AudioBrowserConfig``` configuration to enable ```Soundstripe```. For example, in [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L81): ```Swift AudioBrowserConfig.shared.musicSource = .soundstripe ``` ## Connect My Library ```My Library``` is a default implementation in ```AudioBrowser``` . It allows a user to apply audio that is available on the device. Set the ```.localStorageWithMyFiles``` source in the ```AudioBrowserConfig``` configuration to enable ```My Library```. For example, in [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L100): ```Swift AudioBrowserConfig.shared.musicSource = .localStorageWithMyFiles ``` ## Connect External API The Video Editor includes a special API for integrating your custom audio content provider and applying this content to the Video Editor. The user will be taken to your app's specific screen when audio is requested on the Video Editor screen i.e. the camera or editor. Next, once the user picks audio content on your app screen, you need to follow API and return the user to the Video Editor. Any audio file should be stored on the device before applying. To pass audio content to the Video Editor you need to implement a factory that conforms to the `MusicEditorExternalViewControllerFactory` protocol. And put it to the `musicEditorFactory` property in `ExternalViewControllerFactory`. Your factory should implement the following methods: ```swift protocol MusicEditorExternalViewControllerFactory: AnyObject { /// contoller which will be used for presenting otherwise makeTrackSelectionViewController will be used var audioBrowserController: TrackSelectionViewController? { get set } /// should returns controller which provides audio content for Video Editor SDK /// - selectedAudioItem is currently selected audio item func makeTrackSelectionViewController(selectedAudioItem: AudioItem?) -> TrackSelectionViewController? /// should returns controller which provides effects audio content for Video Editor SDK /// Note: MainMusicViewControllerConfig should contains editButton with type .effect func makeEffectSelectionViewController(selectedAudioItem: AudioItem?) -> EffectSelectionViewController? /// should returns view for countdown animation in record button at music editor func makeRecorderCountdownAnimatableView() -> MusicEditorCountdownAnimatableView? } ``` where `AudioItem` is an entity which includes information about the selected audio item in the Video Editor: ```swift // MARK: - AudioItem protocol protocol AudioItem { var uuid: UUID { get } var url: URL { get } var coverURL: URL? { get } var title: String? { get set } var additionalTitle: String? { get set } /// True - display track with which the video was recorded and allow users to edit it. /// False - track will be playing but not displayed. var isEditable: Bool { get set } } ``` Your custom audio browser implementation should conform to the `TrackSelectionViewController` protocol: ```swift class YourCustomAudioBrowser: UIViewController, TrackSelectionViewController { weak var trackSelectionDelegate: TrackSelectionViewControllerDelegate? } ``` Using `trackSelectionDelegate` you can notify the Video Editor about actions in the audio browser with the following methods: ```swift protocol TrackSelectionViewControllerDelegate: AnyObject { func trackSelectionViewController( viewController: TrackSelectionViewController, didSelectFile url: URL, coverURL: URL?, timeRange: CMTimeRange?, isEditable: Bool, title: String, additionalTitle: String?, uuid: UUID ) func trackSelectionViewControllerDidCancel( viewController: TrackSelectionViewController ) func trackSelectionViewControllerDiscardCurrentTrack( viewController: TrackSelectionViewController ) } ``` ## Connect Apple Music You can pass music from Apple Music to the Video Editor SDK. You should export media to a temporary directory and then pass the music url to the Video Editor SDK using `trackSelectionDelegate`. In this sample, we show how to implement it: ```swift let asset = AVURLAsset(url: url) let destination = FileManager.default .temporaryDirectory .appendingPathComponent("\(NSUUID().uuidString).caf") let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough) exportSession?.outputURL = destination exportSession?.outputFileType = AVFileType.caf exportSession?.exportAsynchronously() { if let error = exportSession?.error { completion(nil, error as NSError) } else { completion(destination, nil) } } ``` To present your custom audio browser in the Video Editor you need to set the created `ExternalViewControllerFactory` to [externalViewControllerFactory](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L29). --- ## Camera screen on iOS Guide to modifying camera UI elements and screen configuration. ## Change icons You can use your app specific icons in Video Editor SDK. Add icon files to Xcode ```Assets``` folder with predefined names.   To add custom icons, use the ```AdditionalEffectsButtonConfiguration``` within ```RecorderConfiguration```, ```TimerConfiguration``` and ```SpeedBarButtonsConfiguration```. Refer to the configuration below. To exclude any button, just don't include it in the ```additionalEffectsButtons``` array. ```swift var config = VideoEditorConfig() config.recorderConfiguration.additionalEffectsButtons = [ AdditionalEffectsButtonConfiguration( identifier: .toggle, imageConfiguration: ImageConfiguration(imageName:"toggle.png"), selectedImageConfiguration: ImageConfiguration(imageName: "toggleSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .flashlight, imageConfiguration: ImageConfiguration(imageName:"flashlight.png"), selectedImageConfiguration: ImageConfiguration(imageName:"flashlightSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .speed, imageConfiguration: ImageConfiguration(imageName:"speed.png"), selectedImageConfiguration: ImageConfiguration(imageName: "speedSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .sound, imageConfiguration: ImageConfiguration(imageName:"sound.png"), selectedImageConfiguration: ImageConfiguration(imageName: "soundSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .muteSound, imageConfiguration: ImageConfiguration(imageName:"muteSound.png"), selectedImageConfiguration: ImageConfiguration(imageName:"muteSoundSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .pip, imageConfiguration: ImageConfiguration(imageName:"pip.png"), selectedImageConfiguration: ImageConfiguration(imageName:"pipSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .beauty, imageConfiguration: ImageConfiguration(imageName:"beauty.png"), selectedImageConfiguration: ImageConfiguration(imageName: "beautySelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .effects, imageConfiguration: ImageConfiguration(imageName:"effects.png"), selectedImageConfiguration: ImageConfiguration(imageName:"effectsSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .masks, imageConfiguration: ImageConfiguration(imageName:"masks.png"), selectedImageConfiguration: ImageConfiguration(imageName: "masksSelected.png") ), AdditionalEffectsButtonConfiguration( identifier: .timer, imageConfiguration: ImageConfiguration(imageName:"timer.png"), selectedImageConfiguration: ImageConfiguration(imageName: "timerSelected.png") ) ] config.recorderConfiguration.timerConfiguration.defaultButton = ImageButtonConfiguration( imageConfiguration: ImageConfiguration(imageName:"timer.png") ) config.recorderConfiguration.speedBarButtons = SpeedBarButtonsConfiguration( imageHalf: ImageConfiguration(imageName:"imageHalf.png"), imageNormal: ImageConfiguration(imageName:"imageNormal.png"), imageDouble: ImageConfiguration(imageName:"imageDouble.png"), imageTriple: ImageConfiguration(imageName:"imageTriple.png"), selectedTitleColor: UIColor, titleColor: UIColor, backgroundColor: UIColor, cornerRadius: CGFloat ) ``` --- ## Cover image on iOS Cover image screen allows users to pick any frame of video as image or choose an image from gallery. This screen can be turned on or off by changing the `isVideoCoverSelectionEnabled` field of `FeatureConfiguration`: ```swift let config = VideoEditorConfig() config.featureConfiguration.isVideoCoverSelectionEnabled = false ``` To change the UI elements of the screen (icons, colors, etc.), please, modify the default values of `VideoCoverSelectionConfiguration`: ```swift let config = VideoEditorConfig() config.extendedVideoCoverSelectionConfiguration.doneButton.textConfiguration?.color = UIColor.red ``` You can override the following string resources in your app: | Key | Default value | | ------------- | ----------- | | Choose Cover Screen Name | Thumbnail | | cover.thumbnail.title | Choose a thumbnail | | cover.gallery.button.title | Choose from Gallery | | cover.delete.button.title | Delete | | Cancel | Cancel | | Done | Done | --- ## Drafts screen on iOS Guide to integrating and customizing drafts screen on Video Editor SDK. ## Configuration Drafts are enabled by default, 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 [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L83): ```swift var config = VideoEditorConfig() config.featureConfiguration.draftsConfig = .enabled ``` You can choose one of these options: - ```.enabled``` - drafts enabled, asks the user to save a draft - ```.enabledSaveToDraftsByDefault``` - drafts enabled, saved by default without asking the user - ```.enabledAskIfSaveNotExport``` - drafts enabled, asks the user to save a draft without export - ```.disabled``` - disabled drafts The default value is ```.enabled``` ## Draft Service The `DraftsService` class is used for managing drafts: ```swift /// External Draft of video session typealias ExternalDraft = VideoSequence /// Allows you to manage drafts class DraftsService { /// Get drafted video sequences func getDrafts() -> [ExternalDraft] /// Remove specific video sequence /// - parameters: /// - externalDraft: Drafted video sequence func removeExternalDraft(_ externalDraft: ExternalDraft) -> Bool /// Get preview for specific drafted video sequence /// - parameters: /// - externalDraft: Drafted video sequence /// - thumbnailHeight: Preview height /// - completion: Completion when preview UIImage generated func getPreviewForVideoSequence( _ externalDraft: ExternalDraft, thumbnailHeight: CGFloat, completion: ((_ preview: UIImage?) -> Void)? ) } ``` To get the instance of `DraftsService` use the `BanubaVideoEditor` instance. ***Example of usage:*** ```swift let videoEditorSDK = BanubaVideoEditor(...) // Initialization of main entity let drafts = videoEditorSDK?.draftsService.getDrafts() // Get drafts list let draft = drafts.first! // Get draft preview videoEditorSDK?.draftsService.getPreviewForVideoSequence( // Choosen draft from list of drafts draft, // Default config, where config is VideoEditorConfig instance thumbnailHeight: config.videoResolutionConfiguration.currentThumbnailHeight, completion: { preview in // Preview usage } ) // Remove draft and use returned value to your own condition usage if needed let _ = videoEditorSDK?.draftsService.removeVideoSequence(videoSequence) // Open Video Editor with preselected draft let draftedConfig = VideoEditorLaunchConfig.DraftedLaunchConfig( // Choosen draft from list of drafts externalDraft: draft, // Any case from DraftsFeatureConfig entity draftsConfig: .enabled ) let config = VideoEditorLaunchConfig( entryPoint: .editor, hostController: self, draftedLaunchConfig: draftedConfig, animated: true ) // Present Video Editor self.videoEditorSDK?.presentVideoEditor( withLaunchConfiguration: config, completion: nil ) ``` ## Transferring drafts BanubaVideoEditor supports archiving drafts into a zip file. It may help you with backing up user data to the server or sharing video content across the devices. For exporting and importing `BanubaVideoEditor` has the following methods: ```swift /// Archive and compress external draft into zip file. /// Returns URL to archive /// Discussion: After performing all actions with zip file your are responsible to remove archive file. func exportExternalDraft(_ externalDraft: ExternalDraft) throws -> URL /// Unarchive ExternalDraft from zip file func importExternalDraft(fromZipUrl url: URL) throws -> ExternalDraft ``` ***Example of usage:*** ```swift let videoEditorSDK = BanubaVideoEditor(...) // Initialization of main entity let drafts = videoEditorSDK?.draftsService.getDrafts() // Get drafts list let draft = drafts.first! // Make archive from draft let archiveURL = try? videoEditorSDK?.exportExternalDraft(draft) // Import draft from archive let importedDraft = try! videoEditorSDK?.importExternalDraft(fromZipUrl: archiveURL) // Open Video Editor with unarchived draft let draftedConfig = VideoEditorLaunchConfig.DraftedLaunchConfig( externalDraft: importedDraft, draftsConfig: .enabled ) let config = VideoEditorLaunchConfig( entryPoint: .editor, hostController: self, draftedLaunchConfig: draftedConfig, animated: true ) self.videoEditorSDK?.presentVideoEditor( withLaunchConfiguration: config, completion: nil ) ``` --- ## Editor screen on iOS Guide to integrating and customizing Editor screen. ## Change icons You can use your app specific icons in Video Editor SDK. Add icon files to Xcode ```Assets``` folder with predefined names.   To add custom icons, use the ```AdditionalEffectsButtonConfiguration``` within ```EditorConfiguration```. Refer to the configuration below. To exclude any button, just don't include it in the ```additionalEffectsButtons``` array. ```swift var config = VideoEditorConfig() config.editorConfiguration.additionalEffectsButtons = [ AdditionalEffectsButtonConfiguration( identifier: .sticker, imageConfiguration: ImageConfiguration(imageName: "stickers"), selectedImageConfiguration: ImageConfiguration(imageName: "stickersSelected") ), AdditionalEffectsButtonConfiguration( identifier: .text, imageConfiguration: ImageConfiguration(imageName: "text"), selectedImageConfiguration: ImageConfiguration(imageName: "textSelected") ), AdditionalEffectsButtonConfiguration( identifier: .captions, imageConfiguration: ImageConfiguration(imageName: "captions"), selectedImageConfiguration: ImageConfiguration(imageName: "captionsSelected") ), AdditionalEffectsButtonConfiguration( identifier: .effects, imageConfiguration: ImageConfiguration(imageName: "effects"), selectedImageConfiguration: ImageConfiguration(imageName: "effectsSelected") ), AdditionalEffectsButtonConfiguration( identifier: .masks, imageConfiguration: ImageConfiguration(imageName: "masks"), selectedImageConfiguration: ImageConfiguration(imageName: "masksSelected") ), AdditionalEffectsButtonConfiguration( identifier: .sound, imageConfiguration: ImageConfiguration(imageName: "music"), selectedImageConfiguration: ImageConfiguration(imageName: "musicSelected") ), AdditionalEffectsButtonConfiguration( identifier: .time, imageConfiguration: ImageConfiguration(imageName: "time"), selectedImageConfiguration: ImageConfiguration(imageName: "timeSelected") ), AdditionalEffectsButtonConfiguration( identifier: .color, imageConfiguration: ImageConfiguration(imageName: "filters"), selectedImageConfiguration: ImageConfiguration(imageName: "filtersSelected") ), AdditionalEffectsButtonConfiguration( identifier: .blur, imageConfiguration: ImageConfiguration(imageName: "blur"), selectedImageConfiguration: ImageConfiguration(imageName: "blurSelected") ), AdditionalEffectsButtonConfiguration( identifier: .autoCut, imageConfiguration: ImageConfiguration(imageName: "autocut"), selectedImageConfiguration: ImageConfiguration(imageName: "autocutSelected") ) ] ``` --- ## NEW Editor screen on iOS Guide to integrating and customizing New Editor screen. ## Overview With the new interface, better controls, and additional quality of life improvements, making stunning videos is easier and more fun than ever. Design and user experience principles are constantly evolving. To keep up with the latest developments and best practices, our team has completely redesigned the Video Editor SDK to be as convenient and enjoyable as possible.       ## Integration Init the Video Editor with argument ```[.useEditorV2: true]``` to enable ```Editor UI V2```: ```swift let videoEditorSDK = BanubaVideoEditor( token: token, arguments: [.useEditorV2: true], configuration: createConfiguration() ) ``` --- ## Export media on iOS Video Editor SDK allows to export a number of media files i.e. video and audio with various resolutions and other configurations. Video is exported as ```.mp4``` file. :::warning Export is a very heavy computational task that takes time and the user has to wait for it to be done. The execution time depends on: 1. the video duration - the longer the video, the longer the execution time; 2. a number of video sources - the more sources, the longer the execution time; 3. a number of effects and their usage in the video - the more effects and the bigger their usage, the longer the execution time; 4. a number of the exported videos - the more videos and audios you want to export, the longer the execution time; 5. the device hardware - the most powerful devices can execute export much quicker. ::: Export only supports the ` Foreground` mode where the user has to wait on the progress screen until the processing is done. The ` Background` export is not allowed on iOS due to the [iOS Restrictions](https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/preparing_your_metal_app_to_run_in_the_background) Here is a screen that is shown in the ` Foreground` mode: ## Video codecs The Video Editor supports the following video codec options: 1. ` HEVC` - H265 codec. ` Default` if the device supports this codec; 2. ` AVC_PROFILES` - H264 codec with High Profile. You can change the video codec for export by using the ` ExportVideoConfiguration.useHEVCCodecIfPossible` property. In this example, the H264 is set in [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L56): ```diff let exportConfiguration = ExportVideoConfiguration( fileURL: destFile, quality: .auto, // highlight-add-next-line + useHEVCCodecIfPossible: false, watermarkConfiguration: watermarkConfiguration ) ``` ## Video quality | 360p(360x640) | 480p(480x854) | QHD540(540x960) | HD(720x1280) | FHD(1080x1920) | QHD(1440x2560) | UHD(2160x3840) | |----|---------------|-----------------|--------------|----------------|----------------|----------------| | 1000 kb/s | 2500 kb/s | 3000 kb/s | 5000 kb/s | 8000 kb/s | 16000 kb/s | 36000 kb/s | The Video Editor has the built-in feature for detecting device performance capabilities and finding optimal video quality params for export. You can provide a custom video resolution for the exported video by using the ` ExportVideoConfiguration.quality` property: ```diff let hdExportConfiguration = ExportVideoConfiguration( fileURL: destFile, // highlight-add-next-line + quality: .videoConfiguration(ExportVideoInfo(resolution: .hd1280x720, useHEVCCodecIfPossible: true)), useHEVCCodecIfPossible: true, watermarkConfiguration: watermarkConfiguration ) ``` ## Export storage The Video Editor export method requires the file destination where the exported video will be stored. First, create the destination file: ```swift let manager = FileManager.default let destFile = manager.temporaryDirectory.appendingPathComponent("tmp.mov") ``` and use it in ` ExportVideoConfiguration`: ```diff let hdExportConfiguration = ExportVideoConfiguration( // highlight-add-next-line + fileURL: destFile, quality: .videoConfiguration(ExportVideoInfo(resolution: .hd1280x720, useHEVCCodecIfPossible: true)), useHEVCCodecIfPossible: true, watermarkConfiguration: watermarkConfiguration ) ``` ## Implement the export flow You can create your own flow for exporting media files for your application. Use ` ExportConfiguration` to create an instance with the configurations that meet your product requirements. Below there is a sample that exports 2 video files: 1. a video with the auto resolution **without** a watermark; 2. a video with the HD resolution **with** a watermark: ```Swift let watermarkConfiguration = WatermarkConfiguration( watermark: ImageConfiguration(imageName: "Common.Banuba.Watermark"), size: CGSize(width: 204, height: 52), sharedOffset: 20, position: .rightBottom ) let autoExportConfiguration = ExportVideoConfiguration( fileURL: destFile1, quality: .auto, useHEVCCodecIfPossible: true, watermarkConfiguration: nil ) let hdExportConfiguration = ExportVideoConfiguration( fileURL: destFile2, quality: .videoConfiguration(ExportVideoInfo(resolution: .hd1280x720, useHEVCCodecIfPossible: true)), useHEVCCodecIfPossible: true, watermarkConfiguration: watermarkConfiguration ) let exportConfig = ExportConfiguration( videoConfigurations: [autoExportConfiguration, hdExportConfiguration], isCoverEnabled: true, gifSettings: nil ) ``` Use the created ` ExportConfiguration` to start the export by using the ` BanubaVideoEditor.export()` method: ```Swift public func export( using configuration: ExportConfiguration, exportProgress: ((TimeInterval) -> Void)?, completion: @escaping ((_ error: Error?, _ exportCoverImages: ExportCoverImages?) -> Void) ) ``` ## Handle the export result The ` BanubaVideoEditor.export()` method allows to start the export and track the result. Provide: 1. ` ExportConfiguration` - where you set up all the required media content you want to make; 2. ` exportProgress` - a callback that gets called when the export progress changes. Values are 0.0-1.0; 3. ` completion` - a callback that gets called when the export is finished with an error or not: ```swift videoEditorSDK?.export( using: exportConfiguration, exportProgress: { progress in DispatchQueue.main.async { // Export is in progress. You can show progress view. Progress is 0.0 - 1.0 ... } }, completion: { [weak self] error, exportCoverImages in DispatchQueue.main.async { // Export finishes. Use 'error' value to detect the state of export. // Hide progress view // You can clear exported session if you do no need it anymore //self?.videoEditorSDK?.clearSessionData() } ``` :::tip If the export is finished successfully, you can use the instance of ` ExportConfiguration` as a result and access the media files and its metadata. ::: ## Add a watermark :::warning A watermark is not added to the exported video by default. ::: You can use your custom watermark for a video. First, create the instance of ` WatermarkConfiguration` to ```swift let watermarkConfiguration = WatermarkConfiguration( watermark: ImageConfiguration(imageName: "Common.Banuba.Watermark"), size: CGSize(width: 204, height: 52), sharedOffset: 20, position: .rightBottom ) ``` where ` position` is used for locating the watermark image in a video: ```swfit public enum WatermarkPosition { case leftTop case leftBottom case rightTop case rightBottom } ``` Next, set ` watermarkConfiguration` to every instance of ` ExportVideoConfiguration` where you want to add a watermark in [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L57): ```diff let hdExportConfiguration = ExportVideoConfiguration( fileURL: destFile, quality: .hd1280x720, useHEVCCodecIfPossible: true, // highlight-add-next-line + watermarkConfiguration: watermarkConfiguration ) ``` ## Export the GIF preview The Video Editor allows to export a preview of the video as a GIF file. The instance of ` GifSettings` is required in ` ExportConfiguration` to export a preview during the export. You can specify this property in [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L63): ```diff let exportConfig = ExportConfiguration( videoConfigurations: [exportConfiguration], isCoverEnabled: true, // highlight-add-next-line + gifSettings: GifSettings(duration: 0.3) ) ``` ## Get the audio track of exported video You can get the video's audio track after its successfull export using the `BanubaVideoEditor.exportAudio()` method: ```Swift public func exportAudio( fileUrl: URL, audioSettings: [String: Any] = VESettings.audio, completion: @escaping (Bool, Error?) -> Void ) ``` :::tip Learn about the available options on the [Apple Developer Portal](https://developer.apple.com/documentation/avfoundation/audio_settings) to provide custom audio quality settings. ::: ## Get the information about the music tracks used during creation of exported video You can use the `musicMetadata` property of `BanubaVideoEditor` class to get the array of `MusicEditorTrack` entities containing the information about the corresponding music tracks. ```swift /// Video Editor main entity and entry point. /// Can present and hide root view controller. /// Has default export method. public class BanubaVideoEditor { /// Simple metadata of music composition settings public var musicMetadata: MusicEditorMetadata? { get } ... } ``` ```swift // MARK: - MusicEditorTrack public struct MusicEditorTrack: Codable { ///Track URL public var url: URL ///Track original URL public var originalURL: URL ///Track title public var title: String ///Track id public var id: Int32 /// Track volume public var volume: Float ... } ``` ## Export metadata analytics While exporting media content, the Video Editor generates simple metadata analytics that you can use to analyze what media content your users make. Metadata is a JSON string and can be received once the export is finished successfully: ```swift let metadataJson: String? = videoEditorSDK?.metadata?.analyticsMetadataJSON ``` The JSON sample: ```json { "export_success": true, // defines if the export finished succesffully "aspect_ratio": "original", // aspect ration used in exported video "video_resolutions": ["1080x1920"], // list of video resolutions used in export "camera_effects": [], // list of effects of features used on camera screen while recording video "ppt_effects": { "visual": 2, // num of visual effects i.e. Glitch, VHS used in exported video "speed": 1, // num of speed effects used in exported video "mask": 6, // num of AR masks used in exported video "color": 3, // num of color effects used in exported video "text": 1, // num of text effects used in exported video "sticker": 1, // num of sticker effects used in exported video "blur": 1 // num of blur effects used in exported video }, "sources": { "camera": 0, // num of video sources recorded on camera screen(not PIP) "gallery": 1, // num of video sources selected in the gallery "pip": 0, // num of video recorded with PIP "slideshow": 0, // num of video exported as slideshow "audio": 0 // num of audi tracks }, "export_duration": 12.645, // export processing duration "video_duration": 20.11, // exported video duration "video_count": 1, // num of exported video files "os_version": "11", // OS version "sdk_version": "1.26.6" // VE SDK version } ``` --- ## Face AR and AR Cloud products on iOS [Banuba Face AR SDK](https://www.banuba.com/facear-sdk/face-filters) product is used on camera and editor screens for applying various AR effects while making video content. ## Overview Any Face AR effect is a folder that includes a number of files required for the Face AR SDK to play this effect. :::warning Make sure every effect folder includes the ` preview.png` file. This file is used as a preview for the AR effect. ::: ## Integrate Face AR :::info ```Banuba Face AR SDK``` integration is included in the [Github sample](https://github.com/Banuba/ve-sdk-ios-integration-sample/). ::: Add the dependency below to the [Podfile](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Podfile#L19) to integrate `Banuba Face AR SDK` into your project: ```ruby pod 'BanubaSDK', '1.48.2' ``` ## Manage effects There are 2 options for managing AR effects: 1. ` bundleEffects` folder - use [bundleEffects](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/bundleEffects) folder, 2. ` AR Cloud` Effects - which are stored on the remote server. :::warning Please, keep the name ` bundleEffects`, otherwise, the app will not start. Create the `bundleEffects` folder if it does not exist. ::: :::tip You can use both options i.e. store just a few AR effects in `bundleEffects` and 100 or more AR effects on `AR Cloud`. ::: ## Integrate AR Cloud `AR Cloud` is a cloud solution for storing the Banuba Face AR effects on the server and is used by the Face AR and Video Editor products. Any AR effect downloaded from `AR Cloud` is cached on the user's device. Add the dependency below to the [Podfile](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Podfile#L11) to integrate `AR Cloud` into your project: ```ruby pod 'BanubaARCloudSDK', '1.48.2' ``` Since the link to your AR Cloud bucket is included into the license token, AR effects will appear once you set the license token with the AR Cloud link. ## Change the effects order By default, all AR effects are listed in alphabetical order. AR effects from `bundleEffects` are listed in the beginning. Provide your ordered list of effects to `preferredMasksOrder` in `VideoEditorConfig`: ```swift videoEditorConfig.recorderConfiguration.recorderEffectsConfiguration.preferredMasksOrder = [ "XYScanner", "Background" ... ] ``` :::warning These are the names of specific directories located in `bundleEffects` or on `AR Cloud`. ::: ## Disable Face AR SDK The Video Editor SDK can work without the Face AR SDK. Change the [Podfile](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Podfile) to disable the Face AR SDK: ```diff banuba_sdk_version = '1.48.2' // highlight-remove-start - pod 'BanubaSDK', banuba_sdk_version // highlight-remove-end // highlight-add-next-line + pod 'BanubaSDKSimple', banuba_sdk_version ``` :::tip Please, keep in mind that you can remove all AR effects from [bundleEffects](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/bundleEffects) if your license does not include the Face AR product. ::: --- ## Drawing on iOS Lets your users draw freely on screen. It is a convenient tool for highlighting important objects in the video or spicing up the frame with funny doodles or stylish art. There are 5 line variants to choose from for more self-expression opportunities. :::important The Drawing feature is disabled by default. Please contact Banuba representatives to know more about using this feature. :::       --- ## Gallery screen on iOS Video Editor SDK includes built in gallery functionality where the user can pick any video or image and use it while making video. :::info The Gallery screen is integrated by default. ::: ## Customizations The gallery is used in the app when you want to select a photo or video stored on your phone. Implement ` GalleryConfiguration` and set it to ` VideoEditorConfig.combinedGalleryConfiguration` to customize the gallery screen. To setup the visible tabs for the gallery, just configure it in the `CombinedGalleryConfiguration` entity: ```swift var config = VideoEditorConfig() config.combinedGalleryConfiguration.visibleTabsInGallery = [.video, .photo] ``` ## Implement a custom gallery Please, follow these steps to integrate your gallery into the SDK: ### Step 1 Implement custom `UIViewController` inherited from `GalleryViewController`: ```swift @objc open class GalleryViewController: UIViewController { open weak var delegate: GalleryViewControllerDelegate? open var configuration: GalleryConfiguration? open var selectionBehaviour: GallerySelectionBehaviour? /// Setups new album at gallery open func useAlbum(_ albumModel: AlbumModel) {} /// Cancel current export open func cancelExport() {} /// Retry export failed items open func retryExport() {} } ``` - `delegate`- use this property to notify the Video Editor SDK about the user's actions in the custom gallery. Below there is a list of possible actions: ```swift // MARK: - GalleryViewControllerDelegate @objc public protocol GalleryViewControllerDelegate: AnyObject { /// Tells delegate object about starting asynchronous operations at the gallery. /// BanubaVideoEditorSDK showing full-screen spinner by this event. It can help to prevent unnecessary actions from a user. func galleryViewController(_ controller: GalleryViewController, didStartExportWith progressHandler: ProgressHandler) /// Tells delegate object about finishing asynchronous operations at the gallery func galleryViewController(_ controller: GalleryViewController, didEndExportWith error: Error?, hideProgressViewCompletion: @escaping () -> Void) /// Tells delegate object about the closing gallery. func galleryViewControllerDidClose(_ controller: GalleryViewController) /// Tells delegate object about completion picking gallery items. func galleryViewControllerDone( _ controller: GalleryViewController, withGalleryItems items: [GalleryItem] ) /// Tells delegate object that he should present message. /// In BanubaVideoEditorSDK it presents popup message. func galleryViewController( _ controller: GalleryViewController, presentMessage message: String ) } ``` - `configuration` - contains the UI configuration. All the details you can find [here](#customizations); - `selectionBehaviour` - contains the gallery settings. ```swift /// Setups gallery selection behaviour @objc public class GallerySelectionBehaviour: NSObject { /// Maximum possible selected gallery items quantity which can select a user. public let maximumSelectedCount: Int /// Setups already selected items quantity if gallery open as a picker at trimmer sceen or other cases. /// Use this field to control maximum selection items. public let selectedItemsCount: Int? /// Setups picker mode if isMultiselectModeEnabled is false. /// Otherwise, multiselection mode enabled. public let isMultiselectModeEnabled: Bool /// Setups gallery video duration fetched from user gallery supported by BanubaVideoEditorSDK. /// By default is 3.0 public let minimumGalleryVideoDuration: TimeInterval /// Setups allowed media types which user can select in gallery public let allowedMediaTypes: [GalleryMediaType] } @objc public enum GalleryMediaType: Int, CaseIterable { case video case photo } ``` `GalleryItem` is a protocol to which your items should conform in order to pass the gallery selection result to `BanubaVideoEditorSDK`: ```swift @objc public protocol GalleryItem: NSObjectProtocol { /// Video representation url asset var urlAsset: AVURLAsset? { get } /// Preview for gallery item var preview: UIImage? { get set } /// GalleryItem duration var duration: TimeInterval { get } /// Type can be video, photo or unknown var type: GalleryItemType { get } /// Requests preview for displaying in gallery list func requestPreview( size: CGSize, handler: @escaping (UIImage?) -> Void ) /// Requests photo with desired size func requestPhoto( size: CGSize, progressHandler: ((Double) -> (Bool))?, handler: @escaping (UIImage?, Error?) -> Void ) /// Requests video url asset func requestAVURLAsset( progressHandler:((Double) -> (Bool))?, handler: @escaping (AVURLAsset?, Error?) -> Void ) /// Requests video player item func requestAVPlayerItem( progressHandler: ((Double) -> (Bool))?, handler: @escaping (AVPlayerItem?, Error?) -> Void ) } ``` ### Step 2 Implement custom `UIViewController` inherited from `AlbumsViewController`: ```swift @objc open class AlbumsViewController: UIViewController { open weak var delegate: AlbumsViewControllerDelegate? open var configuration: AlbumsConfiguration? open var selectedAlbum: AlbumModel? } ``` - `delegate` - notifies `BanubaVideoEditorSDK` about actions that happen in Albums. These are the possible actions: ```swift @objc public protocol AlbumsViewControllerDelegate: AnyObject { /// Tells delegate object about selection the new album func albumsViewController(_ controller: AlbumsViewController, didSelect album: AlbumModel) // Tells delegate object about close action func albumsViewControllerDidClose(_ controller: AlbumsViewController) } ``` - `configuration` - a simple configuration with the `TextButtonConfiguration` and `BackButtonConfiguration` configurations; - `selectedAlbum` - an entity which contains information about the currently selected album: ```swift @objc public protocol AlbumModel { /// Album name var name: String? { get set } /// Album preview var preview: UIImage? { get set } /// Assosiated asset collection with album var assetCollection: PHAssetCollection { get } } ``` ### Step 3 Provide your custom gallery to `BanubaVideoEditorSDK`. Please, follow these steps: - create your own viewControllerFactory that conforms to `GalleryViewControllerFactory`: ```swift @objc public protocol GalleryViewControllerFactory: NSObjectProtocol { /// Creates GalleryViewController func makeGalleryViewController( withConfiguration configuration: GalleryConfiguration, selectionBehaviour: GallerySelectionBehaviour ) -> GalleryViewController } class MyGalleryViewControllerFactory: NSObject, GalleryViewControllerFactory { func makeGalleryViewController( withConfiguration configuration: BanubaUtilities.GalleryConfiguration, albumsConfiguration: BanubaUtilities.AlbumsConfiguration, selectionBehaviour: BanubaUtilities.GallerySelectionBehaviour ) -> BanubaUtilities.GalleryViewController { let controller = UIStoryboard( name: String(describing: UIViewController.self), bundle: Bundle(for: UIViewController.self) ).instantiateInitialViewController() as! UIViewController return controller } } ``` - paste your factory to the `BanubaVideoEditor` init: ```swift /// Example video editor view controller factory class ViewControllerFactory: ExternalViewControllerFactory { var musicEditorFactory: MusicEditorExternalViewControllerFactory? var countdownTimerViewFactory: CountdownTimerViewFactory? var exposureViewFactory: AnimatableViewFactory? //MARK: - ExternalViewControllerFactory protocols variale var galleryViewControllerFactory: GalleryViewControllerFactory? } ... let viewControllerFactory = ViewControllerFactory() // Paste your custom factory to externalViewControllerFactory viewControllerFactory.galleryViewControllerFactory = MyGalleryViewControllersFactory() videoEditorSDK = BanubaVideoEditor( token: token, configuration: config, externalViewControllerFactory: viewControllerFactory ) ``` --- ## Green Screen on iOS Video Editor SDK brings you real-time background subtraction technology for you to empower your users with the best quality virtual background service. Users can automatically remove, change or augment backgrounds. :::important The feature requires Face AR product with [Background Subtraction](https://www.banuba.com/technology/background-subtraction) feature. Please contact Banuba representatives to know more about using this feature. :::   ```Green Screen``` feature doesn't require physical green screens, the neural networks do all the work. --- ## Open Photo Editor SDK from Camera screen on iOS This guide demonstrates how to open [Photo Editor SDK](https://www.banuba.com/photo-editor-sdk) just after taking a photo on Video Editor Camera screen. All recorded video or taken images on Camera screen can be handled using ```videoEditor``` of protocol ```BanubaVideoEditorDelegate```. Provide custom implementation of ```videoEditor``` method to override flow. ``` diff class ViewController: UIViewController, BanubaVideoEditorDelegate, BanubaPhotoEditorDelegate { ... // highlight-add-next-line func videoEditor(_ videoEditor: BanubaVideoEditor, shouldProcessMediaUrls urls: [URL]) -> Bool { // Filter media resources to find the target image for Photo Editor SDK guard let jpegURL = urls.first(where: { $0.pathExtension.lowercased() == "jpeg" }), let imageData = try? Data(contentsOf: jpegURL), !imageData.isEmpty, let resultImage = UIImage(data: imageData) else { return true } // Close Video Editor SDK videoEditor.dismissVideoEditor(animated: true) { DispatchQueue.main.async { [weak self] in guard let self else { return } // Calling clearSessionData() also removes any files stored in urls array videoEditorModule?.videoEditorSDK?.clearSessionData() // Use this launch config to open Photo Editor SDK let launchConfig = PhotoEditorLaunchConfig( hostController: self, entryPoint: .editorWithImage(resultImage) ) } } return false } } ``` --- ## Share video screen on iOS Share video screen allows users to easily share an exported video using popular social media services and OS specific components. :::info This is an optional screen that you can add to your video editing flow. ::: It can be configured by `SharingScreenConfiguration` which is provided to the `presentSharingViewController` function that shows the screen. `SharingScreenConfiguration` has two key properties: * `sharingModels` - describes what kind of sharing services are available on the sharing screen; * `facebookId` - a required option for Facebook/Instagram reels and stories: ```swift private func showSharingScreen(videoUrl: URL, exportCoverImages: ExportCoverImages?) { let config = SharingScreenConfiguration( sharingModels: [ SharingServiceModel( sharingType: .facebookReels, sharingTitle: "Facebook\nReels", sharingImage: UIImage(named: "FbReels") ?? UIImage() ), SharingServiceModel( sharingType: .facebookStories, sharingTitle: "Facebook\nStories", sharingImage: UIImage(named: "FbStories") ?? UIImage() ), SharingServiceModel( sharingType: .instagramStories, sharingTitle: "Instagram\nStories", sharingImage: UIImage(named: "InstStories") ?? UIImage() ), SharingServiceModel( sharingType: .other, sharingTitle: "Other", sharingImage: UIImage(named: "Share") ?? UIImage() ) ], videoImageViewCornerRadius: 15.0, sharingVideoTextConfiguration: TextConfiguration( font: UIFont.boldSystemFont(ofSize: 22.0), color: .white, text: "Share video" ), backgroundConfiguration: BackgroundConfiguration( cornerRadius: 15.0, color: #colorLiteral(red: 0.1215686275, green: 0.1294117647, blue: 0.1411764706, alpha: 1) ), sharingCellConfiguration: SharingCellConfiguration( titleTextConfiguration: TextConfiguration( font: UIFont.systemFont(ofSize: 10.0), color: #colorLiteral(red: 0.4756349325, green: 0.4756467342, blue: 0.4756404161, alpha: 1) ) ), closeButtonConfiguration: RoundedButtonConfiguration( textConfiguration: TextConfiguration( font: UIFont.systemFont(ofSize: 12.0), color: .white, text: "CLOSE" ), cornerRadius: 20.0, backgroundColor: .clear, borderWidth: 1.0, borderColor: #colorLiteral(red: 0.4756349325, green: 0.4756467342, blue: 0.4756404161, alpha: 1) ), facebookId: "YOUR FACEBOOK APP ID" ) BanubaVideoEditor.presentSharingViewController( from: self, configuration: config, mainVideoUrl: videoUrl, videoUrls: [videoUrl], previewImage: exportCoverImages?.coverImage ?? UIImage(), animated: true, completion: nil ) } ``` --- ## Stickers on iOS Guide to using stickers in Video Editor SDK on Android. ## Giphy Video Editor SDK has built in integration with [Giphy service](https://developers.giphy.com/docs/api/) for loading stickers. ```GIPHY``` doesn't charge for their content. The one thing they do require is attribution. Also, there is no commercial aspect to the current version of the product (no advertisements, etc.). Any sticker effect is a GIF file. To use stickers in your project you need to request personal [Giphy API key](https://support.giphy.com/hc/en-us/articles/360020283431-Request-A-GIPHY-API-Key). Set your personal Giphy Api Key into the `giphyAPIKey` parameter of the `GifPickerConfiguration` entity in the [VideoEditorModule](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L83): ```swift var config = VideoEditorConfig() config.gifPickerConfiguration.giphyAPIKey = "YOUR GIPHY KEY" ``` Stickers will appear in the container on editor screen once ```giphyAPIKey``` is set. The placeholder of the search bar is specified by the `com.banuba.searchGif.placeholder` key in the `Localizable.strings` file. --- ## Video recording integration guide on iOS Guide to modifying video recording feature in Video Editor SDK. ## Quality details The subsequent table describes video quality details used for video recording in various resolutions with h264 codec. If h265 (HEVC) codec is used for recording a video, the values listed below will be reduced by approximately 34%: | Recording speed | 360p(360 x 640) | 480p(480 x 854) | 540p(540 x 960) | HD(720 x 1280) | FHD(1080 x 1920) | QHD(2560 x 1440) | 4K(3860 x 2160) | | -------------------------- | --------------- | --------------- | --------------- | --------------- | ---------------- | ---------------- | --------------- | | 0.5x, 1x (Default), 2x, 3x | 1.2 Mbits | 2 Mbits | 2.4 Mbits | 3.6 Mbits | 5.8 Mbits | 16 Mbits | 36 Mbits | ## Implement configurations `VideoEditorConfig` is the core class used for customizing all features in the Video Editor SDK. The class includes many internal config classes that are very useful if you want to create your custom experience. `RecorderConfiguration` is the main configuration class in `VideoEditorConfig` and is used for the video recording functionality: | Property | Values | Description | | ------------- |:-----------------------------------:| :------------- | | videoResolution | VideoResolutionConfiguration | defines the resolution configuration for a video recording | loopAudioWhileRecording | Bool; Default `true` | defines if the audio used in the video recording should be looped | isDynamicMusicTitle | Bool; Default `false` | defines if the music title on the screen changes when a new track is applied | isDefaultFrontCamera | Bool; Default `false` | set `true` if you want to open camera in the front mode | useHEVCCodecIfPossible | Bool; Default `true` | enables H265 codec for recording if it is available on the device | isPhotoSequenceAnimationEnabled | Bool; Default `false` | should use animation for photo sequences | isAudioRateEqualsVideoSpeed | Bool; Default `false` | the video speed selected by the user is applied to audio | isGalleryButtonHidden | Bool; Default `false` | defines if the gallery button located in the bottom-right should be hidden. `true` will hide the button | supportMultiRecords | Bool; Default `true` | defines if the user can record multiple video files | takeAudioDurationAsMaximum | Bool; Default `false` | limits the maximum length of a video recording to match the duration of the audio used in the recording The next very handy config class is `VideoEditorDurationConfig` that is responsible for customizing video recording durations: :::warning All values are in seconds. ::: | Property | Values | Description | | ------------- |:-----------------------------------------------------------:| :------------- | | maximumVideoDuration | TimeInterval > 0; Default `120.0` | the maximum allowed video duration | videoDurations | [TimeInterval] not empty; Default `[60.0, 30.0, 15.0]` | the array of durations allowed for the video recording. The user sees a certain button and can change the duration by tapping. For example, `60.0` means that the user can record multiple video sources with the total duration no more than `60.0` seconds. | minimumDurationFromCamera | TimeInterval > 0; Default `3.0` | the minimum allowed video duration required to proceed and open the next screen | minimumDurationFromGallery | TimeInterval > 0; Default `0.3` | the minimum allowed video duration displayed on the gallery screen | minimumVideoDuration | TimeInterval > 0; Default `1.0` | the minimum allowed video source duration that can be recorded on the camera screen | minimumTrimmedPartDuration | TimeInterval > 0; Default `0.3` | the minimum allowed video source duration to trim | slideshowDuration | TimeInterval > 0; Default `0.3` | the slideshow video duration produced by an image In this example, the maximum video recording duration is set to 30 seconds: ```swift var config = VideoEditorConfig() config.videoDurationConfiguration.maximumVideoDuration = 30.0 ``` And `FeatureConfiguration` that helps to customize the use of some specific features on the camera screen where the video recording happens: | Property | Values | Description | | ------------- |:-------------------------:| :------------- | | isDoubleTapForToggleCameraEnabled | Bool; Default `false` | enables switching between the front and the back camera facing by double tapping on the camera screen | isMuteCameraAudioEnabled | Bool; Default `false` | indicates if the "mute microphone" button is visible on the camera screen | isSpeedBarEnabled | Bool; Default `true` | enables the speed selection bar. If the bar is disabled, the speed recording will be interactively switched by tapping | openAutomaticallyPIPSettingsDropdown | Bool; Default `false` | if this property is enabled, the PiP settings drop down view will be presented after opening the camera screen ## Configure the microphone state The `RecorderConfiguration` class includes the `muteMicrophoneForPIP` property you can use to mute the sound in the PIP mode. The default value is `true`: ```swift var config = VideoEditorConfig() config.recorderConfiguration.muteMicrophoneForPIP = false ``` ## Configure recording modes The recording includes 3 modes for recording content implemented in `captureButtonModes` in the `RecorderConfiguration` class: - `Photo`, - `Video`, - `Photo` and `Video` - **default**. In this example, the recording mode is set to `Video` only: ```swift var config = VideoEditorConfig() config.recorderConfiguration.captureButtonModes = [.video] ``` ## Configure the record button appearance The record button is the main UI control on the camera screen which you can fully customize along with the animation that is played by tapping. Implement the `RecordButtonProvider`, `RecordButton`, `RecordButtonDelegate` protocols to create your custom recording button experience: ```swift public protocol RecordButtonProvider { func getButton() -> RecordButton } public protocol RecordButton: UIView { var delegate: RecordButtonDelegate? { get set } var configuration: RecordButtonConfiguration? { get set } func changeViewToIdleState() func changeViewToRecordingState() } public protocol RecordButtonDelegate: AnyObject { var captureButtonMode: CaptureButtonViewMode { get } func recordButtonDidTakePhoto(_ recordButton: RecordButton) func recordButtonDidCancelTakePhoto(_ recordButton: RecordButton) func recordButtonDidStartVideoRecording(_ recordButton: RecordButton) func recordButtonDidStopVideoRecording(_ recordButton: RecordButton) func recordButtonDidZoomingVideoRecording(_ recordButton: RecordButton, recognizer: UILongPressGestureRecognizer) } ``` Set the new implementation of `RecordButtonProvider` to `RecorderConfiguration.recordButtonProvider`: ```swift var config = VideoEditorConfig() config.recorderConfiguration.recordButtonProvider = ... ``` ## Picture in Picture Picture in Picture, or `PIP`, is a video editing technique that lets you overlay two videos in the same video. The multi-layer editing effect is perfect for reaction videos, slideshows, product demos, and more. This feature is similar to the TikTok duet feature.   :::warning The feature is disabled by default and can be enabled if the license supports it. Please, ask Banuba business representatives to include this feature into your license. ::: The subsequent guide explains how to start and customize `PIP`. First, create `VideoEditorLaunchConfig` in [ViewController](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/ViewController.swift#L65) and provide video content for the feature: ```swift let launchConfig = VideoEditorLaunchConfig( entryPoint: entryPoint, hostController: self, videoItems: resultUrls, // highlight-add-next-line pipVideoItem: resultUrls[.zero], animated: true ) self.presentVideoEditor(with: launchConfig) ``` `PIP` supports 4 modes that you can use: - `Floating`, - `TopBottom`, - `React`, - `LeftRight`. Use `PIPSettingsConfiguration` to customize the PIP implementation: | Property | Values | Description | | ------------- |:-------------------------:| :------------- | | backgroundConfiguration | BackgroundConfiguration | BackgroundConfiguration sets up the background view style | dragIndicatorConfiguration | RoundedButtonConfiguration | the cursor color | titleConfiguration | TextConfiguration | the title font for the controls | layoutSettingsButtonsConfiguration | [PIPSelectableCellConfiguration] | the array of the pip cell configurations In this example, 4 supported PIP modes are set: ```swift var config = VideoEditorConfig() config.pipSettingsConfiguration?.layoutSettingsButtonsConfiguration = [ PIPSelectableCellConfiguration(identifier: .floating), PIPSelectableCellConfiguration(identifier: .react), PIPSelectableCellConfiguration(identifier: .topBottom), PIPSelectableCellConfiguration(identifier: .leftRight) ] ``` You can also change the position of the music button. Use `additionalEffectsButtons` and provide custom `AdditionalEffectsButtonConfiguration` with the `.sound` identifier: ```swift let config = VideoEditorConfig() config.recorderConfiguration.additionalEffectsButtons = [ AdditionalEffectsButtonConfiguration( identifier: .sound, imageConfiguration: ImageConfiguration(imageName: ""), selectedImageConfiguration: ImageConfiguration(imageName: ""), titlePosition: .bottom, position: .top ), ... ] ``` The Video Editor supports 3 options for positioning the music button: `bottom`, `center`, `top`. --- ## Weatherman on iOS Weatherman mode is an improvement on top of Banuba's cutting-edge background replacement. It allows users to drag and drop themselves on any place on the screen to emulate the look of a TV presenter the feature is named after. Weatherman is useful for reactions, learning content, presentations, explainers and other similar videos thanks to the professional feel it creates. :::important The Weatherman feature is disabled by default. Please contact Banuba representatives to know more about using this feature. :::     --- ## Launching Photo Editor on iOS Guide to launching Photo Editor SDK. ## Launch Create an instance of `BanubaPhotoEditor` using the license token: ```swift var configuration = PhotoEditorConfig() let photoEditorSDK = BanubaPhotoEditor( token: token, configuration: configuration ) photoEditorSDK?.delegate = delegate photoEditorSDK?.presentPhotoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` `photoEditorSDK` is `nil` when the license token is incorrect i.e. empty, truncated. If `photoEditorSDK` is not `nil`, you can proceed and start the Video Editor. Next, we strongly recommend checking your license state before starting the Photo Editor: ```swift photoEditorSDK?.getLicenseState(completion: { [weak self] isValid in if isValid { print("✅ License is active, all good") } else { print("❌ License is either revoked or expired") } ... completion(isValid) }) ``` :::danger The "Photo editing is unavailable" screen will appear if you start the Photo Editor SDK with a revoked or expired license. ::: :::warning If you have also integrated the Video Editor SDK into your app, make sure to deallocate any instances of `BanubaVideoEditor` that remain in memory before presenting the Photo Editor in order to prevent crashes: ::: ```swift videoEditorSDK = nil ... photoEditorSDK?.presentPhotoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` ## Photo Editor Lifecycle Events To receive the notifications when a user has either closed the editor or finished editing and saved the edited photo, create a class that conforms to the `BanubaPhotoEditorDelegate` protocol and assign its reference to the `BanubaPhotoEditor.delegate` property: ```swift photoEditorSDK?.delegate = delegate ``` During handling of both notifications you must dismiss the Photo Editor: ```swift /// User closed photo editor without editing image func photoEditorDidCancel( _ photoEditor: BanubaPhotoEditorSDK.BanubaPhotoEditor ) { photoEditor.dismissPhotoEditor(animated: true, completion: nil) } /// User closed photo editor after saving image func photoEditorDidFinishWithImage( _ photoEditor: BanubaPhotoEditorSDK.BanubaPhotoEditor, image: UIImage ) { photoEditor.dismissPhotoEditor(animated: true, completion: nil) // Do something with received image } ``` --- ## Launching from SwiftUI Guide to launch Video Editor from SiftUI. ## Configuration Custom behavior of the Video Editor SDK in your app is implemented using a number of configuration classes. The `VideoEditorConfig` is the main entity used for the Video Editor configuration: ```swift func createConfiguration() -> VideoEditorConfig { var config = VideoEditorConfig() ... return config } ``` Check out the [demo project](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L75) for an example of creating `VideoEditorConfig`. The following configuration starts the Video Editor from the camera screen: ```swift func createLaunchConfiguration() -> VideoEditorLaunchConfig { let launchConfig = VideoEditorLaunchConfig( entryPoint: .camera, hostController: UIViewController(), musicTrack: nil, // Paste a music track as a track preset at the camera screen to record video with music. animated: true ) return launchConfig } ``` The Video Editor supports multiple launch methods described in [this guide](ve-faq.md#what-are-the-available-entry-points-of-the-video-editor). The Video Editor can export multiple media files to meet your requirements. Create an instance of `ExportConfiguration` and provide `Array` where every `ExportVideoConfiguration` is a media file i.e. a video or audio: ```swift func createExportConfiguration() -> ExportConfiguration { let url = FileManager .default .temporaryDirectory .appendingPathComponent("video.mov") if FileManager.default.fileExists(atPath: url.path) { try? FileManager.default.removeItem(at: url) } let exportVideoConfiguration: ExportVideoConfiguration = ExportVideoConfiguration( fileURL: url, quality: .auto, useHEVCCodecIfPossible: true, watermarkConfiguration: nil ) let exportConfiguration = ExportConfiguration( videoConfigurations: [exportVideoConfiguration], isCoverEnabled: true, gifSettings: nil ) return exportConfiguration } ``` Please, check out the [export implementation](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/ViewController.swift#L243) in the sample. Learn the [Export integration guide](guide_export.md) to know more about exporting media content features. ## Launch Use `BanubaVideoEditorSwiftUIView` with the license token to present the Video Editor SDK: ```swift @State private var isPresentingVideoEditor = false .fullScreenCover(isPresented: $isPresentingVideoEditor) { BanubaVideoEditorSwiftUIView( token: "", launchConfig: createLaunchConfiguration(), configuration: createConfiguration(), exportConfiguration: createExportConfiguration() ) .onDidCancel { // The user tapped on the cancel button. Dismissing the video editor. isPresentingVideoEditor = false } .onDidSave { videoURLs in // The video export is succeed. Dismissing the video editor. isPresentingVideoEditor = false } .onUpdateProgress { progress in // Current export progress. } .onDidFail { error in // Dismissing the video editor. isPresentingVideoEditor = false switch error { case .exportError(let description): // There was an error exporting the video. case .initError: // There was an error with init of Video Editor SDK. case .licenseError: // There was an error with license. @unknown default: break } } .ignoresSafeArea() } ``` --- ## Launching from UIKit Guide to launch Video Editor from UIKit. ## Configuration The custom behavior of the Video Editor SDK in your app is implemented using a number of configuration classes. `VideoEditorConfig` is the main entity used for the Video Editor configuration: ```swift class VideoEditorModule { func createConfiguration() -> VideoEditorConfig { var config = VideoEditorConfig() ... return config } } ``` Check out the [demo project](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/VideoEditorModule.swift#L75) for an example of creating `VideoEditorConfig`. ## Launch Create an instance of `BanubaVideoEditor` with the license token: ```swift let videoEditorSDK = BanubaVideoEditor( token: AppDelegate.licenseToken, configuration: config, externalViewControllerFactory: viewControllerFactory ) ``` `videoEditorSDK` is `nil` when the license token is incorrect i.e. empty, truncated. If `videoEditorSDK` is not `nil`, you can proceed and start the Video Editor. Next, we strongly recommend checking your license state before starting the Video Editor: ```swift videoEditorSDK?.getLicenseState( completion: { [weak self] isValid in if isValid { print("✅ License is active, all good") } else { print("❌ License is either revoked or expired") } ... completion(isValid) } ) ``` :::danger The "Video content unavailable" screen will appear if the user starts the Video Editor SDK with a revoked or expired license: ::: The following [implementation](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/ViewController.swift#L48) starts the Video Editor from the camera screen: ```swift let cameraLaunchConfig = VideoEditorLaunchConfig( entryPoint: .camera, hostController: self, musicTrack: nil, // Paste a music track as a track preset at the camera screen to record video with music animated: true ) videoEditorModule.presentVideoEditor(with: cameraLaunchConfig) ``` The Video Editor supports multiple launch methods described in [this guide](ve-faq.md#what-are-the-available-entry-points-of-the-video-editor). ## Implementing export The Video Editor can export multiple media files to meet your requirements. Create an instance of `ExportConfiguration` and provide `Array` where every `ExportVideoConfiguration` is a media file i.e. video or audio. Next, use `BanubaVideoEditor.export()` method and pass the instance of `ExportConfiguration` to start the export. Please, check out the [export implementation](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/ViewController.swift#L243) in the sample. Learn the [Export integration guide](guide_export.md) to know more about exporting media content features. --- ## LLM and Vibe Coding on iOS Banuba Video and Photo Editor SDK documentation is available in LLMs.txt format, optimized for AI. - [llms.txt](../../llms.txt) - [llms-full.txt](../../llms-full.txt) --- ## Installation with CocoaPods(Ios) Learn the [CocoaPods Getting Started Guide](https://guides.cocoapods.org/using/getting-started.html) if you are new to CocoaPods. :::info It is advised to use the latest version of CocoaPods. Please, check your version with ```pod --version``` and upgrade it if necessary. ::: Complete the following steps to get the Photo Editor SDK dependencies using CocoaPods: 1. install CocoaPods using Homebrew: ```sh brew install cocoapods ``` 2. create a new Podfile in your project folder or edit the existing one: ```sh pod init ``` 3. add the necessary pods and our podspecs sources to the Podfile. The list of the required Photo Editor dependencies can be found in the [Podfile](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Podfile) example: ```ruby source 'https://cdn.cocoapods.org/' source 'https://github.com/Banuba/specs.git' source 'https://github.com/sdk-banuba/banuba-sdk-podspecs.git' pod 'BanubaPhotoEditorSDK', '1.2.9' ``` 4. install the Video Editor SDK pods: ```sh pod install --repo-update ``` 4. open the ```***.xcworkspace``` workspace in Xcode and run the project. --- ## Configure Photo Editor on iOS Guide to modifying Photo Editor SDK. ## Initial screen (entry point) selection By default the built-in gallery will be shown first after the presentation of the Photo Editor. In some cases the hosting app may already provide the photo selection functionality using its own custom gallery and it is necessary to directly open the photo editing screen with a preselected photo. To support such use cases there is the `entryPoint` property in `PhotoEditorLaunchConfig`. By using the `editorWithImage` or `editorWithURL` options you can specify the edited image by directly providing either the `UIImage` instance or the *local* `URL` to the on-disk binary image representation. In the following example the image stored in the app's bundle will be opened in the Photo Editor SDK: ```swift let url = Bundle.main.url(forResource: "image", withExtension: "jpg")! let launchConfig = PhotoEditorLaunchConfig( hostController: ..., entryPoint: .editorWithURL(url) ) photoEditorSDK.presentPhotoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` Should there be any problem with decoding the image at the provided url, an error message will be logged into the console by the SDK. ## Configuration of the sharing screen After finishing editing an image, a user is navigated to the Sharing screen. If Facebook or Instagram is installed on the user's phone, it will be possible to present an option to share the image as a Facebook or Instagram Story. To enable such a functionality you need to specify the Facebook app id: ```swift var configuration = PhotoEditorConfig() configuration.previewScreenMode = .enabled( previewScreenConfiguration: .init( shareButtonsMode: .enabled( shareButtonsConfiguration: .init(facebookId: "1234567890") ) ) ) let photoEditorSDK = BanubaPhotoEditor( token: "***", configuration: configuration ) ``` Read more about sharing to Instagram and Facebook stories at https://developers.facebook.com/docs/instagram/sharing-to-stories/. The sharing screen can be disabled entirely. In such case a user will be redirected back to your hosting app after tapping the "Save" button at image editing screen: ```swift var configuration = PhotoEditorConfig() configuration.previewScreenMode = .disabled ``` Alternatively, you can also hide the social networks sharing buttons while keeping the sharing screen: ```swift var configuration = PhotoEditorConfig() configuration.previewScreenMode = .enabled( previewScreenConfiguration: .init(shareButtonsMode: .disabled) ) ``` ## Configuration of the image editing screen To prevent the automatic saving of final edited image to the user's photo library set `false` to `saveResultToPhotoLibrary` in `EditorScreenConfiguration`: ```swift var configuration = PhotoEditorConfig() configuration.editorScreenConfiguration = .init(saveResultToPhotoLibrary: false) ``` ## Add localization strings The Photo Editor SDK displays a number of strings that can be freely changed in the Localizable.strings file of your app. Please, make sure that you've included all the strings with the `photoEditor` key prefix in your app's Localizable.strings file from the [Localized Strings](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/Example/Example/en.lproj/Localizable.strings) file provided with the sample project to prevent placeholders from showing up in the photo editor's UI. If you are still seeing placeholders, make sure that the App Language in the Xcode Scheme Options is set to English as this is the only language supported by this demo app. --- ## Banuba Photo Editor SDK Requirements on iOS Guide contains general requirements for using Photo Editor SDK on iOS. ## License Token Before you commit to a license, you are free to test all the features of the SDK for free. The trial period lasts 14 days. Send us a message to start the [Photo Editor SDK trial](https://www.banuba.com/photo-editor-sdk#form). We will get back to you with the trial token. Feel free to contact us if you have any questions regarding the [Photo Editor SDK](https://www.banuba.com/support). ## Project settings This is what you need to run the AR Photo Editor SDK: - Swift 5.10 or newer, - Xcode 16.0 or newer, - iOS 15.0 or newer. ## SDK size | Photo Editor | Total size | |:------------:|:----------:| |:white_check_mark:| 121 MB | --- ## Installation with Swift Package Manager on iOS Learn the [SPM Getting Started Guide](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) if you are new to SPM. The integration of the PE SDK via SPM is demonstrated in the [spm branch](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/spm) of our native sample. Complete the following steps to get the Photo Editor SDK dependencies using SPM: 1. open `App project` and navigate to the `Package Dependencies` tab; 2. for every package that needs to be installed click the `plus` button and type its repo url; 3. choose `Exact Version` and type the newest SDK version. The following module must be installed: | Package name | GitHub link | |----------|-------------| | BanubaPhotoEditorSDK-iOS | https://github.com/Banuba/BanubaPhotoEditorSDK-iOS | --- ## iOS Requirements and Installation Guide # Banuba Video Editor SDK on iOS Learn about the iOS requirements and features for the Banuba Video Editor SDK. See supported versions, compatible devices, & other details. ## SDK integration video You can watch a follow along video below demonstrating key steps of SDK integration in a project: ## License Token Before you commit to a license, you are free to test all the features of the SDK for free. The trial period lasts 14 days. Send us a message to start the [Video Editor SDK trial](https://www.banuba.com/video-editor-sdk#form). We will get back to you with the trial token. Feel free to contact us if you have any questions regarding [Video Editor SDK](https://www.banuba.com/support). ## Project settings This is what you need to run the AI Video Editor SDK: - Swift 5.9 or newer, - Xcode 16.0 or newer, - iOS 15.0 or newer. - :white_check_mark: arm64, :white_check_mark: arm64e, :exclamation: x86-64 limited support ## Supported media formats | Audio | Video | Images | | ---------- | --------- | ----------- | |.mp3, .aac, .wav, .m4a, .flac, .aiff | .mp4, .mov, .m4v | .bmp, .gif, .heic, .jpeg, .jpg, .png, .tiff ## SDK size Banuba Video Editor SDK with all the core editing features only adds 14 Mb of space to your app’s download size. This parameter can change based on the number of features you want. |SDK| Total size | |:------------|:-----------:| |Banuba Video Editor SDK (core editor)| 14 Mb | |Banuba Video Editor SDK (full bundle)| 45 Mb | |Video Editor with Photo Editor | 80 Mb | |Video Editor with face augmented reality features | 45 - 140 Mb | --- ## Installation with Swift Package Manager Learn the [SPM Getting Started Guide](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) if you are new to SPM. :::note The following guide is compatible with the VE SDK v1.31.3 and newer. If you are looking for a way to install v1.31.2 or older with SPM, please, visit the [legacy docs](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/main/mdDocs/quickstart.md#spm) ::: The integration of the VE SDK via SPM is demonstrated in the [spm branch](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/spm) of our native sample. Complete the following steps to get the Video Editor SDK dependencies using SPM: 1. open `App project` and navigate to the `Package Dependencies` tab; 2. for every package that needs to be installed click the `plus` button and type its repo url; 3. choose `Exact Version` and type the newest SDK version. ## Mandatory modules The following modules must be installed in all cases: | Package name | GitHub link | |----------|-------------| | BanubaVideoEditorSDK-iOS | https://github.com/Banuba/BanubaVideoEditorSDK-iOS | If you are **not** using the Face AR SDK together with the VE, you must add one additional module: | Package name | GitHub link | |----------|-------------| | BanubaSDKSimple-IOS | https://github.com/Banuba/BanubaSDKSimple-IOS | ## Face AR interoperation modules If your license includes the Face AR SDK, please, add the additional modules listed below: | Package name | GitHub link | |----------|-------------| | BanubaSDK-iOS | https://github.com/Banuba/BanubaSDK-iOS | The Face AR packages (`BNB***`) hosted by the `sdk-banuba` account follow their own versioning. The following table specifies the mapping between the compatible VE and Face AR versions. Please, make sure that you specify the correct Face AR version, otherwise run-time incompatibilities may arise leading to crashes: | Video Editor SDK versions | Compatible FaceAR SDK version | |----------------------------------------|-------------------------------| | 1.48.0 | 1.17.5 | | 1.47.0, 1.47.1, 1.47.2 | 1.17.4 | | 1.44.0, 1.45.0, 1.45.1 , 1.46.0 | 1.17.3 | | 1.43.0 | 1.17.1 | | 1.42.0, 1.42.1 | 1.17.0 | | 1.39.0, 1.40.0, 1.41.0 | 1.16.1 | | 1.38.1 | 1.15.2 | | 1.38.0, 1.37.0, 1.36.6 | 1.14.2 | | 1.36.2 | 1.14.1 | | 1.36.0 | 1.14.0 | | 1.35.1 | 1.12.0 | | 1.35.0 | 1.11.1 | | 1.34.1, 1.34.0, 1.33.3, 1.33.2, 1.33.1 | 1.10.1 | | 1.33.0 | 1.9.3 | | 1.32.2, 1.32.1, 1.32.0 | 1.9.1 | If your license includes [AR Cloud](guide_far_arcloud.md), please, also add the following package: | Package name | GitHub link | |----------|-------------| | BanubaARCloudSDK-IOS | https://github.com/Banuba/BanubaARCloudSDK-IOS | ## Optional modules You may skip the module below only if your app provides custom implementations of [Audio Browser](guide_audio_content.md). Otherwise make sure to add it to your project: | Package name | GitHub link | |----------|-------------| | BanubaAudioBrowserSDK-iOS | https://github.com/Banuba/BanubaAudioBrowserSDK-iOS | --- ## FAQ on iOS These are the answers to the most common questions asked about our SDK. ## How do I start the Video Editor with a preselected audio track? To do so, add the MediaTrack instance as a parameter to `VideoEditorLaunchConfig` which is used for starting the Video Editor method: ```swift let cameraLaunchConfig = VideoEditorLaunchConfig( entryPoint: .camera, hostController: self, musicTrack: MediaTrack(...), animated: true ) videoEditorSDK?.presentVideoEditor( withLaunchConfiguration: cameraLaunchConfig, completion: nil ) ``` ## What are the available entry points of the Video Editor? The Video Editor supports multiple launch entry points that are declared in `PresentEventOptions.EntryPoint` to meet all your requirements: ``` swift public enum EntryPoint: String, Codable { case camera case pip case trimmer case editor case drafts case gallery } ``` To open the Video Editor at the desired screen, use `VideoEditorLaunchConfig` that should be passed to the `presentVideoEditor` method of the `BanubaVideoEditor` class: ``` swift public func presentVideoEditor( withLaunchConfiguration configuration: VideoEditorLaunchConfig, completion: (() -> Void)? ) {} ``` 1. Launch from the Camera screen where the user can record a video or take a picture: ``` swift let launchConfig = VideoEditorLaunchConfig( entryPoint: .camera, hostController: UIViewController, musicTrack: MediaTrack, animated: Bool ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` 2. Launch from the Camera screen in the Picture-in-Picture (PIP) mode: :::important The Video Editor will not open in the PIP mode if your license token does not support the PIP feature. ::: ``` swift let launchConfig = VideoEditorLaunchConfig( entryPoint: .pip, hostController: UIViewController, pipVideoItem: URL, animated: Bool ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` 3. Launch from the Trimmer screen where the user can trim a video, add transitions and move to the editing screen for adding effects: ``` swift let launchConfig = VideoEditorLaunchConfig( entryPoint: .trimmer, hostController: UIViewController, videoItems: [URL], musicTrack: MediaTrack, animated: Bool ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` 4. Launch from the Editor screen where the user can add effects to a video: ``` swift let launchConfig = VideoEditorLaunchConfig( entryPoint: .editor, hostController: UIViewController, videoItems: [URL], musicTrack: MediaTrack, animated: Bool ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` 5. Launch from the Drafts screen where the user can pick any non-completed draft and proceed with making a video: ``` swift let launchConfig = VideoEditorLaunchConfig( entryPoint: .drafts, hostController: UIViewController, animated: Bool ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` 6. Launch from the Gallery screen where the user can select videos or photos and proceed to the Editor screen: ``` swift let launchConfig = VideoEditorLaunchConfig( entryPoint: .gallery, hostController: UIViewController, animated: Bool ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfig, completion: nil ) ``` ## How do I use the Video Editor several times from different entry points? Before you want to use VideoEditor again, you need to deinitialize your current editor instance in your [entry point class scope](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/d9733e78a6a752dd8fad849f6aa6d5553eb07f56/Example/Example/ViewController.swift#L675). You need to set 'yourVideoEditorSdkInstance' = nil after following the funcs called [done](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/d9733e78a6a752dd8fad849f6aa6d5553eb07f56/Example/Example/ViewController.swift#L660) and [cancel](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/d9733e78a6a752dd8fad849f6aa6d5553eb07f56/Example/Example/ViewController.swift#L678): ```swift // Video Editor Delegate implementation example extension ViewController: BanubaVideoEditorDelegate { func videoEditorDone(_ videoEditor: BanubaVideoEditor) { // User finished editing sessoin, need to dismiss video editor and export video videoEditorSDK?.dismissVideoEditor( animated: true ) { [weak self] in self?.exportVideo(...) { ... in self?.'yourVideoEditorSdkInstance' = nil } } } func videoEditorDidCancel( _ videoEditor: BanubaVideoEditor ) { // User canceled editing sessoin, need to dismiss video editor videoEditorSDK?.dismissVideoEditor( animated: true, completion: { self?.'yourVideoEditorSdkInstance' = nil } ) } } ``` Use the following approach if you want to [create the BanubaVideoEditor instance](https://github.com/Banuba/ve-sdk-ios-integration-sample/blob/d9733e78a6a752dd8fad849f6aa6d5553eb07f56/Example/Example/ViewController.swift#L42) again. For example, as your tap button action: ```swift @IBAction func videoEditorButtonTapped(_ sender: UIButton) { if 'yourVideoEditorSdkInstance' == nil { 'yourVideoEditorSdkInstance' = BanubaVideoEditor(...) } } ``` ## How do I add a color filter (LUT)? Color Filters (LUTs) are special graphic files that are placed in the / [luts directory](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/luts) inside the main project folder. To add your own icon to be used in order to represent that specific effect on the list, you must place it in the / [assets folder](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/Assets.xcassets/ColorEffectsPreview). The icon resource name must match the image file name in the / luts directory and end with `_preview`. The display name for the resource is set in the [localization files](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/en.lproj/Localizable.strings#L275). The key for the translation string must start with `com.banuba.filter.name.{lut file name}` and end with the name of the lut file. ## I want to enable the slideshow animation. To be able to turn on the slideshow animation, use the following property of the `RecorderConfiguration` and `CombinedGalleryConfiguration` entities: ```swift let config = VideoEditorConfig() config.combinedGalleryConfiguration.isPhotoSequenceAnimationEnabled = true config.recorderConfiguration.isPhotoSequenceAnimationEnabled = true ``` ## I want to change the cursor color. All you need is just to set your color into the `cursorColor` parameter in the `MainOverlayViewControllerConfig` entity: ```swift let config = VideoEditorConfig() config.overlayEditorConfiguration.mainOverlayViewControllerConfig.cursorColor = .white ``` ## How can I get the track name of the audio used in my video after the export? ```swift /// Video Editor main entity and entry point. /// Can present and hide root view controller. /// Has default export method. public class BanubaVideoEditor { /// Simple metadata of music composition settings public var musicMetadata: MusicEditorMetadata? { get } ... } ``` `MusicEditorMetadata` contains the array of `MusicEditorTrack` which contains the following fields: ```swift // MARK: - MusicEditorTrack public struct MusicEditorTrack: Codable { ///Track URL public var url: URL ///Track original URL public var originalURL: URL ///Track title public var title: String ///Track id public var id: Int32 /// Track volume public var volume: Float ... } ``` or if you want to know what track was played on the camera screen, you can use: ```swift /// Video Editor main entity and entry point. /// Can present and hide root view controller. /// Has default export method. public class BanubaVideoEditor { /// Music track which will be played on camera recording public var musicTrack: MediaTrack? { get } ... } ``` ## I want to change the font. You can change the font for the whole Video Editor by calling in `VideoEditorConfig` this method: ```swift func applyFont(_ font: UIFont) ``` or change the font for each screen separately by calling the appropriate methods: ```swift func updateFullScreenActivityFonts(_ font: UIFont) func updateRecorderFonts(_ font: UIFont) func updateAlbumsFonts(_ font: UIFont) func updateEditorFonts(_ font: UIFont) func updateToastFonts(_ font: UIFont) func updateTextEditorFonts(_ font: UIFont) func updateSlideShowFonts(_ font: UIFont) func updateTrimVideoFonts(_ font: UIFont) func updateTrimVideosFonts(_ font: UIFont) func updateFilterFonts(_ font: UIFont) func updateExtendedVideoCoverSelectionFonts(_ font: UIFont) func updateAlertFonts(_ font: UIFont) ``` Changing the font does not affect its size. The font size will be taken by default or specified by you in the entity configuration. ## I want to use custom icons. Any icon in the mobile Video Editor SDK can be replaced. This is how: 1. load custom images to the Assets catalog; 2. locate the screen with the icon you want to change in the [VideoEditorConfig](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/VideoEditorModule.swift#L75) entity. More examples: [Camera Screen](guide_camera.md), [Editor Screen](guide_editor.md); 3. find the specific element and override it with the resource name or use UIImage if available. ## The “luts” file couldn’t be opened because there is no such file. This error occurs because your application bundle doesn't contain the required luts folder. You need to copy the [luts](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/luts) folder to your project. ## I want to add audio filters. The filters' availability depends on the token. However, in order for them to be available, you need to add an implementation of the `VoiceFilterProvider` entity. Here is an example on how to inherit `VoiceFilterProvider` to your own entity: ```swift /// Example voice filter provider struct ExampleVoiceFilterProvider: VoiceFilterProvider { private let filters: [VoiceFilter] // MARK: - VoiceFilterProvider func provideFilters() -> [VoiceFilter] { return filters } init() { filters = [ VoiceFilter( type: .elf, title: NSLocalizedString("com.banuba.musicEditor.elf", comment: "Elf filter title"), image: UIImage(named:"elf") ), VoiceFilter( type: .baritone, title: NSLocalizedString("com.banuba.musicEditor.baritone", comment: "Baritone filter title"), image: UIImage(named:"baritone") ), VoiceFilter( type: .echo, title: NSLocalizedString("com.banuba.musicEditor.echo", comment: "Echo filter title"), image: UIImage(named:"echo") ), VoiceFilter( type: .giant, title: NSLocalizedString("com.banuba.musicEditor.giant", comment: "Giant filter title"), image: UIImage(named:"giant") ), VoiceFilter( type: .robot, title: NSLocalizedString("com.banuba.musicEditor.robot", comment: "Robot filter title"), image: UIImage(named:"robot") ), VoiceFilter( type: .squirrel, title: NSLocalizedString("com.banuba.musicEditor.squirrel", comment: "Squirrel filter title"), image: UIImage(named:"squirrel") ) ] } } ``` Then the instance of ExampleVoiceFilterProvider needs to be passed to the configuration: ```swift var config = VideoEditorConfig() config.musicEditorConfiguration.audioTrackLineEditControllerConfig.voiceFilterProvider = ExampleVoiceFilterProvider() ``` ## I want to change icons and name for effects. The name of the icon for the effect must match the identifier of the effect. Below there is a table with the name, ID and icon of the default effects: | Default image effect | Name | ID | | ---------- | --------- | ----------- | | | Acid whip | 102000 | | Cathode | 102001 | | DV Cam | 102002 | | Flash | 102003 | | Glitch | 102004 | | Glitch 2 | 102005 | | Heat Map | 102006 | | Lumiere | 102007 | | Mirror | 102008 | | Mirror 2 | 102009 | | Pixel Dynamic | 102010 | | Pixel Static | 102011 | | Polaroid | 102012 | | Rave | 102013 | | Soul | 102014 | | Stars | 102015 | | TV-Foam | 102016 | | Transition | 102017 | | Transition 2 | 102018 | | VHS | 102019 | | VHS 2 | 102020 | | Zoom | 102021 | | Zoom 2 | 102022 | | 0.5x | 104000 | | 2x | 104001 In order to change the name of the effect, you need to do it in the [localization file](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/en.lproj/Localizable.strings#L254). ## I want to get the exported video metadata. In order to find out which filter, effects, masks and music were applied to the video, you need to refer to the instance of the `BanubaVideoEditor` entity. The instance: ``` swift let videoEditorSDK = BanubaVideoEditor( ... ) // to get color filter let videoFilter = videoEditorSDK?.metadata?.colorOnVideoMetadata // to get effects let videoEffects = videoEditorSDK?.metadata?.effectsOnVideoMetadata // to get gifs let videoGif = videoEditorSDK?.metadata?.gifOnVideoMetadata // to get texts let videoText = videoEditorSDK?.metadata?.textOnVideoMetadata // to get music track from record screen let videoMusicTrack = videoEditorSDK?.musicTrack // to get music tracks from editor screen let videoTracks = videoEditorSDK?.musicMetadata?.tracks ``` ## I want to change the codec type from h264 to h265. All you need is to just set `useHEVCCodecIfPossible` to `true` in the `VideoEditorConfig, ExportVideoInfo or ExportVideoConfiguration` entities. The first one you need when you're creating `BanubaVideoEditor`, the two last ones - when you're preparing a video for export: ```swift let exportVideoInfo = ExportVideoInfo( resolution: ..., useHEVCCodecIfPossible: true ) let configuration = ExportVideoConfiguration( fileURL: ..., quality: ..., useHEVCCodecIfPossible: true, watermarkConfiguration: ... ) ``` ## How do I specify the video file saving directory? In `ExportVideoConfiguration` set the desired path in the fileURL parameter: ```swift let exportVideoConfigurations: [ExportVideoConfiguration] = [ ExportVideoConfiguration( fileURL: fileURL, quality: .auto, useHEVCCodecIfPossible: true, watermarkConfiguration: watermarkConfiguration ) ] ``` ## How do I change the language (how do I add new locale support)? 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 iOS way. See how to [create locale directories and resource files](https://developer.apple.com/documentation/xcode/localization) for more details. After adding a new locale resource file into your application with the integrated VE SDK, you need to re-define the VE SDK strings keys with the new locale string values. To do that, you need to add all the needed string keys in the new locale `Localizable.strings` file. You can find the main VE SDK string keys you need in the [Localizable.strings](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/en.lproj/Localizable.strings) file. The newly added locale will be applied after the device language is changed by the system settings. ## How can I change the extension of the exported video? To save the video in the format you want, you just need to add the appropriate `PathComponent` when creating the video URL: ```swift let videoURL = manager.temporaryDirectory.appendingPathComponent("tmp.mov") ``` See an example in the [sample](https://github.com/Banuba/ve-sdk-ios-integration-sample/tree/main/Example/Example/ViewController.swift#L238). See all the formats supported for the video export [here](guide_export.md). ## I want to change the order of masks, video effects or filters. The SDK allows to reorder masks and filters in the way you need. To achieve this, use the `preferredLutsOrder` and `preferredMasksOrder` properties: ``` swift let config = VideoEditorConfig() // Sorting for the record screen config.recorderConfiguration.recorderEffectsConfiguration.preferredLutsOrder = [ "egypt", "norway", "japan" ... ] config.recorderConfiguration.recorderEffectsConfiguration.preferredMasksOrder = [ "XYScanner", "Background" ... ] // Sorting for the post processing screen config.filterConfiguration.preferredLutsOrder = [ "byers", "sunset", "vinyl" ... ] config.filterConfiguration.preferredMasksOrder = [ "XYScanner", "Background" ... ] config.filterConfiguration.preferredVideoEffectOrderAndSet = [ VisualEffectApplicatorType.acid, VisualEffectApplicatorType.dvCam ... ] ``` ## Is it possible to disable the Trim screen? It is possible to turn off the trimmer screen after the camera screen. The trimmer screen will still be accessible after importing media files from the gallery. To disable it, just change the `supportsTrimRecordedVideo` property to `false` in the `FeatureConfiguration` entity: ``` swift func createVideoEditorConfig() -> VideoEditorConfig { var config = VideoEditorConfig() ... // Default is false config.featureConfiguration.supportsTrimRecordedVideo = true ... return config } ``` ## Is it possible to disable the transition effects? Transitions are visual effects applying to the segue between two videos. They are provided with the Banuba Video Editor SDK **by default**. To disable or enable transitions, set the `useTransitions` flag inside the `FeatureConfiguration` class to false or true respectively: ``` swift /// Allows you to use transition effects between videos /// Defaults is true public var useTransitions: Bool ``` Example: ``` swift let config = VideoEditorConfig() config.featureConfiguration.useTransitions = true ``` :::note Transition effects are not being played if the closest video (either to the left or to the right of the transition icon) is very short. ::: ## How can I convert one or several still images to a video programmatically? If you would like to create a video from UIImage instances without opening the Video Editor, you can use our API SDK tailored for solutions with the custom built UI, namely the `exportSlideshowImages` method of the `VEExport` class. ## Is it possible to store localization strings in a file other than Localizable.strings? Sometimes other 3rd party dependencies overwrite the `Localizable.strings` file stored in the app bundle during compilation. In such cases it is also possible to store the localization strings in the `Banuba.strings` file. ## How to change the default animation (exposure view) shown after tapping on the camera preview? Create a class conforming to `ExternalViewControllerFactory` that implements the `exposureViewFactory` property. In the following example the default view factory `DefaultExposureViewFactory` provided by the SDK is used: ```swift class ExampleViewControllerFactory: ExternalViewControllerFactory { var exposureViewFactory: AnimatableViewFactory? var musicEditorFactory: MusicEditorExternalViewControllerFactory? var countdownTimerViewFactory: CountdownTimerViewFactory? init() { exposureViewFactory = DefaultExposureViewFactory() } } ``` Pass the instance of your factory to the `externalViewControllerFactory` argument of `BanubaVideoEditor`: ```swift let videoEditorSDK = BanubaVideoEditor( token: ..., configuration: ..., // highlight-add-next-line + externalViewControllerFactory: ExampleViewControllerFactory() ) ``` ## How to change the default countdown timer shown on the camera screen? Create a class conforming to `ExternalViewControllerFactory` that implements the `countdownTimerViewFactory` property. In the following example the view factory that provides the built-in class `CountdownView` is implemented: ```swift class ExampleViewControllerFactory: ExternalViewControllerFactory { var exposureViewFactory: AnimatableViewFactory? var musicEditorFactory: MusicEditorExternalViewControllerFactory? var countdownTimerViewFactory: CountdownTimerViewFactory? init() { countdownTimerViewFactory = CountdownTimerViewControllerFactory() } /// Example countdown timer view factory for Recorder countdown animation class CountdownTimerViewControllerFactory: CountdownTimerViewFactory { func makeCountdownTimerView() -> CountdownTimerAnimatableView { let countdownView = CountdownView() countdownView.frame = UIScreen.main.bounds countdownView.font = countdownView.font.withSize(102.0) countdownView.digitColor = .white return countdownView } } } ``` Pass the instance of your factory to the `externalViewControllerFactory` argument of `BanubaVideoEditor`: ```swift let videoEditorSDK = BanubaVideoEditor( token: ..., configuration: ..., // highlight-add-next-line + externalViewControllerFactory: ExampleViewControllerFactory() ) ``` ## How to change the global color appearance (palette) of the SDK? Create an instance of `VideoEditorColorsPalette` with the desired colors and assign it to `VideoEditorConfig`: ```swift var config = VideoEditorConfig() config.setupColorsPalette( VideoEditorColorsPalette( primaryColor: .white, secondaryColor: .black, accentColor: .white, effectButtonColorsPalette: EffectButtonColorsPalette( defaultIconColor: .white, defaultBackgroundColor: .clear, selectedIconColor: .black, selectedBackgroundColor: .white ), addGalleryItemBackgroundColor: .white, addGalleryItemIconColor: .black, timelineEffectColorsPalette: TimelineEffectColorsPalette.default ) ) ``` ## How to change the video preview resizing mode at Recording Screen Starting from the 1.35.0 version of the SDK the `RecorderConfiguration` has the property called `previewScalingMode` with two possible options `aspectFill` and `aspectFit`: ```swift /// Specifies options of scaling the video preview from camera at recorder screen public enum RecorderPreviewScalingMode { /// Fill entire preview screen with camera video feed case aspectFill /// Fit camera video in the screen so that entire video is always visible case aspectFit } var config = VideoEditorConfig() config.recorderConfiguration.previewScalingMode = .aspectFit ``` By default the `aspectFill` option is used. It provides the same preview scaling behaviour as in previous releases of the SDK. ## How to collect logs when I encounter an issue? If you encounter a crash or other issue and are unsure how to provide logs to the support team, follow steps provided below: 1. Connect your iPhone to your Mac via USB or Wi-Fi. 2. Open Xcode → go to Window → Devices and Simulators. 3. In the list, select your device. 4. In the right panel, click Open Console. 5. You will now see all system and app logs in real time. 6. To view only logs for your app, use the Filter by process field and enter the app’s Bundle ID. Once you’ve collected the logs, copy them into a separate text file and send it to the [support team](https://www.banuba.com/support) for further investigation. --- ## Video Templates on iOS Templates let users create stunning videos quickly and easily using predefined sets of effects, transitions, and music. All it takes to make a shareable piece is changing the placeholders. With templates, even people who are new to video editing or just lack time can make impressive content in minutes.       :::important The ```Video Templates``` is not enabled by default. Contact Banuba representatives to know more. ::: ## Launch Video Templates "se the ```.videoTemplates``` entry point in ```VideoEditorLaunchConfig``` to launch the Video Editor SDK from Video templates. ```swift let launchConfiguration = VideoEditorLaunchConfig( entryPoint: .videoTemplates, hostController: self, animated: true ) videoEditorSDK.presentVideoEditor( withLaunchConfiguration: launchConfiguration, completion: nil ) ```