Export video compositions as MP4 files with H.264 encoding, progress events, and configurable bitrate and resolution.
MP4 is the most widely supported video format. CE.SDK renders the page timeline, encodes frames with H.264, and muxes audio into the MP4 container on a background export engine.
This guide covers exporting an existing page block to MP4, tracking progress,
canceling an in-progress export, configuring resolution and bitrate, exporting
a timeline segment, and writing the returned ByteBuffer to a file.
Export to MP4#
Call engine.block.exportVideo(...) with a page block, timeline range, and
MimeType.MP4. The method returns a ByteBuffer containing the encoded video
data.
suspend fun exportMp4( engine: Engine, page: DesignBlock, pageDuration: Double,): ByteBuffer { val videoBytes = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = pageDuration, mimeType = MimeType.MP4, progressCallback = {}, )
check(videoBytes.hasRemaining()) { "MP4 export is empty" } return videoBytes}Pass the page duration when you want to export the full timeline. The export API currently supports page blocks for video output.
Tracking Export Progress#
The progressCallback receives rendered frames, encoded frames, and total
frames. Use the encoded frame count for user-facing progress because encoding is
usually the slower stage.
suspend fun exportMp4WithProgress( engine: Engine, page: DesignBlock, pageDuration: Double, progressEvents: MutableList<ExportVideoProgress>,): ByteBuffer { val videoBytes = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = pageDuration, mimeType = MimeType.MP4, progressCallback = { progress -> progressEvents += progress Log.i( "ExportToMp4Guide", "Encoded ${progress.encodedFrames} of ${progress.totalFrames} frames", ) }, )
check(videoBytes.hasRemaining()) { "progress MP4 export is empty" } return videoBytes}Cancel an Export#
engine.block.exportVideo(...) is a suspending Android API, so cancel the
coroutine that owns the export. Keep the returned Job in your UI state or
view model and call cancel() when the user cancels the operation.
fun startCancelableMp4Export( scope: CoroutineScope, engine: Engine, page: DesignBlock, pageDuration: Double, onProgress: (ExportVideoProgress) -> Unit,): Job = scope.launch(Dispatchers.Main) { try { val videoBytes = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = pageDuration, mimeType = MimeType.MP4, progressCallback = onProgress, ) check(videoBytes.hasRemaining()) { "cancelable MP4 export is empty" } } catch (exception: CancellationException) { Log.i("ExportToMp4Guide", "MP4 export canceled") throw exception }}
fun cancelMp4Export(exportJob: Job) { exportJob.cancel()}Let CancellationException propagate after cleanup or logging so structured
concurrency can finish the canceled job correctly. The Android binding stops
the background export engine when the suspending export exits.
Configure Video Encoding#
Pass ExportVideoOptions to control the target box, framerate, and bitrate.
Resolution and Framerate#
Set targetWidth, targetHeight, and frameRate when you need a target box
for video output and a predictable playback cadence. CE.SDK renders the page
large enough to fill the target box while preserving its aspect ratio, so the
encoded frame may exceed one requested axis when the page and target box use
different aspect ratios.
suspend fun exportMp4WithResolutionOptions( engine: Engine, page: DesignBlock, pageDuration: Double,): ByteBuffer { val options = ExportVideoOptions( targetWidth = 1280F, targetHeight = 720F, frameRate = 30F, // Use 30 fps for smooth playback on common mobile targets. )
val videoBytes = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = pageDuration, mimeType = MimeType.MP4, progressCallback = {}, options = options, )
check(videoBytes.hasRemaining()) { "resolution MP4 export is empty" } return videoBytes}Video and Audio Bitrate#
Set videoBitrate and audioBitrate in bits per second to control file size
and compression quality. Use 0 when you want the engine to choose a bitrate
automatically.
suspend fun exportMp4WithBitrateOptions( engine: Engine, page: DesignBlock, pageDuration: Double,): ByteBuffer { val options = ExportVideoOptions( videoBitrate = 8_000_000, audioBitrate = 128_000, )
val videoBytes = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = pageDuration, mimeType = MimeType.MP4, progressCallback = {}, options = options, )
check(videoBytes.hasRemaining()) { "configured MP4 export is empty" } return videoBytes}Export Parameters and Options#
| Parameter or option | Default | Description |
|---|---|---|
mimeType |
Required | Use MimeType.MP4 for MP4 video output. |
timeOffset |
Required | Start time in seconds on the page timeline. |
duration |
Required | Length in seconds to export. Pass the page duration for the full timeline. |
progressCallback |
Required | Receives ExportVideoProgress with rendered, encoded, and total frame counts. |
targetWidth |
null |
Optional target-box width in pixels. Use with targetHeight; the final width may be larger when aspect ratios differ. |
targetHeight |
null |
Optional target-box height in pixels. Use with targetWidth; the final height may be larger when aspect ratios differ. |
frameRate |
30F |
Target framerate in Hz. |
videoBitrate |
0 |
Video bitrate in bits per second. 0 enables automatic selection. |
audioBitrate |
0 |
Audio bitrate in bits per second. 0 enables automatic selection. |
allowTextOverhang |
false |
Includes text bounds that account for glyph overhangs. |
Export a Partial Timeline#
Use timeOffset and duration to export a segment without changing the scene.
Both values use seconds relative to the page timeline.
suspend fun exportPartialTimeline( engine: Engine, page: DesignBlock,): ByteBuffer { val videoBytes = engine.block.exportVideo( block = page, timeOffset = 0.25, // Start after the first quarter-second to demonstrate offsets. duration = 0.5, // Export a short segment so partial exports stay fast. mimeType = MimeType.MP4, progressCallback = {}, options = ExportVideoOptions( frameRate = 12F, // Lower fps keeps this short preview segment lightweight. ), )
check(videoBytes.hasRemaining()) { "partial MP4 export is empty" } return videoBytes}Handle Export Results#
The returned ByteBuffer stays in memory. Write it to app storage, upload it to
your backend, or pass it to another Android API without copying it into an
intermediate array.
suspend fun writeMp4ToTempFile(videoBytes: ByteBuffer): File = withContext(Dispatchers.IO) { val outputFile = File.createTempFile("export-to-mp4-", ".mp4") val videoData = videoBytes.asReadOnlyBuffer() outputFile.outputStream().channel.use { channel -> while (videoData.hasRemaining()) { channel.write(videoData) } }
check(outputFile.length() > 0L) { "MP4 output file is empty" } outputFile}Delete temporary files after your app finishes sharing, uploading, or processing the exported video.
Troubleshooting#
- Export fails or hangs: Make sure the scene and media assets are fully
loaded before starting the export. Check
engine.editor.getAvailableMemory()before large exports and reduce the requested size or framerate on low-memory devices. - Poor video quality: Increase
videoBitrateand keep source media at least as large as the target size. - Slow exports: Lower
targetWidth,targetHeight,frameRate, or bitrate. Clamp requested dimensions withengine.editor.getMaxExportSize()so the app does not offer unsupported output sizes. - Playback issues on devices: Lower the requested size, framerate, or bitrate, and test the generated MP4 on the devices your app supports.
API Reference#
| API | Description |
|---|---|
engine.block.exportVideo(block=_, timeOffset=_, duration=_, mimeType=_, progressCallback=_, options=_, onPreExport=_, uriResolver=_) |
Exports a page timeline to a video ByteBuffer and reports rendering and encoding progress. |
CoroutineScope.launch(context=_) |
Starts a coroutine that can own a long-running export operation. |
Job.cancel() |
Requests cancellation of the coroutine running the export. |
engine.editor.getAvailableMemory() |
Returns available memory in bytes so apps can avoid starting large exports on low-memory devices. |
engine.editor.getMaxExportSize() |
Returns the maximum export dimension in pixels for constraining requested output width and height. |
ExportVideoOptions(targetWidth=_, targetHeight=_, frameRate=_, videoBitrate=_, audioBitrate=_, allowTextOverhang=_) |
Configures the MP4 target box, framerate, bitrate, and text overhang behavior. |
Next Steps#
- Export Overview - Compare all supported export formats
- Size Limits - Understand and configure limits on exported file dimensions or data size.
- Export Audio - Export audio tracks separately
- Partial Export - Learn how to export specific blocks, groups, and page elements instead of entire scenes using CE.SDK’s programmatic export API.