Search Docs
Loading...
Skip to content

To WebM

Export the video editor’s result as a WebM file with VP9 video and Opus audio, on iOS and on Android.

WebM is an open video format. Its codecs are royalty-free, so you can encode and publish them without a patent license. The engine bundles the WebM encoders, so the export works the same on every iOS and Android device.

The @imgly/editor-react-native module uses the native iOS and Android editors, and the default video editor exports MP4. To export WebM, replace the video editor natively on both platforms through the module’s builderClosure. Your JavaScript code stays the same: IMGLYEditor.openEditor() returns an EditorResult whose artifact points to the WebM file.

iOS#

Assign a custom builder for the video preset. Call useWebmVideoEditor() before you open the editor, for example in AppDelegate.application(_:didFinishLaunchingWithOptions:).

import IMGLYEditor
import IMGLYEditorModule
import IMGLYEngine
import SwiftUI
IMGLYEditorModuleSwiftAdapter.shared.builderClosure = { preset, _ in
if preset == .video {
webmVideoEditor()
} else {
EditorBuilder.design()
}
}

The custom editor is the default video editor with one change: the onExport callback passes .webm to OnExport.exportVideo. The helper shows the export progress, writes the file with the .webm extension and returns the EditorResult to React Native.

private func webmVideoEditor() -> EditorBuilder.Builder {
EditorBuilder.custom { settings, _, _, result in
WebmVideoEditor(settings: settings, result: result)
}
}
// The `custom` closure is not isolated to the main actor, so the editor is built in `body`, which is.
private struct WebmVideoEditor: View {
let settings: EditorSettings
let result: EditorBuilder.EditorBuilderResult
var body: some View {
NavigationView {
Editor(EngineSettings(
license: settings.license,
userID: settings.userId,
baseURL: URL(string: settings.baseUri),
host: "react-native",
))
.imgly.configuration {
VideoEditorConfiguration { builder in
builder.onCreate { engine, _ in
if let createScene = try OnCreate.loadFromSettings(settings) {
try await VideoEditorConfiguration.defaultOnCreate(createScene: createScene)(engine)
} else {
try await VideoEditorConfiguration.defaultOnCreate()(engine)
}
}
builder.onExport { engine, eventHandler, _ in
do {
let editorResult = try await OnExport.exportVideo(engine, eventHandler, .webm)
result(.success(editorResult))
} catch {
if error is CancellationError {
return
}
result(.failure(error))
}
}
}
ModalEditorConfiguration(result: result)
}
}
.navigationViewStyle(.stack)
}
}

Android#

Assign a custom builder for the video preset, for example in MainApplication.onCreate. The custom editor needs the Compose setup from the customization guide.

import androidx.compose.runtime.Composable
import androidx.core.net.toUri
import kotlinx.coroutines.CancellationException
import ly.img.editor.Editor
import ly.img.editor.configuration.video.VideoConfigurationBuilder
import ly.img.editor.configuration.video.callback.onCreate
import ly.img.editor.configuration.video.callback.onExport
import ly.img.editor.configuration.video.model.ExportStatus
import ly.img.editor.core.configuration.EditorConfiguration
import ly.img.editor.core.configuration.remember
import ly.img.editor.reactnative.module.IMGLYEditorModule
import ly.img.editor.reactnative.module.builder.EditorBuilder
import ly.img.editor.reactnative.module.builder.EditorBuilderDefaults
import ly.img.editor.reactnative.module.builder.EditorBuilderResult
import ly.img.editor.reactnative.module.model.EditorPreset
import ly.img.editor.reactnative.module.model.EditorSettings
import ly.img.engine.MimeType
IMGLYEditorModule.builderClosure = { preset, _ ->
if (preset == EditorPreset.VIDEO) {
EditorBuilder.custom {
WebmVideoEditor(settings, result, onClose)
}
} else {
EditorBuilder.design()
}
}

The custom editor replaces the exportByteBuffer step of onExport. It calls engine.block.exportVideo with MimeType.WEBM and updates exportStatus, so the editor shows the progress. EditorBuilderDefaults.getExportResult writes the file with the .webm extension and returns the EditorResult to React Native.

@Composable
private fun WebmVideoEditor(
settings: EditorSettings,
result: EditorBuilderResult,
onClose: (Throwable?) -> Unit,
) {
Editor(
license = settings.license,
host = "react-native",
baseUri = settings.baseUri.toUri(),
userId = settings.userId,
configuration = {
EditorConfiguration.remember(::VideoConfigurationBuilder) {
onCreate = {
onCreate(
createScene = {
EditorBuilderDefaults.onCreateScene(
scope = this@Editor,
settings = settings,
defaultUri = "file:///android_asset/scene/video.scene".toUri(),
)
},
)
}
onExport = {
onExport(
exportByteBuffer = {
val engine = editorContext.engine
val page = requireNotNull(engine.scene.getCurrentPage())
engine.block.exportVideo(
block = page,
timeOffset = 0.0,
duration = engine.block.getDuration(page),
mimeType = MimeType.WEBM,
progressCallback = { progress ->
exportStatus = ExportStatus.Loading(
progress = progress.encodedFrames.toFloat() / progress.totalFrames,
)
},
)
},
postExport = {
val editorResult = EditorBuilderDefaults.getExportResult(
scope = this@Editor,
byteBuffer = it,
mimeType = MimeType.WEBM,
)
exportStatus = null
result(Result.success(editorResult))
},
error = {
exportStatus = null
if (it !is CancellationException) {
result(Result.failure(it))
}
},
)
}
}
},
) {
onClose(it)
}
}

Choose the Codecs#

Without options, a WebM export encodes VP9 video and Opus audio. To select VP8 or AV1, Vorbis audio, or a transparency stream, pass export options to exportVideo in the native code. The iOS guide and the Android guide show these options.

Troubleshooting#

  • The editor still exports MP4: The module uses its default video editor when builderClosure is not set. Assign the closure before you call IMGLYEditor.openEditor(), on both platforms.
  • The export fails with ENCODE.VIDEO_CODEC_CONTAINER_MISMATCH or ENCODE.AUDIO_CODEC_CONTAINER_MISMATCH: A codec in the export options does not fit WebM. Use VP8, VP9 or AV1 with Opus or Vorbis.
  • The file does not play on iOS: AVFoundation does not play WebM. Upload the file, or play it with a player that supports WebM.

Next Steps#