Skip to content

Commit

Permalink
Refine API
Browse files Browse the repository at this point in the history
  • Loading branch information
Him188 committed Dec 30, 2024
1 parent fa73b21 commit c5e8d79
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 65 deletions.
60 changes: 29 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,59 +16,49 @@ Supported targets and backends:

Platforms that are not listed above are not supported yet.

> [!NOTE]
> [!WARNING]
>
> This is a work in progress. We have a working implementation for the listed platforms
> in [Animeko,](https://github.com/open-ani/Animeko) and we are working on extracting the core media
> This is a work in progress. The following steps will not work for desktop JVMs.
>
> We have a working implementation for the listed platforms
> in [Animeko](https://github.com/open-ani/Animeko), and we are working on extracting the core media
> player logic into this separate library.
>
> We are also working on porting libmpv as a more robust and feature-rich backend than VLC.
## Installation

Check the latest
version: [![Maven Central](https://img.shields.io/maven-central/v/org.openani.mediamp/mediamp-core)](https://img.shields.io/maven-central/v/org.openani.mediamp/mediamp-core)
version: [![Maven Central](https://img.shields.io/maven-central/v/org.openani.mediamp/mediamp-api)](https://img.shields.io/maven-central/v/org.openani.mediamp/mediamp-api)

### Kotlin Multiplatform
### 1. Add Version Catalogs

> [!NOTE]
>
> This is provisional. We are still working on publishing the library, especially for the backend
In `settings.gradle.kts`, add:

```kotlin
kotlin {
val mediampVersion = "0.1.0" // Replace with the latest version
sourceSets.commonMain.dependencies {
implementation("org.openani.mediamp:mediamp-core:$mediampVersion") // for data-layer, does not depend on Compose

implementation("org.openani.mediamp:mediamp-compose:$mediampVersion") // for Compose UI
dependencyResolutionManagement {
repositories {
mavenCentral()
}
sourceSets.androidMain.dependencies {
implementation("org.openani.mediamp:mediamp-backend-exoplayer:$mediampVersion")
}
sourceSets.jvmMain.dependencies { // Desktop JVM
implementation("org.openani.mediamp:mediamp-backend-vlc:$mediampVersion")
versionCatalogs {
create("mediampLibs") {
from("org.openani.mediamp:catalog:0.0.4") // replace with the latest version
}
}
}
```

### Gradle Version Catalogs
then reload the project in the IDE.

```toml
[versions]
mediamp = "0.1.0" # Replace with the latest version
### 2. Add Dependencies

[libraries]
mediamp-core = { group = "org.openani.mediamp", module = "mediamp-core", version.ref = "mediamp" }
mediamp-compose = { group = "org.openani.mediamp", module = "mediamp-compose", version.ref = "mediamp" }
mediamp-backend-exoplayer = { group = "org.openani.mediamp", module = "mediamp-backend-exoplayer", version.ref = "mediamp" }
mediamp-backend-vlc = { group = "org.openani.mediamp", module = "mediamp-backend-vlc", version.ref = "mediamp" }
```
You will need to add the `libs.mediamp.api` to your data/domain layer,
`libs.mediamp.compose` to your UI layer, and choose a backend for each target platform:

```kotlin
kotlin {
sourceSets.commonMain.dependencies {
implementation(libs.mediamp.core) // for data-layer, does not depend on Compose
implementation(libs.mediamp.api) // for data-layer, does not depend on Compose
implementation(libs.mediamp.compose) // for Compose UI
}
sourceSets.androidMain.dependencies {
Expand All @@ -85,7 +75,15 @@ kotlin {
```kotlin
fun main() = singleWindowApplication {
val player = rememberMediampPlayer()
MediaPlayer(player, Modifier.fillMaxSize())
Column {
Button(onClick = {
player.playUrl("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WhatCarCanYouGetForAGrand.mp4")
}) {
Text("Play")
}

MediaPlayer(player, Modifier.fillMaxSize())
}
}
```

Expand Down
44 changes: 40 additions & 4 deletions mediamp-api/src/commonMain/kotlin/MediampPlayer.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
/*
* 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.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

@file:OptIn(MediampInternalApi::class)

package org.openani.mediamp

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
Expand All @@ -24,8 +27,9 @@ import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import org.openani.mediamp.features.PlaybackSpeed
import org.openani.mediamp.features.PlayerFeatures
import org.openani.mediamp.features.playerFeaturesOf
import org.openani.mediamp.features.buildPlayerFeatures
import org.openani.mediamp.internal.MediampInternalApi
import org.openani.mediamp.metadata.AudioTrack
import org.openani.mediamp.metadata.Chapter
Expand All @@ -41,6 +45,7 @@ import org.openani.mediamp.source.UriMediaSource
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.cancellation.CancellationException
import kotlin.reflect.KClass

/**
* An extensible media player that plays [MediaSource]s. Instances can be obtained from a [MediampPlayerFactory].
Expand Down Expand Up @@ -187,6 +192,11 @@ public interface MediampPlayer {
* Additional features that are supported by the underlying player implementation.
*/
public val features: PlayerFeatures

/**
* Releases all resources held by the player. The instance will be unusable after this call.
*/
public fun release()
}

/**
Expand Down Expand Up @@ -412,5 +422,31 @@ public class DummyMediampPlayer(
),
)

override val features: PlayerFeatures = playerFeaturesOf()
override val features: PlayerFeatures = buildPlayerFeatures {
add(
PlaybackSpeed,
object : PlaybackSpeed {
override val valueFlow: MutableStateFlow<Float> = MutableStateFlow(1f)
override val value: Float get() = valueFlow.value
override fun set(speed: Float) {
valueFlow.value = speed
}
},
)
}

public object Factory : MediampPlayerFactory<DummyMediampPlayer> {
override val forClass: KClass<DummyMediampPlayer> = DummyMediampPlayer::class

override fun create(context: Any, parentCoroutineContext: CoroutineContext): DummyMediampPlayer {
return DummyMediampPlayer(parentCoroutineContext)
}

@Composable
override fun Surface(mediampPlayer: DummyMediampPlayer, modifier: Modifier) {
}
}

override fun release() {
}
}
12 changes: 10 additions & 2 deletions mediamp-api/src/commonMain/kotlin/features/AudioLevelController.kt
Original file line number Diff line number Diff line change
@@ -1,9 +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.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

package org.openani.mediamp.features
Expand Down Expand Up @@ -61,3 +62,10 @@ public interface AudioLevelController : Feature {

public companion object Key : FeatureKey<AudioLevelController>
}

/**
* Sets the mute state of the audio to the opposite of the current state.
*/
public fun AudioLevelController.toggleMute() {
setMute(!isMute.value)
}
13 changes: 5 additions & 8 deletions mediamp-api/src/jvmMain/kotlin/MediampPlayerFactory.jvm.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
/*
* 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.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

package org.openani.mediamp

import org.openani.mediamp.internal.MediampInternalApi
import java.util.ServiceLoader
import kotlin.coroutines.CoroutineContext

public actual fun MediampPlayer(
context: Any,
parentCoroutineContext: CoroutineContext,
): MediampPlayer =
@OptIn(MediampInternalApi::class)
MediampPlayerFactoryLoader.first()
.create(context, parentCoroutineContext)
): MediampPlayer = MediampPlayerFactoryLoader.first()
.create(context, parentCoroutineContext)

@MediampInternalApi
public object MediampPlayerFactoryLoader {
private val factories = ServiceLoader.load(MediampPlayerFactory::class.java).toList()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +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.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

@file:kotlin.OptIn(MediampInternalApi::class)
Expand Down Expand Up @@ -386,6 +387,10 @@ class ExoPlayerMediampPlayer @UiThread constructor(
add(Buffering, buffering)
}

override fun release() {
exoPlayer.release()
}

init {
backgroundScope.launch(Dispatchers.Main) {
while (currentCoroutineContext().isActive) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +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.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

package org.openani.mediamp.backend.vlc
Expand All @@ -19,7 +20,7 @@ import androidx.compose.ui.unit.IntSize
import kotlin.math.roundToInt

@Composable
fun MediaPlayerSurfaceWithVlc(
fun VlcMediaPlayerSurface(
mediampPlayer: VlcMediampPlayer,
modifier: Modifier = Modifier,
) {
Expand Down
11 changes: 9 additions & 2 deletions mediamp-backend-vlc/src/main/kotlin/VlcMediampPlayer.kt
Original file line number Diff line number Diff line change
@@ -1,9 +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.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

@file:OptIn(MediampInternalApi::class)
Expand Down Expand Up @@ -246,6 +247,12 @@ class VlcMediampPlayer(parentCoroutineContext: CoroutineContext) : MediampPlayer
add(PlaybackSpeed.Key, playbackSpeed)
}

override fun release() {
player.submit {
player.release()
}
}

init {
// NOTE: must not call native player in a event
player.events().addMediaEventListener(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +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.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/mediamp/blob/main/LICENSE
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

package org.openani.mediamp.backend.vlc
Expand All @@ -26,6 +27,6 @@ class VlcMediampPlayerFactory : MediampPlayerFactory<VlcMediampPlayer> {
mediampPlayer: VlcMediampPlayer,
modifier: Modifier
) {
MediaPlayerSurfaceWithVlc(mediampPlayer, modifier)
VlcMediaPlayerSurface(mediampPlayer, modifier)
}
}
15 changes: 12 additions & 3 deletions mediamp-ui/src/commonMain/kotlin/ui/guesture/FastSkipState.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/*
* Copyright (C) 2024 OpenAni and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

package org.openani.mediamp.core.guesture

import androidx.compose.foundation.gestures.awaitEachGesture
Expand All @@ -13,11 +22,11 @@ import androidx.compose.ui.input.pointer.pointerInput
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.openani.mediamp.core.state.PlayerState
import org.openani.mediamp.core.state.MediampPlayer

@Composable
fun rememberPlayerFastSkipState(
playerState: PlayerState,
playerState: MediampPlayer,
gestureIndicatorState: GestureIndicatorState,
): FastSkipState {
return remember(playerState) {
Expand All @@ -26,7 +35,7 @@ fun rememberPlayerFastSkipState(
}

class PlayerFastSkipState(
private val playerState: PlayerState,
private val playerState: MediampPlayer,
private val gestureIndicatorState: GestureIndicatorState,
) {
private var originalSpeed = 0f
Expand Down
13 changes: 11 additions & 2 deletions mediamp-ui/src/commonMain/kotlin/ui/guesture/GestureLock.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
/*
* Copyright (C) 2024 OpenAni and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

package org.openani.mediamp.core.guesture

import androidx.compose.foundation.BorderStroke
Expand Down Expand Up @@ -29,7 +38,7 @@ import me.him188.ani.app.ui.foundation.theme.slightlyWeaken
import org.openani.mediamp.core.ControllerVisibility
import org.openani.mediamp.core.VideoControllerState
import org.openani.mediamp.core.progress.MediaProgressSliderState
import org.openani.mediamp.core.state.PlayerState
import org.openani.mediamp.core.state.MediampPlayer
import kotlin.time.Duration.Companion.seconds

@Composable
Expand Down Expand Up @@ -127,7 +136,7 @@ fun LockableVideoGestureHost(
progressSliderState: MediaProgressSliderState,
indicatorState: GestureIndicatorState,
fastSkipState: FastSkipState,
playerState: PlayerState,
playerState: MediampPlayer,
locked: Boolean,
enableSwipeToSeek: Boolean,
audioController: LevelController,
Expand Down
Loading

0 comments on commit c5e8d79

Please sign in to comment.