Skip to content

Commit

Permalink
Apply new playback state to AbstractMediampPlayer (#3)
Browse files Browse the repository at this point in the history
* apply new playback state to `AbstractMediampPlayer`

* enforce playback control methods call on main thread

* make some API to async semantics

* ensure `setMediaData` thread-safe

* update doc

* Update mediamp-api/src/commonMain/kotlin/AbstractMediampPlayer.kt

Co-authored-by: Him188 <[email protected]>

* Update mediamp-api/src/commonMain/kotlin/MediampPlayer.kt

Co-authored-by: Him188 <[email protected]>

* Update mediamp-api/src/commonMain/kotlin/MediampPlayer.kt

Co-authored-by: Him188 <[email protected]>

* Update mediamp-api/src/commonMain/kotlin/MediampPlayer.kt

Co-authored-by: Him188 <[email protected]>

* Update mediamp-api/src/commonMain/kotlin/MediampPlayer.kt

Co-authored-by: Him188 <[email protected]>

* optimize doc

* adapt ExoPlayer and VLC player

* adapt mpv

* abstract player test

* add api test and dummy player module

* fix test

* Fix junit engine

* disable explicit api in mediamp-api-test

* Remove explicit `public` from AbstractMediampPlayerTest

* rename dummy to test

---------

Co-authored-by: Him188 <[email protected]>
  • Loading branch information
StageGuard and Him188 authored Jan 16, 2025
1 parent b68a7a4 commit 4dec7ac
Show file tree
Hide file tree
Showing 14 changed files with 1,067 additions and 565 deletions.
50 changes: 50 additions & 0 deletions mediamp-api-test/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2024 OpenAni and contributors.
*
* Use of this source code is governed by the GNU GENERAL PUBLIC LICENSE version 3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
*/

plugins {
kotlin("multiplatform")
id("com.android.library")

`mpp-lib-targets`
kotlin("plugin.serialization")
}

description = "Test suite for MediaMP Core API"

android {
namespace = "org.openani.mediamp.api.test"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

kotlin {
jvmToolchain(8)
sourceSets {
commonMain.dependencies {
implementation(libs.kotlinx.io.core) // TODO: 2024/12/16 remove
compileOnly(libs.androidx.annotation)
api(libs.kotlinx.coroutines.core)

implementation(projects.mediampApi)

api(kotlin("test-annotations-common", libs.versions.kotlin.get()))
api(libs.kotlinx.coroutines.test)
}
sourceSets["jvmMain"].dependencies {
api(kotlin("test-junit", libs.versions.kotlin.get()))
}
iosMain.dependencies {
implementation(libs.androidx.annotation)
}
androidMain.dependencies {
api(libs.androidx.annotation)
}
}
}
184 changes: 184 additions & 0 deletions mediamp-api-test/src/commonMain/kotlin/AbstractMediampPlayerTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (C) 2024 OpenAni and contributors.
*
* Use of this source code is governed by the GNU GENERAL PUBLIC LICENSE version 3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
*/

@file:Suppress("FunctionName")

package org.openani.mediamp.api.test

import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.runTest
import org.openani.mediamp.MediampPlayer
import org.openani.mediamp.PlaybackState
import org.openani.mediamp.source.MediaExtraFiles
import org.openani.mediamp.source.UriMediaData
import org.openani.mediamp.togglePause
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

abstract class AbstractMediampPlayerTest {
abstract fun createMediampPlayer(): MediampPlayer

@Test
fun `initial state`(): TestResult = runTest {
val player = createMediampPlayer()
// Initially, the player should be in CREATED state
assertEquals(PlaybackState.CREATED, player.playbackState.value)

// No media data yet
val mediaData = player.mediaData.first()
assertNull(mediaData)
}

@Test
fun `set media`(): TestResult = runTest {
val player = createMediampPlayer()
// Use UriMediaData for demonstration; this requires no real file
val data = UriMediaData(
uri = "file:///fake_video.mp4",
headers = emptyMap(),
extraFiles = MediaExtraFiles()
)

// Setting the media data should transition the state to READY
player.setMediaData(data)
assertEquals(PlaybackState.READY, player.playbackState.value)

// mediaData flow should now emit the new MediaData
val currentMediaData = player.mediaData.first()
assertNotNull(currentMediaData)
// Because we used UriMediaData, we can check if it's the same instance
assertEquals(data, currentMediaData)
}

@Test
fun `resume and pause`(): TestResult = runTest {
val player = createMediampPlayer()
val data = UriMediaData(
uri = "file:///fake_video.mp4",
headers = emptyMap(),
extraFiles = MediaExtraFiles()
)

// Setup media data -> READY
player.setMediaData(data)
assertEquals(PlaybackState.READY, player.playbackState.value)

// Resume -> PLAYING
player.resume()
assertEquals(PlaybackState.PLAYING, player.playbackState.value)

// Pause -> PAUSED
player.pause()
assertEquals(PlaybackState.PAUSED, player.playbackState.value)
}

@Test
fun `stop playback`(): TestResult = runTest {
val player = createMediampPlayer()
val data = UriMediaData(
uri = "file:///fake_video.mp4",
headers = emptyMap(),
extraFiles = MediaExtraFiles()
)

player.setMediaData(data)
assertEquals(PlaybackState.READY, player.playbackState.value)

// Resume -> PLAYING
player.resume()
assertEquals(PlaybackState.PLAYING, player.playbackState.value)

// Stop playback -> FINISHED
player.stopPlayback()
assertEquals(PlaybackState.FINISHED, player.playbackState.value)

// mediaData flow should now emit `null` because we've released the resource
val mediaDataAfterStop = player.mediaData.first()
assertNull(mediaDataAfterStop)
}

@Test
fun `close player`(): TestResult = runTest {
val player = createMediampPlayer()
val data = UriMediaData(
uri = "file:///fake_video.mp4",
headers = emptyMap(),
extraFiles = MediaExtraFiles()
)

// Go to READY -> PLAYING
player.setMediaData(data)
player.resume()
assertEquals(PlaybackState.PLAYING, player.playbackState.value)

// Close -> DESTROYED
player.close()
assertEquals(PlaybackState.DESTROYED, player.playbackState.value)

// Further calls should have no effect, e.g. calling resume or stopPlayback won't change the state
player.resume()
assertEquals(PlaybackState.DESTROYED, player.playbackState.value)
player.stopPlayback()
assertEquals(PlaybackState.DESTROYED, player.playbackState.value)
}

@Test
fun `seek and skip`(): TestResult = runTest {
val player = createMediampPlayer()
val data = UriMediaData(
uri = "file:///fake_video.mp4",
headers = emptyMap(),
extraFiles = MediaExtraFiles()
)

player.setMediaData(data)
player.resume()

// Check initial dummy position
assertEquals(10000L, player.currentPositionMillis.value)

// Seek to 20s
player.seekTo(20000L)
assertEquals(20000L, player.currentPositionMillis.value)

// Skip +5s => 25s
player.skip(5000L)
assertEquals(25000L, player.currentPositionMillis.value)

// Skip -10s => 15s
player.skip(-10000L)
assertEquals(15000L, player.currentPositionMillis.value)
}

@Test
fun `toggle pause`(): TestResult = runTest {
val player = createMediampPlayer()
player.setMediaData(
UriMediaData(
uri = "file:///fake_video.mp4",
headers = emptyMap(),
extraFiles = MediaExtraFiles()
)
)

// READY -> PLAY
player.resume()
assertEquals(PlaybackState.PLAYING, player.playbackState.value)

// togglePause -> PAUSE
player.togglePause()
assertEquals(PlaybackState.PAUSED, player.playbackState.value)

// togglePause -> PLAYING
player.togglePause()
assertEquals(PlaybackState.PLAYING, player.playbackState.value)
}
}
10 changes: 10 additions & 0 deletions mediamp-api-test/src/commonMain/kotlin/package.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright (C) 2024 OpenAni and contributors.
*
* Use of this source code is governed by the GNU GENERAL PUBLIC LICENSE version 3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
*/

package org.openani.mediamp.api.test

Loading

0 comments on commit 4dec7ac

Please sign in to comment.