Working with playlist on the ExoPlayer, I managed the setting of the playlist by following the documentation.
val firstSource = ProgressiveMediaSource.Factory(...).createMediaSource(firstVideoUri);
val secondSource = ProgressiveMediaSource.Factory(...).createMediaSource(secondVideoUri);
val playlist = ConcatenatingMediaSource(firstSource, secondSource);
player.prepare(playlist)
But I noticed that the transition between video is instantaneous. However, I would like to add a waiting stage between each video, exactly like youtube does (let's say 5 sec). Something like:
Is it already handled by the Exoplayer itself or not? If not what is a clean way to solve the problem ?
You can use following code.
object : CountDownTimer(1500, 500) {
override fun onTick(millisUntilFinished: Long) {
//Update your Ui.
}
override fun onFinish() {
//Thats it 5 seconds gone.
}
}.start()
For more details on CountDownTimer
Related
I'm using the Exoplayer library to load videos in my application, but in certain videos I want to avoid that the user can't go forward in the video progress bar, only backward, until now I had achieved something by adding a listener to the DefaultTimeBar in this way:
var currentPosition: Long = 0
val exoPlayerProgressBar: DefaultTimeBar? = localPlayerView?.findViewById(
R.id.exo_progress
) as? DefaultTimeBar
exoPlayerProgressBar?.addListener(object: TimeBar.OnScrubListener {
override fun onScrubStart(timeBar: TimeBar, position: Long) {
currentPosition = position
}
override fun onScrubMove(timeBar: TimeBar, position: Long) {
if (currentPosition < position) {
timeBar.setPosition(position)
timeBar.setEnabled(false)
}
}
override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {
timeBar.setEnabled(true)
}
})
But it doesn't work well at all, I think the listener takes a long time to be called and the position of the onScrubStart method is not exact and besides, although I manage to disable the user to advance, I haven't managed to do it if he clicks on a place on the bar. Is there another way that I don't know?
If you are using custom view for your xml then setting this in xml will work for androidx.media3.ui.DefaultTimeBar
app:touch_target_height="0dp"
I also got this answer from one of stackoverflow answer only but not having link as of now.
You should use ExoPlayer.Listener and override onPlaybackStateChanged() to detect state of playing video progress (Player.STATE_IDLE, Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED), use Player.STATE_READY to hide the progress bar and Player.STATE_BUFFERING to show the progress bar.
override fun onPlaybackStateChanged(playbackState: Int) {
when (playbackState) {
Player.STATE_ENDED -> {}
Player.STATE_READY -> hideProgressBar() //*Hide progressbar!
Player.STATE_BUFFERING -> showProgressBar() //*Show progressbar!
Player.STATE_IDLE -> {}
}
}
Listening to playback events
I want to animate the progress bar so i am setting its progress using for loop but the loop is too much faster that i can't see the animation . I want the code to add a delay in the loop , i tried using thread delay but not working -
here is the code
private fun showProgress() {
for(i in 0..100){
Thread{
binding.customProgressBar.progress=i
Thread.sleep(100)
}
}
}
Solution : Was not calling start method , but if there any other approach then please let me know
private fun showProgress() {
Thread {
for (i in 0..100) {
binding.customProgressBar.progress = i
Thread.sleep(100)
}
}.start()
}
now i want to terminate the thread when fragment is on pause state .
how to achieve that ?
since you are using kotlin it is better to use coroutine, you can achieve your goal with something like this:
private suspend fun someProgress(scope: CoroutineScope) {
val job = scope.launch {
for (i in 0..100) {
binding.customProgress.progress = i
delay(100)
}
}
// use job.cancel() for cancelling the job or use job.join() for waiting for the job to finish
}
you can learn more about coroutine and how it works in here.
I have the MediaPlayer configured inside my ViewModel. All I want is a way to observe the mediaPlayer.currentPosition in a Composable. However, I cannot find a way to store it in a MutableState<T> value for Compose to observe. I just have a simple slider:
Slider(value = someMutableStateValue, onValueChange = { someMutableStateValue = it }
In my ViewModel, I declare var someMutableStateValue by mutableStateOf(0f)
Now, this app is being built for TV so I cannot provide touch input whatsoever. I don't think it would be required anyway, but I'm informing just in case.
All I want, is a method that will update the value of someMutableStateValue every time the MediaPlayer.currentPosition changes. Maybe there is some listener, but I can't find it as of now. I'm comfortable with using LiveData too, but I'd like to avoid that if possible.
PS: I actually do not even need to pass anything to the onValueChange listener, since it would never be called on a TV.
PS MArk II: Also, I cannot find out why the Slider take up all the width of my Screen? Is it the default implementation? Even hardcoding the width or for that matter, using the fillMaxWidth(...) Modifier with a restricted fraction doesn't seem to work.
It appears MediaPlayer doesn't offer a callback mechanism for the playback progress. I would guess this is because technically the progress would change with every frame but running a callback on every frame would impose a lot of CPU overhead, so they just omitted it alltogether. Instead, you need to resort to polling the progress. If you want to do this on the composable side, you could use LaunchedEffect:
var progress by remember { mutableStateOf(0f) }
LaunchedEffect(Unit){
while(isActive){
progress = mediaPlayer.currentPosition / mediaPlayer.duration
delay(200) // change this to what feels smooth without impacting performance too much
}
}
This is based on #Adriak K's answer above with a small improvement for ExoPlayer(v2.16.1) in case anyone else is looking into this:
var isPlaying by remember {
mutableStateOf(false)
}
isPlaying can be updated as below to keep track of player pause/resume state:
exoplayer.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
isPlaying = playbackState == Player.STATE_READY && isPlaying()
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason)
isPlaying = playWhenReady
}
})
LaunchedEffect(key1 = player, key2 = isPlaying) {
while (isActive && isPlaying) {
sliderPosition = (player.currentPosition + 0.0f) / player.duration
delay(200)
}
}
What I am trying to do is retrieve data from the server when I click a button. When I click the button, I want to show my "Loading..." TextView for 2 seconds, and only then show the data I got from the server. How can I do this?
For now my animation is working, but the data is showing almost instantly. I want to delay that. Using Thread.sleep(2000) causes both the data and Loading to be delayed.
val loadingAnimation :TextView = findViewById<TextView>(R.id.loadingAnimationTextView)
val alphaAnim = AlphaAnimation(1.0f, 0.0f)
alphaAnim.startOffset = 0
alphaAnim.duration = 2000
alphaAnim.setAnimationListener(object : AnimationListener {
override fun onAnimationRepeat(animation: Animation?) {
//not sure what code to put here
}
override fun onAnimationEnd(animation: Animation) {
// make invisible when animation completes, you could also remove the view from the layout
loadingAnimation.setVisibility(View.INVISIBLE)
}
override fun onAnimationStart(animation: Animation?) {
loadingAnimation.setVisibility(View.VISIBLE)
}
})
loadingAnimation.setAnimation(alphaAnim)
Thread.sleep(2000)
You can use the handler for this task.
Handler(Looper.getMainLooper()).postDelayed({
// Show you data here
loadingAnimation.setVisibility(View.INVISIBLE)
}, 2000)
Here, 2000 = 2 seconds
It's probably easier to use the ViewPropertyAnimator stuff:
loadingAnimation.animate()
.alpha(0)
.duration(2000)
.withEndAction {
// data displaying code goes here
}.start()
but honestly I don't think there's anything wrong with populating an invisible list, and just making it visible when you want to display it. But that up there is a way to chain runnable code and animations, one after the other
I am new to programming and I am making a basic app for radio in which an introduction audio sounds when you press a button, then a second audio should appear until the network connection of the online radio is established.
I have managed to make the intro audio sound complete when I click, then silence is generated until the online radio plays, but I don't know how to put a second audio that detects the charging status before the radio plays. This is my code:
fun MediaPlayerRadio(){
mediaPlayer = MediaPlayer.create(
this#MainActivity,
Uri.parse("https://radiolink.com")
)
mediaPlayer?.start()
}
........................................................................
fun MediaPlayerIntroSound(){
mediaPlayer = MediaPlayer.create(this, R.raw.SoundIntro)
mediaPlayer?.start()
}
........................................................................
fun click_Button_Radio(){
btn.setOnClickListener(){
if (btn.isSelected){
btn.isSelected = false
mediaPlayer?.stop()
}else{
btn.isSelected = !btn.isSelected
MediaPlayerIntroSound()
mediaPlayer!!.setOnCompletionListener(object : MediaPlayer.OnCompletionListener {
override fun onCompletion(mp: MediaPlayer?) {
MediaPlayerRadio()
}
})
}
}
}
I hope you can support me with this.
For this, you will have to use 2 media players, one for intro sound and another for actual sound.
While the time actual sound media player gets ready, you can play different audios on the intro sound media player.
To stop the intro audio player once the other player is ready, you can make use of a view model.
//Play Media player
mediaPlayer = MediaPlayer.create(this#MainActivity, R.raw.first_audio)
mediaPlayer.start()
// Add completion listener, so that we can change the audio
// once the first one is finished
mediaPlayer.setOnCompletionListener {
mediaPlayer.release()
mediaPlayer = MediaPlayer.create(this#MainActivity, R.raw.second_audio)
mediaPlayer.start()
}
// We are observing a MutableLiveData of Boolean type,
// once it is true we will stop the intro media player
// and start the radio player
viewModel.isPlayerReady.observe(this) { isReady ->
if(isReady) {
mediaPlayer.stop()
// Start radio media player
}
}
// This is how I am changing the value of MutableLiveData
demoBtn.setOnClickListener {
viewModel.isPlayerReady.value = true
}
I have tested the above approach and it works fine as expected.
Steps to link a view model to the activity:
Add the dependency: implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
Make a view model class and a mutable live variable of type boolean in it.
class MainViewModel: ViewModel() {
val isPlayerReady = MutableLiveData<Boolean>()
}
Link view model with the activity.
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
....
}