Android ExoPlayer已更改
如何在Android ExoPlayer已更改,android,listener,progress,exoplayer,Android,Listener,Progress,Exoplayer,如何在ExoPlayer上监视进度变化? 我试图实现一个隐藏的MediaController和重写setonseekbarchaneglistener方法,但目前没有成功。我想知道是否有另一种方法可以收听ExoPlayer进程。不确定这是最好的方法,但我是通过重载轨迹渲染器来实现的 我使用的是videoPlayer.getBufferedPercentage(),但您也可以自己计算百分比,只需使用TrackRenderer的getBufferedPositionUs()和getDurationU
ExoPlayer
上监视进度变化?我试图实现一个隐藏的
MediaController
和重写setonseekbarchaneglistener
方法,但目前没有成功。我想知道是否有另一种方法可以收听ExoPlayer
进程。不确定这是最好的方法,但我是通过重载轨迹渲染器来实现的
我使用的是videoPlayer.getBufferedPercentage()
,但您也可以自己计算百分比,只需使用TrackRenderer
的getBufferedPositionUs()
和getDurationUs()
公共接口监听器{
公共进度变更(长期进度);
}
公共类CustomVideoRenderer扩展了MediaCodeVideoTrackRenderer{
长期进展=0;
private final CopyOnWriteArraySet progressListeners=new CopyOnWriteArraySet();
// [...]
//跳过的构造函数
// [...]
public void doSomeWork(long positionUs,long elapsederrealtimeus)抛出ExoPlaybackException{
super.doSomeWork(位置、延时);
long tmpProgress=videoPlayer.getBufferedPercentage();
if(tmpProgress!=此.progress){
this.progress=tmpProgress;
for(ProgressListener ProgressListener:this.progressListeners){
progressListener.onProgressChange(进度);
}
}
}
public void addProgressListener(ProgressListener侦听器){
this.progressListeners.add(listener);
}
}
我知道这个问题很老了。但是,我是在实现ExoPlayer
时发现这一点的。这是为了帮助以后也这样做的其他人:)
因此,我遵循以下方法来跟踪播放的进度。这是在ExoPlayer
googledocs中完成的方式。它可以根据需要工作
在中签出PlayerControlView.java
updateProgressBar()
是更新SeekBar
进度的函数:
private void updateProgressBar() {
long duration = player == null ? 0 : player.getDuration();
long position = player == null ? 0 : player.getCurrentPosition();
if (!dragging) {
mSeekBar.setProgress(progressBarValue(position));
}
long bufferedPosition = player == null ? 0 : player.getBufferedPosition();
mSeekBar.setSecondaryProgress(progressBarValue(bufferedPosition));
// Remove scheduled updates.
handler.removeCallbacks(updateProgressAction);
// Schedule an update if necessary.
int playbackState = player == null ? Player.STATE_IDLE : player.getPlaybackState();
if (playbackState != Player.STATE_IDLE && playbackState != Player.STATE_ENDED) {
long delayMs;
if (player.getPlayWhenReady() && playbackState == Player.STATE_READY) {
delayMs = 1000 - (position % 1000);
if (delayMs < 200) {
delayMs += 1000;
}
} else {
delayMs = 1000;
}
handler.postDelayed(updateProgressAction, delayMs);
}
}
private final Runnable updateProgressAction = new Runnable() {
@Override
public void run() {
updateProgressBar();
}
};
希望这有帮助 这至少适用于Exoplayer 2
有四种播放状态:状态空闲、状态缓冲、状态就绪和状态结束
检查播放状态很容易。至少有两种解决方案:if语句或switch语句
无论播放状态如何,您都可以执行您的方法或设置其他内容,例如progressbar
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_ENDED) {
showControls();
Toast.makeText(getApplicationContext(), "Playback ended", Toast.LENGTH_LONG).show();
}
else if (playbackState == ExoPlayer.STATE_BUFFERING)
{
progressBar.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), "Buffering..", Toast.LENGTH_SHORT).show();
}
else if (playbackState == ExoPlayer.STATE_READY)
{
progressBar.setVisibility(View.INVISIBLE);
}
}
扩展您当前的播放器类(例如SimpleExoPlayer)并添加
然后在播放器活动中,只需实现接口并使用player.startProgressUpdater()启动更新代码>为了清楚起见,进度事件没有内置的EventListener,但是您可以在updateProgress()函数中调用Handler.postDelayed来获取当前进度
private void updateProgress(){
//get current progress
long position = player == null ? 0 : player.getCurrentPosition();
//updateProgress() will be called repeatedly, you can check
//player state to end it
handler.postDelayed(updateProgressAction,1000)
}
private final Runnable updateProgressAction = new Runnable() {
@Override
public void run() {
updateProgress();
}
};
有关更多详细信息,请参阅inside Exoplayer的源代码试试这个,它对我有用:
handler = new Handler();
runnable = new Runnable() {
@Override
public void run() {
progressbar.setProgress((int) ((exoPlayer.getCurrentPosition()*100)/exoPlayer.getDuration()));
handler.postDelayed(runnable, 1000);
}
};
handler.postDelayed(runnable, 0);
这里,
getCurrentPosition()
:以毫秒为单位返回当前播放位置
getDuration()
:曲目的持续时间(毫秒)
仅将onTouchListener与MotionEvent.ACTION\u一起使用
SeekBar exo_progress = (SeekBar) findViewById(R.id.exo_progress);
exo_progress.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
//put your code here!!
}
return false;
}
});
我发现了一个使用RxJava的非常优雅的解决方案。这也涉及到轮询模式,但我们确保每1秒使用一次间隔进行轮询
public Observable<Long> playbackProgressObservable =
Observable.interval(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
要最终连接到一起,只需调用subscribe,进度更新将每秒发出一次:
playbackProgressObservable.subscribe( { progress -> // Update logic here } )
注意:可观察。间隔
在调度程序的默认调度程序
上运行。计算()
。因此,您可能需要添加一个observeOn()
操作符,以确保将结果发送到正确的线程
playbackProgressObservable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(progress -> {}) // Update logic here
上述声明将为您提供一个一次性,在您完成观察后必须将其处理掉。
您可以这样做->
private var playbackDisposable: Disposable? = null
playbackDisposable = playbackProgressObservable
.observeOn(AndroidSchedulers.mainThead())
.subscribe(progress -> {}) // Update logic here
然后处理资源->
playbackDisposable?.dispose()
我不确定这是否是正确的方法,但我使用和TimerTask
来更新正在播放的音频的进度。
在我的MusicController课程中,我提出:
private void sendElapsedDuration() {
//To send the current elapsed time
final Timer t = new Timer();
Runnable r = new Runnable() {
@Override
public void run() {
t.schedule(new TimerTask() {
@Override
public void run() {
EventBus.getDefault().post(
new ProgressNotification(
player.getCurrentPosition(), player.getDuration())
);
if (player.getCurrentPosition() >= player.getDuration() ){
// The audio is ended, we pause the playback,
// and reset the position
player.seekTo(0);
player.setPlayWhenReady(false);
this.cancel();
// stopping the Runnable to avoid memory leak
mainHandler.removeCallbacks(this);
}
}
},0,1000);
}
};
if(player != null) {
if (player.getPlaybackState() != Player.STATE_ENDED)
mainHandler.postDelayed(r, 500);
else {
//We put the TimerTask to sleep when audio is not playing
t.cancel();
}
}
}
然后,在将侦听器添加到SimpleExoPlayer实例时,我调用了onPlayerStateChanged
中的方法。上面的代码通过EventBus发送每1秒(1000毫秒)播放的音频的经过时间和总持续时间。然后在承载SeekBar的活动中:
@Subscribe(threadMode = ThreadMode.MAIN)
public void updateProgress(ProgressNotification pn) {
seekBar.setMax((int) pn.duration);
seekBar.setProgress((int) pn.currentPosition);
}
如果您想完成此操作,只需听onpositionintercontinuction()
。如果正在清除seekbar
,它将为您提供信息如果您完全或部分使用播放器视图或播放器控件视图(仅用于按钮或其他),您可以直接从中设置进度侦听器:
PlayerControlView playerControlView = miniPlayerCardView.findViewById(R.id.playerView);
ProgressBar audioProgressBar = miniPlayerCardView.findViewById(R.id.audioProgressBar);
playerControlView.setProgressUpdateListener((position, bufferedPosition) -> {
int progressBarPosition = (int) ((position*100)/player.getDuration());
int bufferedProgressBarPosition = (int) ((bufferedPosition*100)/player.getDuration());
audioProgressBar.setProgress(progressBarPosition);
audioProgressBar.setSecondaryProgress(bufferedProgressBarPosition);
});
我也有这个问题,我找到了解决办法
但是解决方案:
1.创建如下类:
public class ProgressTracker implements Runnable {
public interface PositionListener{
public void progress(long position);
}
private final Player player;
private final Handler handler;
private PositionListener positionListener;
public ProgressTracker(Player player, PositionListener positionListener) {
this.player = player;
this.positionListener = positionListener;
handler = new Handler();
handler.post(this);
}
public void run() {
long position = player.getCurrentPosition();
positionListener.progress(position);
handler.postDelayed(this, 1000);
}
public void purgeHandler() {
handler.removeCallbacks(this);
}
}
2.最后在代码中使用它:
tracker = new ProgressTracker(player, new ProgressTracker.PositionListener() {
@Override
public void progress(long position) {
Log.i(TAG, "VideoViewActivity/progress: position=" + position);
}
});
3.在最后一步中,当您想要释放玩家时,不要忘记呼叫purgeHandler(重要)
我是通过kotlin flow做的
private fun audioProgress(exoPlayer: SimpleExoPlayer?) = flow<Int> {
while (true) {
emit(((exoPlayer?.currentPosition?.toFloat()?.div(exoPlayer.duration.toFloat())?.times(100))?.toInt()!!))
delay(1000)
}
}.flowOn(Dispatchers.IO)
rx java实现:
private val disposablesVideoControlsDisposable = CompositeDisposable()
fun showVideoControlsAndSimilarTray() {
videoSeekBar?.setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) seekVideoProgress(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
val disposable = Observable.interval(0, 1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
calculateVideoProgress()
}
disposablesVideoControlsDisposable.add(disposable)
}
private fun calculateVideoProgress() {
val currentMill = exoPlayer.currentPosition
val totalMillis = exoPlayer.duration
if (totalMillis > 0L) {
val remainMillis = (totalMillis - currentMill).toFloat() / 1000
val remainMins = (remainMillis / 60).toInt()
val remainSecs = (remainMillis % 60).toInt()
videoProgressText.setText("$remainMins:${String.format("%02d", remainSecs)}")
seekBarProgress.set((currentMill.toFloat() / totalMillis * 100).toInt())
}
}
private fun seekVideoProgress(progress: Int) {
val seekMillis = exoPlayer.duration.toFloat() * progress / 100
exoPlayer.seekTo(seekMillis.toLong())
}
最后,当您完成时:
fun disposeVideoControlsObservable() {
disposablesVideoControlsDisposable.clear()
}
很简单
var player = SimpleExoPlayer.Builder(context).build();
player.addListener(object:Player.Listener{
override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(player, events)
if (events.containsAny(
Player.EVENT_IS_LOADING_CHANGED,
Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_PLAY_WHEN_READY_CHANGED,
Player.EVENT_IS_PLAYING_CHANGED
)) {
log(msg="progres ${player.currentPosition}")
}
}
})
您可以在1356行查看com.google.android.exoplayer2.ui.PlayerControlView.java您可以提供关于您的解决方案的更好的描述吗?我希望这会有所帮助。这并不能回答任何问题,这实际上意味着您正在轮询state@Thomas这实际上似乎是一种正确的解决方案,因为exoplayer本身对UI生成进度事件的频率太高。@AnkitAggarwal我有自定义进度条如何在播放视频plz Help时更新进度?从今天开始,exoplayer 2.14.0版的上述方法已经改变了很多,发现很难获得用于拖动的等效方法/变量progressBarValue,…谢谢你的回答它是有效的…但它是
tracker.purgeHandler();
player.release();
player = null;
private fun audioProgress(exoPlayer: SimpleExoPlayer?) = flow<Int> {
while (true) {
emit(((exoPlayer?.currentPosition?.toFloat()?.div(exoPlayer.duration.toFloat())?.times(100))?.toInt()!!))
delay(1000)
}
}.flowOn(Dispatchers.IO)
val audioProgressJob = launch {
audioProgress(exoPlayer).collect {
MP_progress_bar.progress = it
}
}
private val disposablesVideoControlsDisposable = CompositeDisposable()
fun showVideoControlsAndSimilarTray() {
videoSeekBar?.setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) seekVideoProgress(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
val disposable = Observable.interval(0, 1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
calculateVideoProgress()
}
disposablesVideoControlsDisposable.add(disposable)
}
private fun calculateVideoProgress() {
val currentMill = exoPlayer.currentPosition
val totalMillis = exoPlayer.duration
if (totalMillis > 0L) {
val remainMillis = (totalMillis - currentMill).toFloat() / 1000
val remainMins = (remainMillis / 60).toInt()
val remainSecs = (remainMillis % 60).toInt()
videoProgressText.setText("$remainMins:${String.format("%02d", remainSecs)}")
seekBarProgress.set((currentMill.toFloat() / totalMillis * 100).toInt())
}
}
private fun seekVideoProgress(progress: Int) {
val seekMillis = exoPlayer.duration.toFloat() * progress / 100
exoPlayer.seekTo(seekMillis.toLong())
}
fun disposeVideoControlsObservable() {
disposablesVideoControlsDisposable.clear()
}
var player = SimpleExoPlayer.Builder(context).build();
player.addListener(object:Player.Listener{
override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(player, events)
if (events.containsAny(
Player.EVENT_IS_LOADING_CHANGED,
Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_PLAY_WHEN_READY_CHANGED,
Player.EVENT_IS_PLAYING_CHANGED
)) {
log(msg="progres ${player.currentPosition}")
}
}
})