diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..c224ad5 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index f988785..0bd3ec2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index a77c7ba..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -plugins { - id 'com.android.application' -} - -android { - compileSdk 35 - buildToolsVersion '35.0.0' - - defaultConfig { - applicationId "com.mediadevkit.mdkplayer" - minSdkVersion 21 - targetSdkVersion 35 - versionCode 1 - versionName "1.0" - - //testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - jniDebuggable true - debuggable true - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 - } - dependenciesInfo { - includeInApk true - } - ndkVersion '27.2.12479018' - // Extract native libraries from the APK - packagingOptions.jniLibs.useLegacyPackaging true - namespace 'com.mediadevkit.mdkplayer' -} - -dependencies { - - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - //androidTestImplementation 'androidx.test.ext:junit:1.1.5' - //androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - // https://stackoverflow.com/questions/16682847/how-to-manually-include-external-aar-package-using-new-gradle-android-build-syst - //implementation(name:'sdk-debug', ext:'aar') - //implementation 'com.mediadevkit.sdk:sdk-debug@aar' - //implementation files('../sdk/build/outputs/aar/sdk-debug.aar') -// https://developer.android.com/studio/projects/android-library#CreateLibrary - implementation project(":sdk") -} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..246ee11 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + alias(libs.plugins.androidApplication) + alias(libs.plugins.composeCompiler) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.mediadevkit.mdkplayer" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + applicationId = "com.mediadevkit.mdkplayer" + minSdk = libs.versions.minSdk.get().toInt() + targetSdk = libs.versions.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0" + } + + buildTypes { + val release by getting { + isMinifyEnabled = true + isJniDebuggable = true + isShrinkResources = true + proguardFiles += getDefaultProguardFile("proguard-android-optimize.txt") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions.jvmTarget = "11" + composeOptions.kotlinCompilerExtensionVersion = "1.5.14" +} + +dependencies { + implementation(libs.androidx.activityCompose) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.lifecycle) + implementation(projects.sdk) + implementation(libs.bundles.compose) +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4607f6e..af5a272 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,17 +4,13 @@ + android:theme="@style/Theme.Mdk" + android:icon="@mipmap/ic_launcher"> + + android:exported="true"> diff --git a/app/src/main/assets/fonts/font.ttf b/app/src/main/assets/fonts/font.ttf new file mode 100644 index 0000000..9659afc Binary files /dev/null and b/app/src/main/assets/fonts/font.ttf differ diff --git a/app/src/main/java/com/mediadevkit/mdkplayer/MainActivity.java b/app/src/main/java/com/mediadevkit/mdkplayer/MainActivity.java deleted file mode 100644 index 18fd60e..0000000 --- a/app/src/main/java/com/mediadevkit/mdkplayer/MainActivity.java +++ /dev/null @@ -1,225 +0,0 @@ -package com.mediadevkit.mdkplayer; - -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.net.Uri; -import android.opengl.GLSurfaceView; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.util.Log; -import android.view.Display; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.SurfaceView; -import android.view.VelocityTracker; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.LinearLayout; - -import androidx.activity.result.ActivityResultCallback; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import com.mediadevkit.sdk.MDKPlayer; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; -import android.graphics.ColorSpace; -import android.widget.Switch; - -// TODO: render to SurfaceTexture surface. OnFrameAvailable will be called after swapBuffers or data copied? http://blog.csdn.net/king1425/article/details/72773331 -// TODO: request permissions(sdcard) for android 23+ -// AppCompatActivity: incompatible with android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" // https://stackoverflow.com/questions/39604889/how-to-fix-you-need-to-use-a-theme-appcompat-theme-or-descendant-with-this-a/39604946 -public class MainActivity extends AppCompatActivity { - private SurfaceView mView = null; - private GLSurfaceView mGLView = null; - private MDKPlayer mPlayer = null; - private VelocityTracker mVelocityTracker = null; - private int mState = 0; - private int mPos = 0; - private float mX = 0; - final GestureDetector gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener()); - private ActivityResultLauncher mGetContent = registerForActivityResult(new ActivityResultContracts.GetContent(), - new ActivityResultCallback() { - @Override - public void onActivityResult(Uri uri) { - // Handle the returned Uri - if (uri == null) - return; - Log.i("MDK.java", "mGetContent: " + uri.toString()); - mPlayer.setState(0); - mPlayer.setNextMedia(null); - mPlayer.setMedia(uri.toString()); - mPlayer.setState(1); - } - }); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - //getWindow().setColorMode(ActivityInfo.COLOR_MODE_HDR); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Log.i("MDK.Java","getColorMode(): " + getWindow().getColorMode()); - } - //this.requestWindowFeature(Window.FEATURE_NO_TITLE); //It's enough to remove the line - //But if you want to display full screen (without action bar) write too - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - setContentView(R.layout.activity_main); - mPlayer = new MDKPlayer(); - mView = findViewById(R.id.surfaceView); - mPlayer.setSurfaceView(mView); - findViewById(R.id.Open).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mGetContent.launch("video/*"); - } - }); - Button playStopBtn = findViewById(R.id.PlayStop); - playStopBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - playStopBtn.setText(mPlayer.state() == 0 ? "Play" : "Stop"); - mPlayer.setState(mPlayer.state() == 0 ? 1 : 0); - } - }); - Switch hdr = findViewById(R.id.HDR); - hdr.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton btn, boolean isChecked){ - if (isChecked) - mPlayer.setColorSpace(0); - else - mPlayer.setColorSpace(1); - } - }); - hdr.setChecked(true); -/* - mGLView = findViewById(R.id.glSurfaceView); - mGLView.setEGLConfigChooser(8, 8, 8, 0, 0, 0); - mGLView.setEGLContextClientVersion(2); - mGLView.setRenderer(new DemoRenderer(mPlayer)); -*/ - Intent intent = getIntent(); - String action = intent.getAction(); - if (Intent.ACTION_VIEW.equals(action)) { - mPlayer.setMedia(intent.getDataString()); - } else { - // getExternalFilesDir(Environment.DIRECTORY_MOVIES).toString() // app local - //mPlayer.setMedia(Environment.getExternalStorageDirectory().toString() + "/Movies/Samsung Chasing The Light Demo.ts"); - //mPlayer.setMedia("https://live.nodemedia.cn:8443/live/b480_265.flv"); - //mPlayer.setMedia("http://192.168.3.168:8888/86831_2158.ts"); - //mPlayer.setMedia("https://www.rmp-streaming.com/media/big-buck-bunny-720p.mp4"); - String[] urls = new String[15]; - for (int i = 0; i < 10; ++i) - urls[i] = "/sdcard/Movies/s/s0" + i + ".mkv"; - for (int i = 10; i < 15; ++i) - urls[i] = "/sdcard/Movies/s/s" + i + ".mkv"; - //mPlayer.setPlayList(urls); - } - //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - - gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener(){ - public boolean onDoubleTap(MotionEvent e) { - //Log.i("MDK.Java","onDoubleTap e.getX(): " + e.getX()); - if (mPlayer.state() == 1) - mPlayer.setState(2); - else - mPlayer.setState(1); - return true; - } - public boolean onDoubleTapEvent(@NonNull MotionEvent e) { return false;} - public boolean onSingleTapConfirmed(MotionEvent e) {return false;} - }); - } - - @Override - protected void onPause() { - super.onPause(); - //mView.setVisibility(View.GONE); - mPlayer.setState(2); - // if not set null here, surface holder will be destroyed too, but SurfaceHolder store in player class will not change when calling mPlayer.setSurfaceView(mView) in onResume() and surface is not updated in native - mPlayer.setSurfaceView(null); - } - - @Override - protected void onResume() { - super.onResume(); - //mView.setVisibility(View.VISIBLE); - mPlayer.setState(1); - mPlayer.setSurfaceView(mView); // ensure vo is created - } - - @Override - public boolean onTouchEvent(final MotionEvent event) { - gestureDetector.onTouchEvent(event); - int action = event.getActionMasked(); - switch (action) { - case MotionEvent.ACTION_DOWN: { - mX = event.getX(); - mPos = mPlayer.position(); - mState = mPlayer.state(); - if (mState == 0) - mState = 1; - //Log.i("MDK.Java","DOWN event.getX(): " + event.getX() + " mPos:" + mPos); - //mPlayer.setState(2); - if (mState == 1) - mPlayer.setState(2); - else - mPlayer.setState(1); - if (mVelocityTracker == null) - mVelocityTracker = VelocityTracker.obtain(); - mVelocityTracker.clear(); - mVelocityTracker.addMovement(event); - } - break; - case MotionEvent.ACTION_MOVE: { - mVelocityTracker.addMovement(event); - mVelocityTracker.computeCurrentVelocity(1000); - float dx = event.getX() - mX; - if (dx > 20) { // TODO: depends on duration and screen size, not velocity - mPos += 1000; - mX = event.getX(); - } else if (dx < -20) { - mPos -= 1000; - mX = event.getX(); - } - //Log.i("MDK.Java","MOVE event.getX(): " + event.getX() + " mPos:" + mPos); - if (mPos > 0) - mPlayer.seek(mPos); - //Log.i("MDK.Java","velocityTraker: "+mVelocityTracker.getXVelocity()); - } - break; - case MotionEvent.ACTION_UP: - //mPlayer.setState(mState); - //Log.i("MDK.Java","UP event.getX(): " + event.getX()); - break; - case MotionEvent.ACTION_CANCEL: - mVelocityTracker.recycle(); - break; - default: - break; - } - return true; - } - static class DemoRenderer implements GLSurfaceView.Renderer { - private final MDKPlayer mPlayer; - DemoRenderer(MDKPlayer player) { - mPlayer = player; - } - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - } - public void onSurfaceChanged(GL10 gl, int w, int h) { - mPlayer.resizeVideoSurface(w, h); - } - public void onDrawFrame(GL10 gl) { - mPlayer.renderVideo(); - } - } -} diff --git a/app/src/main/java/com/mediadevkit/mdkplayer/MainActivity.kt b/app/src/main/java/com/mediadevkit/mdkplayer/MainActivity.kt new file mode 100644 index 0000000..d32cef9 --- /dev/null +++ b/app/src/main/java/com/mediadevkit/mdkplayer/MainActivity.kt @@ -0,0 +1,56 @@ +package com.mediadevkit.mdkplayer + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.* +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import com.mediadevkit.sdk.KotlinPlayer + +@Stable +object Navigator { + var currentScreen: Screen by mutableStateOf(Screen.Root) +} + +@Immutable +sealed interface Screen { + data object Root : Screen + data class Player(val url: String, val config: KotlinPlayer.Config) : Screen +} + + +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { App() } + } + +} + +@Composable +fun App() { + MaterialTheme( + colorScheme = darkColorScheme(), + content = { + Scaffold( + content = { + BackHandler( + enabled = Navigator.currentScreen != Screen.Root, + onBack = { Navigator.currentScreen = Screen.Root }, + ) + val modifier = Modifier.padding(it) + when (val screen = Navigator.currentScreen) { + is Screen.Player -> PlayerScreen(modifier, screen.url, screen.config) + Screen.Root -> RootScreen(modifier) + } + } + ) + } + ) +} + + + diff --git a/app/src/main/java/com/mediadevkit/mdkplayer/PlayerScreen.kt b/app/src/main/java/com/mediadevkit/mdkplayer/PlayerScreen.kt new file mode 100644 index 0000000..6581c71 --- /dev/null +++ b/app/src/main/java/com/mediadevkit/mdkplayer/PlayerScreen.kt @@ -0,0 +1,323 @@ +package com.mediadevkit.mdkplayer + +import android.content.pm.ActivityInfo +import android.os.Build +import android.view.SurfaceView +import androidx.activity.compose.LocalActivity +import androidx.compose.foundation.* +import androidx.compose.foundation.interaction.* +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.* +import androidx.lifecycle.compose.LocalLifecycleOwner +import com.mediadevkit.sdk.* +import kotlinx.coroutines.* +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +@Composable +fun PlayerScreen( + modifier: Modifier = Modifier, + url: String, + config: KotlinPlayer.Config, +) { + val activity = LocalActivity.current + val player = remember { KotlinPlayer(config) } + val state = rememberPlayerState(player) + var userValue by remember { mutableFloatStateOf(0f) } + val sliderSource = remember(::MutableInteractionSource) + val isSliding by sliderSource.collectIsDraggedAsState() + var type by remember { mutableStateOf(MediaType.Unknown) } + val lifecycleOwner = LocalLifecycleOwner.current + + val sliderValue by remember { + derivedStateOf { + when { + isSliding -> userValue + else -> state.progress + } + } + } + + LaunchedEffect( + key1 = isSliding, + block = { + player.state = if (isSliding) 2 else 1 + } + ) + LaunchedEffect( + key1 = userValue, + block = { player.position = (state.duration * userValue.toDouble()).inWholeMilliseconds } + ) + DisposableEffect( + key1 = Unit, + effect = { + player.state = 0 + player.media = url + player.state = 1 + val listener = LifecycleEventObserver { _, event -> + if (event.targetState == Lifecycle.State.CREATED) player.state = 2 + } + lifecycleOwner.lifecycle.addObserver(listener) + onDispose { + lifecycleOwner.lifecycle.removeObserver(listener) + player.release() + } + } + ) + + TracksDialog( + type = type, + onClick = { index, stream -> + val key = when (stream) { + is MdkAudioStream -> "audio.tracks" + is MdkSubtitle -> "subtitle.tracks" + is MdkVideoStream -> "video.tracks" + } + player.setProperty(key, "-1") //fixme: if not set to -1 before, tracks are combined !! + player.setProperty(key, "$index") + when (stream) { + is MdkAudioStream -> state.audioTrack = index + is MdkSubtitle -> state.subtitleTrack = index + is MdkVideoStream -> state.videoTrack = index + } + type = MediaType.Unknown + }, + onDismiss = { type = MediaType.Unknown }, + state = state, + ) + + Column( + modifier = modifier.fillMaxSize(), + content = { + AndroidView( + modifier = Modifier.weight(1f), + factory = { SurfaceView(it).apply(player::setSurface) }, + onRelease = { player.setSurface(null) }, + ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + content = { + IconButton( + onClick = { + when (state.state) { + 1 -> player.state = 2 + else -> player.state = 1 + } + }, + content = { + Icon( + imageVector = when (state.state) { + 1 -> Icons.Default.Pause + 2 -> Icons.Default.PlayArrow + else -> Icons.Default.PlayArrow + }, + contentDescription = null + ) + } + ) + Spacer( + modifier = Modifier.width(12.dp), + ) + Slider( + modifier = modifier.weight(1f), + interactionSource = sliderSource, + value = sliderValue, + onValueChange = { userValue = it }, + ) + } + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp, alignment = Alignment.CenterHorizontally), + content = { + IconButton( + onClick = { + state.hdr = !state.hdr + player.hdr = state.hdr + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + activity?.window?.colorMode = when { + state.hdr -> ActivityInfo.COLOR_MODE_HDR + else -> ActivityInfo.COLOR_MODE_DEFAULT + } + } + }, + content = { + Icon( + imageVector = when { + state.hdr -> Icons.Default.HdrOn + else -> Icons.Default.HdrOff + }, + contentDescription = null, + ) + } + ) + IconButton( + enabled = state.mediaInfo?.audio?.isNotEmpty() == true, + onClick = { type = MediaType.Audio }, + content = { Icon(Icons.Default.Audiotrack, null) } + ) + IconButton( + enabled = state.mediaInfo?.subtitles?.isNotEmpty() == true, + onClick = { type = MediaType.Subtitles }, + content = { Icon(Icons.Default.ClosedCaption, null) } + ) + } + ) + }, + ) + +} + +@Stable +class PlayerState { + var hdr: Boolean by mutableStateOf(false) + var position: Duration by mutableStateOf(Duration.ZERO) + var state: Int by mutableIntStateOf(0) + var mediaInfo: MdkMediaInfo? by mutableStateOf(null) + val duration: Duration by derivedStateOf { mediaInfo?.duration?.milliseconds ?: Duration.ZERO } + + val progress: Float by derivedStateOf { + when { + duration <= Duration.ZERO -> 0f + else -> (position / duration).toFloat() + } + } + + var audioTrack: Int by mutableIntStateOf(-1) + var videoTrack: Int by mutableIntStateOf(-1) + var subtitleTrack: Int by mutableIntStateOf(-1) + +} + +@Stable +@Composable +fun rememberPlayerState(player: KotlinPlayer): PlayerState { + val state = remember(::PlayerState) + LaunchedEffect( + key1 = Unit, + block = { + withContext(Dispatchers.IO) { + while (isActive) { + try { + withContext(Dispatchers.Main) { + state.position = player.position.milliseconds + } + } finally { + delay(100) + } + } + } + }, + ) + DisposableEffect( + key1 = Unit, + effect = { + val listener = object : KotlinPlayer.Listener { + + override fun onMediaStatus(previous: MediaStatus, next: MediaStatus) { + if (state.mediaInfo == null && next.isPrepared) { + state.mediaInfo = player.mediaInfo + state.audioTrack = if (state.mediaInfo?.audio.isNullOrEmpty()) -1 else 0 + state.subtitleTrack = if (state.mediaInfo?.subtitles.isNullOrEmpty()) -1 else 0 + state.videoTrack = if (state.mediaInfo?.video.isNullOrEmpty()) -1 else 0 + } + } + + override fun onState(newValue: Int) { + state.state = newValue + } + } + player.listeners += listener + onDispose { player.listeners -= listener } + } + ) + return state +} + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TracksDialog( + state: PlayerState, + type: MediaType, + onDismiss: () -> Unit, + onClick: (index: Int, stream: MdkStream) -> Unit, +) { + val isVisible by rememberUpdatedState(type != MediaType.Unknown) + val tracks by remember(type) { + derivedStateOf { + when (type) { + MediaType.Unknown -> emptyList() + MediaType.Audio -> state.mediaInfo?.audio.orEmpty() + MediaType.Video -> state.mediaInfo?.video.orEmpty() + MediaType.Subtitles -> state.mediaInfo?.subtitles.orEmpty() + } + } + } + if (isVisible) { + BasicAlertDialog( + onDismissRequest = onDismiss, + content = { + Surface( + modifier = Modifier.fillMaxWidth() + .fillMaxHeight(.5f), + shape = MaterialTheme.shapes.medium, + content = { + Column( + modifier = Modifier.fillMaxSize() + .verticalScroll( + state = rememberScrollState() + ), + content = { + for (index in tracks.indices) { + val track = tracks[index] + val isSelected by remember { + derivedStateOf { + when (track) { + is MdkAudioStream -> index == state.audioTrack + is MdkSubtitle -> index == state.subtitleTrack + is MdkVideoStream -> index == state.videoTrack + } + } + } + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + onClick.invoke(index, track) + } + .padding(16.dp), + content = { + Text( + modifier = Modifier.weight(1f), + text = "Track ${track.index}" + ) + if (isSelected) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = null, + ) + } + } + ) + } + } + ) + } + ) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mediadevkit/mdkplayer/RootScreen.kt b/app/src/main/java/com/mediadevkit/mdkplayer/RootScreen.kt new file mode 100644 index 0000000..a6c4de5 --- /dev/null +++ b/app/src/main/java/com/mediadevkit/mdkplayer/RootScreen.kt @@ -0,0 +1,110 @@ +package com.mediadevkit.mdkplayer + +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.* +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.unit.dp +import com.mediadevkit.sdk.KotlinPlayer + +var decodeToSurfaceView by mutableStateOf(false) + +@Composable +fun RootScreen( + modifier: Modifier = Modifier, +) { + val (getUrl, setUrl) = remember { mutableStateOf(defaultUrl) } + + val intentLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetContent(), + onResult = { uri -> + if (uri == null) return@rememberLauncherForActivityResult + Navigator.currentScreen = Screen.Player( + url = uri.toString(), + config = KotlinPlayer.Config(decodeToSurfaceView), + ) + } + ) + Column( + modifier = modifier + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(20.dp, alignment = Alignment.CenterVertically), + content = { + Row( + modifier = Modifier, + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + content = { + Text("Decode to SurfaceView") + Switch( + checked = decodeToSurfaceView, + onCheckedChange = { decodeToSurfaceView = it }, + ) + } + ) + Button( + onClick = { intentLauncher.launch("video/*") }, + content = { Text("Open file") } + ) + HorizontalDivider() + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + content = { + OutlinedTextField( + modifier = Modifier.weight(1f), + value = getUrl, + onValueChange = setUrl, + label = { Text("Url") }, + singleLine = true, + ) + Button( + shape = MaterialTheme.shapes.small, + onClick = { + if ( getUrl.isBlank() ) return@Button + Navigator.currentScreen = Screen.Player( + url = getUrl, + config = KotlinPlayer.Config(decodeToSurfaceView) + ) + }, + content = { Text("Go") } + ) + } + ) + + + }, + ) +} + + + + + +//dv profile 5 fhd +//BL_RPU_dvhe-05_1920x1080@24fps_0_6313.mp4 + +//dv profile 5 uhd +//BL_RPU_dvhe-05_3840x2160@24fps_0_6313.mp4 + +//dv profile 8.4 fhd +//BL_RPU_dvhe-08-84_1920x1080@24fps_0_6313.mp4 + +//dv profile 8.4 uhd +//BL_RPU_dvhe-08-84_3840x2160@24fps_0_6313.mp4 + +//dv profile 8.1 fhd +//BL_RPU_dvhe-08-mapDynamic1000-81_1920x1080@24fps_0_6313.mp4 + +//dv profile 8.1 uhd +//BL_RPU_dvhe-08-mapDynamic1000-81_3840x2160@24fps_0_6313.mp4 + +val baseUrl = "https://media.githubusercontent.com/media/DolbyLaboratories/dolby-vision-contents/refs/heads/main/SolLevante_Netflix/{FILE}?download=true" +//val defaultUrl = baseUrl.replace("{FILE}", "BL_RPU_dvhe-08-mapDynamic1000-81_1920x1080@24fps_0_6313.mp4") +val defaultUrl = "https://github.com/ietf-wg-cellar/matroska-test-files/raw/master/test_files/test5.mkv" \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..869a948 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 448a9a4..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - -