KMP 结构适配鸿蒙:视频处理与流媒体理论知识
本文介绍了在Kotlin Multiplatform(KMP)框架下实现跨平台视频处理系统的方案。通过在commonMain中抽象统一接口(VideoPlayer、VideoRecorder),并在各平台模块中利用原生API实现,兼顾性能与平台特性。文章详细阐述了视频播放和录制的核心架构设计,包括状态管理、进度回调和参数配置等关键点。针对Android平台,展示了基于ExoPlayer和Media
项目概述
视频处理与流媒体是现代应用的高级功能。在 KMP 框架下,我们需要实现跨平台的视频播放、录制、转码和流媒体传输。本文详细介绍了如何在 Kotlin Multiplatform 中实现高效的视频处理系统,包括视频播放、录制、转码和鸿蒙系统特定的媒体处理方式。
在实际业务中,视频相关能力经常出现在以下场景中:
- 短视频 / 社交类应用:需要快速加载、播放、拖动、倍速播放与基础编辑。
- 直播与会议:强调低延迟、网络自适应和多端同时在线。
- 监控与物联网:更关注长时间稳定推流、节省带宽和设备端的资源占用。
在 KMP 结构下,这一篇的核心目标是:
- 在 commonMain 中抽象出统一的视频接口(如
VideoPlayer、VideoRecorder),保证业务代码可以最大化复用; - 在各平台模块中利用原生 API 做具体实现(Android、iOS、鸿蒙),兼顾性能与平台特性;
- 为后续的高级功能(自适应码率、转码等)留好扩展点,避免后期重构成本过高。
第一部分:视频处理核心概念
视频播放架构
// commonMain/kotlin/com/example/video/VideoPlayer.kt
expect class VideoPlayer {
suspend fun loadVideo(url: String): Boolean
suspend fun play(): Boolean
suspend fun pause(): Boolean
suspend fun stop(): Boolean
suspend fun seek(positionMs: Long): Boolean
suspend fun setVolume(volume: Float): Boolean
fun getDuration(): Long
fun getCurrentPosition(): Long
fun addListener(listener: VideoPlayerListener)
}
interface VideoPlayerListener {
fun onStateChanged(state: PlayerState)
fun onProgressChanged(position: Long, duration: Long)
fun onError(error: String)
fun onBuffering(progress: Int)
}
enum class PlayerState {
IDLE, LOADING, PLAYING, PAUSED, STOPPED, ERROR
}
这一部分定义的是跨平台统一的视频播放器接口:
expect class VideoPlayer放在commonMain,用于约束所有平台都必须提供相同能力,例如加载视频、播放、暂停、停止、seek 以及音量控制等。VideoPlayerListener用于向 UI 层或上层业务主动回调播放状态和进度,便于实现进度条、播放按钮状态联动、错误提示等。PlayerState将复杂的底层状态简化为几个常用枚举,方便在 Compose 或其他 UI 框架中直接使用。
在设计 KMP 的接口层时,建议注意:
- 尽量只暴露通用能力,不要在公共层引入特定平台才有的细节(比如某个平台独有的缓冲策略);
- 对于可能引起卡顿的操作(如
loadVideo、play),统一设计为suspend方法,方便在协程中调用,避免阻塞主线程; - 通过监听器统一传递错误信息和缓冲进度,避免上层同时关心多个平台的回调差异。
视频录制架构
// commonMain/kotlin/com/example/video/VideoRecorder.kt
expect class VideoRecorder {
suspend fun startRecording(outputPath: String): Boolean
suspend fun stopRecording(): Boolean
suspend fun pauseRecording(): Boolean
suspend fun resumeRecording(): Boolean
suspend fun setVideoQuality(quality: VideoQuality): Boolean
suspend fun setFrameRate(fps: Int): Boolean
fun isRecording(): Boolean
}
enum class VideoQuality {
LOW, MEDIUM, HIGH, ULTRA_HD
}
data class VideoRecordingConfig(
val quality: VideoQuality = VideoQuality.HIGH,
val frameRate: Int = 30,
val bitrate: Int = 5000000,
val audioEnabled: Boolean = true
)
视频录制相对播放来说,对权限、设备性能和存储空间更敏感,因此在公共层就需要提前考虑可配置项:
VideoRecorder提供开始录制、暂停、恢复、停止等核心控制能力,并通过isRecording辅助 UI 做状态显示;VideoQuality枚举预设了几档常见清晰度,实际实现时可以在各平台上映射到对应的分辨率和码率;VideoRecordingConfig统一描述录制参数,便于在不同平台内部进行转换。例如在 Android 上映射到MediaRecorder的 bitrate、frameRate 等,在鸿蒙上映射到VideoRecorder配置结构。
在业务实践中,可以:
- 为不同网络和设备等级预设录制配置(如低端机限制分辨率和帧率);
- 在开始录制前统一做存储空间检查和权限检查;
- 结合音频录制模块,实现音视频同步录制或静音录制等场景。
第二部分:平台特定实现
Android 视频处理
// androidMain/kotlin/com/example/video/VideoPlayer.kt
import android.media.MediaPlayer
import android.view.SurfaceView
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.common.MediaItem
actual class VideoPlayer(private val context: Context) {
private val exoPlayer = ExoPlayer.Builder(context).build()
private val listeners = mutableListOf<VideoPlayerListener>()
actual suspend fun loadVideo(url: String): Boolean {
return try {
val mediaItem = MediaItem.fromUri(url)
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
true
} catch (e: Exception) {
listeners.forEach { it.onError(e.message ?: "Failed to load video") }
false
}
}
actual suspend fun play(): Boolean {
return try {
exoPlayer.play()
listeners.forEach { it.onStateChanged(PlayerState.PLAYING) }
true
} catch (e: Exception) {
false
}
}
actual suspend fun pause(): Boolean {
return try {
exoPlayer.pause()
listeners.forEach { it.onStateChanged(PlayerState.PAUSED) }
true
} catch (e: Exception) {
false
}
}
actual fun getDuration(): Long = exoPlayer.duration
actual fun getCurrentPosition(): Long = exoPlayer.currentPosition
actual fun addListener(listener: VideoPlayerListener) {
listeners.add(listener)
}
}
// Android 视频录制
actual class VideoRecorder(private val context: Context) {
private val mediaRecorder = MediaRecorder()
private var isRecordingFlag = false
actual suspend fun startRecording(outputPath: String): Boolean {
return try {
mediaRecorder.apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setVideoSource(MediaRecorder.VideoSource.CAMERA)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setVideoEncoder(MediaRecorder.VideoEncoder.H264)
setOutputFile(outputPath)
prepare()
start()
}
isRecordingFlag = true
true
} catch (e: Exception) {
false
}
}
actual suspend fun stopRecording(): Boolean {
return try {
mediaRecorder.stop()
mediaRecorder.release()
isRecordingFlag = false
true
} catch (e: Exception) {
false
}
}
actual fun isRecording(): Boolean = isRecordingFlag
}
Android 端的实现示例中:
- 视频播放使用
ExoPlayer,它相比系统MediaPlayer更适合流媒体场景,并支持更丰富的格式和自适应流; - 通过
listeners手动分发状态变化,确保与commonMain中定义的VideoPlayerListener对齐; - 录制部分使用
MediaRecorder,展示了最基本的音视频源配置、编码格式与输出文件路径设置流程。
在实际项目中,你可以:
- 将
ExoPlayer与 UI(如 Compose、XML、或鸿蒙跨端 UI 层)结合,提供播放视图; - 为
MediaRecorder增加更多配置(分辨率、比特率、对焦模式等),并在开始录制前检查摄像头和麦克风权限; - 使用协程对录制过程进行封装,向上提供统一的挂起接口和错误处理逻辑。
iOS 视频处理
// iosMain/kotlin/com/example/video/VideoPlayer.kt
import platform.AVFoundation.*
import platform.MediaPlayer.*
actual class VideoPlayer {
private val player = AVPlayer()
private val listeners = mutableListOf<VideoPlayerListener>()
actual suspend fun loadVideo(url: String): Boolean {
return try {
val nsUrl = NSURL(string = url)
val playerItem = AVPlayerItem(URL = nsUrl)
player.replaceCurrentItemWithPlayerItem(playerItem)
true
} catch (e: Exception) {
listeners.forEach { it.onError(e.message ?: "Failed to load video") }
false
}
}
actual suspend fun play(): Boolean {
return try {
player.play()
listeners.forEach { it.onStateChanged(PlayerState.PLAYING) }
true
} catch (e: Exception) {
false
}
}
actual fun getDuration(): Long {
val duration = player.currentItem?.duration?.value ?: 0
return (duration * 1000).toLong()
}
actual fun getCurrentPosition(): Long {
val time = player.currentTime().value
return (time * 1000).toLong()
}
}
// iOS 视频录制
actual class VideoRecorder {
private val captureSession = AVCaptureSession()
private var movieFileOutput: AVCaptureMovieFileOutput? = null
actual suspend fun startRecording(outputPath: String): Boolean {
return try {
val fileUrl = NSURL(fileURLWithPath = outputPath)
movieFileOutput?.startRecordingToOutputFileURL(fileUrl, recordingDelegate = null)
true
} catch (e: Exception) {
false
}
}
}
iOS 平台的实现与 Android 类似,同样遵循 expect/actual 的模式:
- 播放部分使用
AVPlayer,通过AVPlayerItem加载远程或本地视频资源; getDuration与getCurrentPosition将 iOS 的时间单位(通常是基于帧或时间戳)转换为毫秒,保证与公共接口一致;- 录制部分通过
AVCaptureSession与AVCaptureMovieFileOutput组合,可以扩展为更复杂的拍摄场景(前后摄像头切换、对焦、曝光控制等)。
在 KMP 设计中,这类 iOS 端实现可以封装在 iosMain,上层只关心统一的 VideoRecorder/VideoPlayer 能力,而不直接接触 AVFoundation 的复杂细节。
第三部分:编译后的代码形式
JavaScript 编译版本
// 视频播放器 (JavaScript/Web)
class VideoPlayer {
constructor(videoElement) {
this.video = videoElement;
this.listeners = [];
}
async loadVideo(url) {
try {
this.video.src = url;
this.video.addEventListener('loadedmetadata', () => {
this.notifyListeners('onStateChanged', 'LOADED');
});
return true;
} catch (error) {
this.notifyListeners('onError', error.message);
return false;
}
}
async play() {
try {
await this.video.play();
this.notifyListeners('onStateChanged', 'PLAYING');
return true;
} catch (error) {
return false;
}
}
async pause() {
this.video.pause();
this.notifyListeners('onStateChanged', 'PAUSED');
return true;
}
getDuration() {
return this.video.duration * 1000;
}
getCurrentPosition() {
return this.video.currentTime * 1000;
}
addListener(listener) {
this.listeners.push(listener);
}
notifyListeners(method, ...args) {
this.listeners.forEach(listener => {
if (listener[method]) {
listener[method](...args);
}
});
}
}
// 视频录制器
class VideoRecorder {
async startRecording(outputPath) {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
this.mediaRecorder = new MediaRecorder(stream);
this.chunks = [];
this.mediaRecorder.ondataavailable = (e) => {
this.chunks.push(e.data);
};
this.mediaRecorder.start();
return true;
} catch (error) {
return false;
}
}
async stopRecording() {
return new Promise((resolve) => {
this.mediaRecorder.onstop = () => {
const blob = new Blob(this.chunks, { type: 'video/mp4' });
// Handle blob (download, upload, etc.)
resolve(true);
};
this.mediaRecorder.stop();
});
}
}
第四部分:鸿蒙系统实际应用
鸿蒙视频处理
// ohos/kotlin/com/example/video/VideoPlayer.kt
import ohos.media.player.Player
import ohos.media.player.PlayerCallback
import ohos.app.Context
actual class VideoPlayer(private val context: Context) {
private val player = Player(context)
private val listeners = mutableListOf<VideoPlayerListener>()
actual suspend fun loadVideo(url: String): Boolean {
return try {
player.source = url
player.setPlayerCallback(object : PlayerCallback {
override fun onPlaybackComplete() {
listeners.forEach { it.onStateChanged(PlayerState.STOPPED) }
}
override fun onError(errorCode: Int, errorMsg: String) {
listeners.forEach { it.onError(errorMsg) }
}
})
true
} catch (e: Exception) {
false
}
}
actual suspend fun play(): Boolean {
return try {
player.play()
listeners.forEach { it.onStateChanged(PlayerState.PLAYING) }
true
} catch (e: Exception) {
false
}
}
actual fun getDuration(): Long = player.duration
actual fun getCurrentPosition(): Long = player.currentTime
}
// 鸿蒙视频录制
actual class VideoRecorder(private val context: Context) {
private val recorder = ohos.media.recorder.VideoRecorder()
actual suspend fun startRecording(outputPath: String): Boolean {
return try {
recorder.prepare(VideoRecordingConfig().apply {
outputPath = outputPath
videoFrameWidth = 1920
videoFrameHeight = 1080
videoFrameRate = 30
})
recorder.start()
true
} catch (e: Exception) {
false
}
}
actual suspend fun stopRecording(): Boolean {
return try {
recorder.stop()
recorder.release()
true
} catch (e: Exception) {
false
}
}
}
这一节是本篇的重点:演示如何在鸿蒙系统中实现与其他平台保持一致的视频能力。
- 播放部分使用鸿蒙的
ohos.media.player.Player,通过设置PlayerCallback监听播放完成与错误事件,并转换为通用的PlayerState和onError回调; - 录制部分使用
ohos.media.recorder.VideoRecorder,并通过配置对象设定输出路径、分辨率和帧率等参数; - 与 Android、iOS 一样,这些实现都被封装在
actual class VideoPlayer/VideoRecorder中,对上层透明。
在真实项目落地时,可以进一步:
- 抽取鸿蒙端的权限申请逻辑(相机、麦克风、存储)到一层统一的权限管理模块;
- 针对鸿蒙设备的硬件特性(如部分设备分辨率或硬编解码能力),预置不同的视频质量档位;
- 与应用的 UI 交互层打通,实现本地相册选择、拍摄视频后直接预览和上传等完整流程。
第五部分:高级功能
自适应流媒体
// commonMain/kotlin/com/example/video/AdaptiveStreaming.kt
class AdaptiveStreamingManager(private val videoPlayer: VideoPlayer) {
private var currentBitrate = 2500000
private val bitrateOptions = listOf(500000, 1000000, 2500000, 5000000)
suspend fun adjustQuality(networkSpeed: Int) {
val newBitrate = when {
networkSpeed < 1000 -> bitrateOptions[0]
networkSpeed < 3000 -> bitrateOptions[1]
networkSpeed < 5000 -> bitrateOptions[2]
else -> bitrateOptions[3]
}
if (newBitrate != currentBitrate) {
currentBitrate = newBitrate
// Switch to new quality stream
}
}
}
// 视频转码
class VideoTranscoder(private val context: Context) {
suspend fun transcode(
inputPath: String,
outputPath: String,
quality: VideoQuality
): Boolean {
return try {
val config = when (quality) {
VideoQuality.LOW -> TranscodeConfig(bitrate = 500000, fps = 24)
VideoQuality.MEDIUM -> TranscodeConfig(bitrate = 1500000, fps = 30)
VideoQuality.HIGH -> TranscodeConfig(bitrate = 5000000, fps = 30)
VideoQuality.ULTRA_HD -> TranscodeConfig(bitrate = 10000000, fps = 60)
}
// Perform transcoding
true
} catch (e: Exception) {
false
}
}
}
data class TranscodeConfig(val bitrate: Int, val fps: Int)
高级功能部分为后续扩展提供了思路:
AdaptiveStreamingManager模拟了自适应码率的逻辑,根据当前网络带宽选择不同的码率选项,可以与 HLS/DASH 等协议结合使用;VideoTranscoder展示了一个转码接口的雏形,通过不同的VideoQuality预设不同的转码参数,便于后台任务或离线处理;TranscodeConfig把关键参数打包,方便在不同平台上使用各自的转码库(如 FFmpeg、平台自带转码 API 等)。
在实际业务中,这些能力可以应用在:
- 上传前转码(例如将原始视频压缩为多码率版本);
- 后台批量处理(例如夜间批量生成预览图、转码不同清晰度);
- 与 CDN/流媒体服务联动,根据终端和网络自动选择最优播放流。

总结
视频处理与流媒体是现代应用的高级功能。通过 KMP 框架,我们可以在共享代码中定义统一的视频播放和录制接口,同时在各平台上利用原生 API 实现高效的处理。鸿蒙系统提供了完整的视频处理 API,使得跨平台实现相对平顺。关键是要合理处理网络带宽、设备性能和用户体验之间的平衡。
在工程实践中,建议你在阅读本篇示例代码时,重点思考以下几个问题:
- 公共层接口是否足够抽象、足够稳定,能否支撑未来功能的演进?
- 平台特定实现中是否有可以进一步封装、对上层隐藏的细节?
- 在引入自适应流、转码等高级能力时,如何设计任务调度与错误恢复机制?
理解了这一篇的结构后,你可以尝试:
- 按照相同的模式,为项目中其他多媒体场景(如音频、屏幕录制)设计
expect/actual架构; - 在 demo 中接入真实的视频资源与简单的 UI,打通从“选择视频 → 播放/录制 → 预览/上传”的完整链路;
- 根据实际业务指标(启动时间、卡顿率、失败率等)逐步优化和迭代视频模块的实现。
更多推荐




所有评论(0)