Skip to content

Commit

Permalink
feat: cache chunk in disk and preload next music
Browse files Browse the repository at this point in the history
  • Loading branch information
hpp2334 committed Nov 27, 2024
1 parent 96f6390 commit 1a44b3f
Show file tree
Hide file tree
Showing 17 changed files with 651 additions and 264 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ enum class EaseIconButtonType {
}

data class EaseIconButtonColors(
val buttonBg: Color,
val iconTint: Color,
val buttonBg: Color? = null,
val buttonDisabledBg: Color? = null,
val iconTint: Color? = null,
)

@Composable
Expand All @@ -73,7 +74,7 @@ fun EaseIconButton(
if (!isVariant) {
Color.Transparent
} else {
MaterialTheme.colorScheme.surfaceVariant
overrideColors?.buttonDisabledBg ?: MaterialTheme.colorScheme.surfaceVariant
}
} else {
overrideColors?.buttonBg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import uniffi.ease_client_android.IAssetLoadDelegateForeign
import uniffi.ease_client_android.IPlayerDelegateForeign
import uniffi.ease_client_android.apiBackendPlayNext
import uniffi.ease_client_android.apiBackendPlayPrevious
import uniffi.ease_client_android.apiCloseAsset
import uniffi.ease_client_android.apiOpenAsset
import uniffi.ease_client_android.apiPollAsset
import uniffi.ease_client_backend.AssetChunkData
import uniffi.ease_client_backend.AssetChunkRead
import uniffi.ease_client_backend.AssetLoadStatus
import uniffi.ease_client_backend.MusicToPlay
import uniffi.ease_client_shared.DataSourceKey
Expand Down Expand Up @@ -108,12 +110,35 @@ private class EaseMusicDataSource : BaseDataSource(true) {

private var _currentDataSpec: DataSpec? = null

private var _sessionId: ULong = 0UL
private var _currentStatus: AssetLoadStatus = AssetLoadStatus.Pending
private val _byteQueue = ByteQueue()

fun poll() {
val result = apiPollAsset(this._handle!!)
when (result) {
is AssetChunkRead.Chunk -> {
when (result.v1) {
is AssetChunkData.Buffer -> {
_byteQueue.put(result.v1.v1)
}
is AssetChunkData.Status -> {
this._currentStatus = result.v1.v1
}
}
}
AssetChunkRead.None -> {}
AssetChunkRead.NotOpen -> {
this._currentStatus = AssetLoadStatus.NotFound
}
}
}

override fun read(buffer: ByteArray, offset: Int, length: Int): Int {
try {
if (_byteQueue.isEmpty() && this._handle != null) {
poll()
}

val status = this._currentStatus
if (status is AssetLoadStatus.NotFound) {
throw IOException("Not Found")
Expand Down Expand Up @@ -152,23 +177,8 @@ private class EaseMusicDataSource : BaseDataSource(true) {

_currentDataSpec = dataSpec

val sessionId = ++_sessionId

try {
val self = this
this._handle = apiOpenAsset(key, dataSpec.position.toULong(), object : IAssetLoadDelegateForeign {
override fun onStatus(status: AssetLoadStatus) {
if (sessionId == _sessionId) {
self._currentStatus = status
}
}

override fun onChunk(chunk: ByteArray) {
if (sessionId == _sessionId) {
self._byteQueue.put(chunk)
}
}
})
this._handle = apiOpenAsset(key, dataSpec.position.toULong())
return C.LENGTH_UNSET.toLong()
} catch (e: Exception) {
println(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,11 @@ private fun MusicPanel(
buttonType = EaseIconButtonType.Primary,
painter = painterResource(id = R.drawable.icon_play),
disabled = state.loading,
overrideColors = if (state.loading) {
EaseIconButtonColors(
buttonDisabledBg = MaterialTheme.colorScheme.secondary,
)
} else { null },
onClick = {
bridge.dispatchClick(MusicControlWidget.PLAY)
}
Expand Down
76 changes: 36 additions & 40 deletions rust-libs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 16 additions & 15 deletions rust-libs/ease-client-android/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
};

use ease_client::{build_client, Action, ViewAction};
use ease_client_backend::Backend;
use ease_client_backend::{AssetChunkRead, Backend};
use ease_client_shared::backends::{
app::ArgInitializeApp, encode_message_payload, generated::Code, player::PlayerDelegateEvent,
storage::DataSourceKey, MessagePayload,
Expand All @@ -16,10 +16,10 @@ use tracing::subscriber::set_global_default;

use crate::{
foreigns::{
AssetLoadDelegate, IAssetLoadDelegateForeign, IAsyncAdapterForeign,
IPermissionServiceForeign, IPlayerDelegateForeign, IRouterServiceForeign,
IToastServiceForeign, IViewStateServiceForeign, PermissionServiceDelegate, PlayerDelegate,
RouterServiceDelegate, ToastServiceDelegate, ViewStateServiceDelegate,
IAsyncAdapterForeign, IPermissionServiceForeign, IPlayerDelegateForeign,
IRouterServiceForeign, IToastServiceForeign, IViewStateServiceForeign,
PermissionServiceDelegate, PlayerDelegate, RouterServiceDelegate, ToastServiceDelegate,
ViewStateServiceDelegate,
},
inst::{BACKEND, CLIENTS, RT},
};
Expand Down Expand Up @@ -190,7 +190,7 @@ pub fn api_flush_backend_spawned_local() {
pub async fn api_load_asset(key: DataSourceKey) -> Option<Vec<u8>> {
RT.spawn(async move {
if let Some(backend) = BACKEND.try_backend() {
let file = backend.asset_server().load(key, 0).await;
let file = backend.asset_loader().load(key, 0).await;
if let Ok(Some(file)) = file {
return file.bytes().await.ok().map(|v| v.to_vec());
}
Expand All @@ -202,24 +202,25 @@ pub async fn api_load_asset(key: DataSourceKey) -> Option<Vec<u8>> {
}

#[uniffi::export]
pub fn api_open_asset(
key: DataSourceKey,
offset: u64,
listener: Arc<dyn IAssetLoadDelegateForeign>,
) -> u64 {
pub fn api_open_asset(key: DataSourceKey, offset: u64) -> u64 {
let _guard = RT.enter();
let backend = BACKEND.backend();
backend.asset_loader().open(key, offset)
}

#[uniffi::export]
pub fn api_poll_asset(handle: u64) -> AssetChunkRead {
let _guard = RT.enter();
let backend = BACKEND.backend();
backend
.asset_server()
.open(key, offset as usize, AssetLoadDelegate::new(listener))
backend.asset_loader().poll(handle)
}

#[uniffi::export]
pub fn api_close_asset(id: u64) {
let _guard = RT.enter();
let backend = BACKEND.try_backend();
if let Some(backend) = backend {
backend.asset_server().close(id);
backend.asset_loader().close(id);
}
}

Expand Down
7 changes: 1 addition & 6 deletions rust-libs/ease-client-android/src/foreigns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ease_client::{
IPermissionService, IRouterService, IToastService, IViewStateService, RootViewModelState,
RoutesKey,
};
use ease_client_backend::{AssetLoadStatus, IAssetLoadDelegate, IPlayerDelegate, MusicToPlay};
use ease_client_backend::{IPlayerDelegate, MusicToPlay};
use ease_client_shared::backends::{music::MusicId, player::PlayerDurations};

macro_rules! generate_delegate {
Expand Down Expand Up @@ -60,11 +60,6 @@ generate_delegate!(PlayerDelegate, IPlayerDelegate, IPlayerDelegateForeign, {
request_total_duration(id: MusicId);
});

generate_delegate!(AssetLoadDelegate, IAssetLoadDelegate, IAssetLoadDelegateForeign, {
on_status(status: AssetLoadStatus);
on_chunk(chunk: Vec<u8>);
});

#[uniffi::export(with_foreign)]
pub trait IAsyncAdapterForeign: Send + Sync + 'static {
fn on_spawn_locals(&self, app_id: Option<u64>);
Expand Down
6 changes: 3 additions & 3 deletions rust-libs/ease-client-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ ease-client-shared = { path = "../ease-client-shared" }
once_cell = "1.18.0"
serde = { version = "1.0", features = ["derive"] }
tracing = "0.1"
tokio-util = { version = "0.7.8", features = ["io"] }
num-traits = "0.2"
num-derive = "0.2"
bytes = "1.5.0"
Expand All @@ -25,9 +24,10 @@ getset = "0.1.3"
serde_json = "1.0"
serde_bytes = "0.11.14"
futures = "0.3.30"
id3 = "1.8.0"
lofty = "0.18.0"
nom = "7"
unicode-segmentation = "1.10.1"
base64 = "0.22.1"
uniffi = "=0.28.3"
redb = "2.2.0"
lru = "0.12.5"
rmp-serde = "1.3.0"
Loading

0 comments on commit 1a44b3f

Please sign in to comment.