Kotlin Multiplatform (KMP) enables developers to write a single codebase that can be shared across multiple platforms such as iOS, Android, and web. For video applications, KMP allows developers to implement common business logic for video streaming, playback, and metadata handling while still utilizing platform-specific components for UI and custom functionalities.

This reduces code duplication, increases maintainability, and speeds up development by allowing the use of shared logic across platforms without sacrificing native performance or flexibility.

Setting Up Kotlin Multiplatform

To start using Kotlin Multiplatform, first define a shared module, which will contain the core logic related to video processing, API interactions, and streaming. This shared module is then integrated into platform-specific modules for tasks like video playback, UI handling, and other native features.

Example: Shared Video Streaming Logic Using KMP

code
// Shared Module: VideoStreamingLogic.kt
expect class VideoPlayer {
fun play(url: String)
fun pause()
fun stop()
}

// Android Implementation
actual class VideoPlayer actual constructor() {
actual fun play(url: String) {
// Android-specific video playback code
}

actual fun pause() {
// Android-specific pause code
}

actual fun stop() {
// Android-specific stop code
}
}

// iOS Implementation
actual class VideoPlayer actual constructor() {
actual fun play(url: String) {
// iOS-specific video playback code
}

actual fun pause() {
// iOS-specific pause code
}

actual fun stop() {
// iOS-specific stop code
}
}

Explanation:

  • expect and actual: These keywords are used to define platform-specific implementations for common functionality. The shared code contains the expect declaration, while platform-specific code contains the actual implementation for Android and iOS.
  • VideoPlayer: The class provides the interface for video playback logic that can be reused across both platforms.
Live  stream

Using Shared Code for Video Playback and Streaming

With Kotlin Multiplatform, developers can share logic for interacting with video streaming services, managing video quality, buffering, and metadata handling. For example, you could write a shared function to fetch video details (such as title, description, and streaming URL) from an API and then use platform-specific components to handle the video playback.

Example: Shared Logic for Fetching Video Metadata

code
// Shared Module: VideoMetadataFetcher.kt
interface VideoMetadataFetcher {
fun fetchMetadata(videoId: String): VideoMetadata
}

// Android Implementation
actual class VideoMetadataFetcherImpl actual constructor() : VideoMetadataFetcher {
actual fun fetchMetadata(videoId: String): VideoMetadata {
// Android-specific API call for video metadata
}
}

// iOS Implementation
actual class VideoMetadataFetcherImpl actual constructor() : VideoMetadataFetcher {
actual fun fetchMetadata(videoId: String): VideoMetadata {
// iOS-specific API call for video metadata
}
}

Explanation:

  • Defines a platform-agnostic contract for fetching video metadata. This interface allows shared code to request metadata without depending on platform-specific details.
  • Provides the Android-specific behavior for the fetchMetadata() function. This implementation interacts with Android APIs to retrieve metadata for a given video.
  • Supplies the iOS-specific logic for the fetchMetadata() method. This version uses iOS-native APIs to obtain metadata based on the video ID.

Unit Testing Shared Video Logic in Kotlin Multiplatform

Testing is crucial to ensure the correctness of shared business logic before it integrates with platform-specific components. Kotlin Multiplatform allows writing unit tests in the shared module using common testing libraries like Kotlin Test or Multiplatform Test.

This helps validate shared components such as metadata parsing, URL validation, or business rules around video playback without requiring a full device/emulator environment.

Example: Testing Shared Metadata Parser Logic

code
// Shared Module: VideoMetadataParser.kt
data class VideoMetadata(val title: String, val description: String, val url: String)

class VideoMetadataParser {
fun parse(rawData: String): VideoMetadata {
// Simulated JSON parsing logic
val parts = rawData.split("|")
return VideoMetadata(parts[0], parts[1], parts[2])
}
}

code
// Shared Test: VideoMetadataParserTest.kt
import kotlin.test.*

class VideoMetadataParserTest {

@Test
fun testParseMetadata() {
val parser = VideoMetadataParser()
val raw = "Sample Title|A test description|https://example.com/video.mp4"
val metadata = parser.parse(raw)

assertEquals("Sample Title", metadata.title)
assertEquals("A test description", metadata.description)
assertEquals("https://example.com/video.mp4", metadata.url)
}
}

Explanation:

  • VideoMetadataParser: A shared utility that interprets raw metadata (e.g., from an API or local source) into a structured format.
  • Unit test: Validates parsing logic using kotlin.test, ensuring that shared parsing logic behaves consistently across all platforms. This kind of testing is fast and helps catch issues early in development.

Platform-Specific Integration for Video Playback

While Kotlin Multiplatform enables the sharing of business logic, platform-specific controls are essential for handling video playback, as each platform uses different media components. For example, Android typically uses ExoPlayer, while iOS uses AVPlayer. Kotlin Multiplatform allows you to implement these platform-specific components within the shared codebase.

Example: Platform-Specific Video Playback Implementation

code
// Shared VideoPlayer Interface
expect interface VideoPlayer {
fun play(url: String)
fun pause()
fun stop()
}

// Android Implementation with ExoPlayer
actual class VideoPlayerImpl actual constructor() : VideoPlayer {
private val player = ExoPlayer.Builder(context).build()

actual fun play(url: String) {
val mediaItem = MediaItem.fromUri(url)
player.setMediaItem(mediaItem)
player.prepare()
player.play()
}

actual fun pause() {
player.pause()
}

actual fun stop() {
player.stop()
}
}

// iOS Implementation with AVPlayer
actual class VideoPlayerImpl actual constructor() : VideoPlayer {
private val player = AVPlayer()

actual fun play(url: String) {
val playerItem = AVPlayerItem(url = URL(string = url))
player.replaceCurrentItem(with: playerItem)
player.play()
}

actual fun pause() {
player.pause()
}

actual fun stop() {
player.replaceCurrentItem(with: nil)
}
}

Explanation:

  • The VideoPlayer interface defines common playback methods (play, pause, and stop), while the platform-specific implementations handle the actual playback logic for Android (ExoPlayer) and iOS (AVPlayer).
  • ExoPlayer (Android) and AVPlayer (iOS) are used in their respective platforms for video streaming and playback.

Video Streaming Protocol Support: HLS and DASH

Kotlin Multiplatform can also help developers integrate adaptive bitrate streaming protocols like HLS (HTTP Live Streaming) and DASH (Dynamic Adaptive Streaming over HTTP). These protocols allow video players to adjust the video quality based on network conditions, ensuring smooth playback even on fluctuating connections.

Example: Configuring HLS Streaming for Video Playback

code
// Shared VideoPlayer Interface (Continued)
expect interface VideoPlayer {
fun playHLS(url: String)
}

// Android Implementation with ExoPlayer for HLS
actual class VideoPlayerImpl actual constructor() : VideoPlayer {
private val player = ExoPlayer.Builder(context).build()

actual fun playHLS(url: String) {
val mediaItem = MediaItem.fromUri(url)
player.setMediaItem(mediaItem)
player.prepare()
player.play()
}
}

// iOS Implementation with AVPlayer for HLS
actual class VideoPlayerImpl actual constructor() : VideoPlayer {
private val player = AVPlayer()

actual fun playHLS(url: String) {
val playerItem = AVPlayerItem(url = URL(string = url))
player.replaceCurrentItem(with: playerItem)
player.play()
}
}

Explanation:

  • playHLS: A shared function that allows both Android and iOS platforms to handle HLS streams.
  • ExoPlayer (Android) and AVPlayer (iOS) support HLS out of the box, allowing seamless streaming based on available bandwidth.