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. A canceled job stops the
export in the engine at once. The Android binding then stops the background
export engine.
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. Without frameRate, the video uses the scene’s
scene/frameRate (30 by default).
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 |
null |
Target framerate in Hz, greater than 0 and at most 240. null uses the scene’s scene/frameRate. |
videoBitrate |
0 |
Video bitrate in bits per second. 0 enables automatic selection. |
audioBitrate |
0 |
Audio bitrate in bits per second. 0 enables automatic selection. |
coverTimeOffset |
null |
null uses current page playback, clamped to the exported frames. An explicit offset is relative to the exported video start and must be finite and within [0, duration). |
allowTextOverhang |
false |
Includes text bounds that account for glyph overhangs. |
Set a Video Cover#
By default, MP4 exports include a cover from the page playback position when
export starts. Omit coverTimeOffset or pass null to use this position. For a
partial export, playback stays relative to the page timeline. A position outside
the exported segment uses its first or last frame.
Set coverTimeOffset to override the default in seconds from the beginning of the
exported video. It must be finite, at least 0, and less than the export duration;
invalid values fail the export. For a partial export, this offset is relative to
the exported segment, rather than the page timeline.
The engine embeds the exported frame containing that time as a PNG in the MP4
covr metadata. Within the exported range, a time between frame boundaries
selects the earlier frame, matching playback rather than advancing to the next frame.
The cover preserves the exported aspect ratio, fits within 512 × 512 pixels, and
is never upscaled. Transparent areas use black, matching video playback.
Displaying that cover depends on the player or file browser.
suspend fun exportMp4WithCover( engine: Engine, page: DesignBlock, pageDuration: Double,): ByteBuffer = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = pageDuration, mimeType = MimeType.MP4, progressCallback = {}, // Override current playback with a cover from the exported video start. options = ExportVideoOptions(coverTimeOffset = 0.0),)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.