Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SeekTo throws IndexOutOfRange after adding item to DynamicConcatenatingMediaSource. #3407

Closed
ourdex86 opened this issue Oct 30, 2017 · 19 comments
Labels

Comments

@ourdex86
Copy link

ourdex86 commented Oct 30, 2017

I am trying to use DynamicConcatenatingMediaSource to create dynamic playlists.

Here is the implementation.

public void initMusicPlayer(){

        if (songs !=null)
        {

            MediaSource mediaSource;
            ArrayList<MediaSource> sources = new ArrayList<>();

            MusicItem song;
            for (int i=0;i< songs.size();i++)
            {
                song = songs.get(i);
                mediaSource = buildMediaSource(Uri.parse(song.getMusicUrl()));
                sources.add(mediaSource);
            }

            dynamicMediaSource = new DynamicConcatenatingMediaSource();
            dynamicMediaSource.addMediaSources(sources);
            exoPlayer.prepare(dynamicMediaSource,false,false);

            exoPlayer.addListener(this);
            if (currentPlayingSongIndex == -1)
            {
                exoPlayer.setPlayWhenReady(false);
            }
            else
            {
                exoPlayer.seekTo(currentPlayingSongIndex,0);
                exoPlayer.setPlayWhenReady(true);
            }
        }
    }

public void addItemToPlaylist(MusicItem song,boolean shouldPlay){

        long position = exoPlayer.getContentPosition();
        MediaSource mediaSource = buildMediaSource(Uri.parse(song.getMusicUrl()));
        dynamicMediaSource.addMediaSource(mediaSource);
        exoPlayer.prepare(dynamicMediaSource,false,false);

        if (shouldPlay)
        {
            exoPlayer.seekTo(currentPlayingSongIndex,0);
        }
        else
        {
            exoPlayer.seekTo(currentPlayingSongIndex,position);
        }
    }

But, the audio is not played. Its immediately invoking onPlayerStateChanged with player state STATE_ENDED

Exoplayer Version : r2.5.4
Android OS Version : 7.1.2
Device : Pixel XL

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2017

When the player immediately changes to STATE_ENDED, it usually means that your media files can't be loaded or played. Have you checked whether you can play your files with ExoPlayer directly (without using DynamicConcatenatingPlaylist)? Also, please check if you receive an error on Player.EventListener.onPlayerError().

In addition, please note that you only need to call player.prepare(mediaSource) once. It seems like you re-prepare the player in addItemToPlaylist. This is not necessary, just add the new playlist item to the dynamic concatenating media source.

@ourdex86
Copy link
Author

ourdex86 commented Oct 31, 2017 via email

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2017

Could you try to remove the seekTo operations? A transition to STATE_ENDED might also be caused by a seek to an invalid position.

@ourdex86
Copy link
Author

ourdex86 commented Oct 31, 2017 via email

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2017

I'm just trying to find the reason for the problem you have. You're right that you should be able to call seekTo to play a particular song in the playlist. So, does it help to remove the seekTo operations? That is, your content plays without going to STATE_ENDED?

@Tolriq
Copy link
Contributor

Tolriq commented Oct 31, 2017

Seeks needs to happens after STATE_READY and not just after the prepare got caught by this one once :)

And you should not call prepare after adding songs to the playlist if the playlist was already prepared.

Init should call exoPlayer.prepare(dynamicMediaSource , true,true); to start clean then nothing more is needed.

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2017

Not sure if I understand you correctly - does that mean you solved your problem now? Particularly by not calling prepare again?

@Tolriq
Copy link
Contributor

Tolriq commented Oct 31, 2017

I'm not the OP, I just give possible solutions to the issue from code posted and personal experience since I'm playing with DynamicConcatenatingMediaSource right now.

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2017

Oh, sorry :) Didn't even notice. Thanks for the advice then.

One small correction: seeks can happen at any time after calling prepare, there should be no need to wait for STATE_READY.

The rest is correct and as already described - prepare should only be called once preferably with the (source, true, true) parameters. Not resetting the state in prepare is usually only used when re-preparing after an error.

@Tolriq
Copy link
Contributor

Tolriq commented Oct 31, 2017

@tonihei don't want to hijack the thread, but in 2.5.4 even with a simple MediaSource, calling prepare then seek then playwhenready does not seek at all.

Only working solution, is prepare without playwhenready, wait for STATE_READY then seek then playwhenready(true)

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2017

@Tolriq Preparing a media source and immediately seeking works fine for me. If you feel there's an issue, it would be great if you can open a new GitHub issue with reproduction steps.

@ourdex86 Did disabling the seeks prevent the player from switching to STATE_ENDED immediately?

@ourdex86
Copy link
Author

ourdex86 commented Nov 1, 2017

@tonihei & @Tolriq .

Actually you both gave valid points.
I did the following & now everything works.

public void initMusicPlayer() {
        if (songs !=null)
        {
            long currentDuration = -1;
            if (isPlaying() && exoPlayer.getCurrentWindowIndex() == currentPlayingSongIndex)
            {
                currentDuration = exoPlayer.getCurrentPosition();
            }
            MediaSource mediaSource;
            ArrayList<MediaSource> sources = new ArrayList<>();
            MusicItem song;
            for (int i=0;i< songs.size();i++)
            {
                song = songs.get(i);
                mediaSource = buildMediaSource(Uri.parse(song.getMusicUrl()));
                sources.add(mediaSource);
            }
            dynamicMediaSource = new DynamicConcatenatingMediaSource();
            dynamicMediaSource.addMediaSources(sources);
            exoPlayer.addListener(this);
            exoPlayer.prepare(dynamicMediaSource,true,true);
            if (currentPlayingSongIndex == -1)
            {
                exoPlayer.setPlayWhenReady(false);
            }
            else
            {
                if (currentDuration != -1)
                {
                    exoPlayer.seekTo(currentPlayingSongIndex,currentDuration);
                }
                else
                {
                    exoPlayer.seekTo(currentPlayingSongIndex,0);
                }
                exoPlayer.setPlayWhenReady(true);
                sendBroadcast(new Intent("PLAY"));
                showNotification();
            }
        }
    }

public void addItemToPlaylist(MusicItem song,boolean shouldPlay)
    {
        songs = SingleTon.sharedInstance().getMusicItemsList();
        if (dynamicMediaSource!=null)
        {
            MediaSource mediaSource = buildMediaSource(Uri.parse(song.getMusicUrl()));
            dynamicMediaSource.addMediaSource(mediaSource);
            if (shouldPlay)
            {
                Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        exoPlayer.seekTo(currentPlayingSongIndex,0);
                    }
                },500);
            }
        }
        else
        {
            initMusicPlayer();
        }
    }

But, I have to use a delay of 500 millis to play an item that was added to the playlist or else its getting crashed.

@Tolriq
Copy link
Contributor

Tolriq commented Nov 1, 2017

@tonihei I have also noticed that seekTo can crash but Javadoc does not says it can throw and the func is not marked a throwing.

@ourdex86 it's hard to know when you call addItemToPlaylist with just this extract :) You may want to provide full crash logs and more details to get more help.

@ourdex86
Copy link
Author

ourdex86 commented Nov 1, 2017

@Tolriq

Lets say I am playing a media source at index 1 & then while its being played if I add another item to the DynamicMediaSource & if I try to play the same added media source immediately after adding
then I am getting this "IndexOutOfRange" exception at DynamicMediaSource.

But, after adding it to the DynamicMediaSource & if I give a delay of 500 ms & then if I play the recently added media source then its working fine.

I hope you got it

@tonihei
Copy link
Collaborator

tonihei commented Nov 1, 2017

@Tolriq player.seekTo is not supposed to throw. We noticed that there's a bug which may throw under certain circumstances after seeking (#3362). We're currently fixing this.

@ourdex86 Judging from your code, it seems that it was the seekTo that was throwing, right? As I said above, seeking to the item you just added should be supported and there should be no need for you to to delay this operation. The question is whether you are having a problem because of the issue we're currently fixing anyway (#3362) or whether your problem is something else and related to the DynamicConcatenatingMediaSource.

Could you please post a complete stack trace of your error or attach a bug report for us to investigate this further? Thanks!

@tonihei tonihei changed the title DynamicConcatenatingMediaSource not working SeekTo throws IndexOutOfRange after adding item to DynamicConcatenatingMediaSource. Nov 1, 2017
@tonihei
Copy link
Collaborator

tonihei commented Nov 7, 2017

I was able to reproduce your issue myself. It's kind of related to the ongoing refactoring of the seek handling, so it's hopefully going to be fixed soon.

@tonihei tonihei added bug and removed need more info labels Nov 9, 2017
@tonihei
Copy link
Collaborator

tonihei commented Nov 9, 2017

We will push a fix to the dev branch soon which will allow you to pass in a custom Runnable to any of the DynamicConcatenatingMediaSource methods. These Runnables will be executed immediately after the player was made aware of the change. This removes the need for your workaround and also the timing of 500ms. The resulting code for your example would look like this:

dynamicMediaSource.addMediaSource(mediaSource, new Runnable() {
  @Override
  public void run() {
    exoPlayer.seekTo(newSongIndex, 0);
  }
});

@Tolriq
Copy link
Contributor

Tolriq commented Nov 9, 2017

@tonihei Just to confirm as 2.6.0 is already branched this means that this will go in for the release after?

@ojw28
Copy link
Contributor

ojw28 commented Nov 9, 2017

It will be cherry-picked into 2.6.0, I think.

ojw28 pushed a commit that referenced this issue Nov 13, 2017
…ions.

These callbacks are executed on the app thread after the corresponding
timeline update was triggered. This ensures that seek operations see the
updated timelines and are therefore valid, even if the seek is performed into a
window which didn't exist before.

GitHub:#3407

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=175136187
ojw28 pushed a commit that referenced this issue Nov 13, 2017
…ions.

These callbacks are executed on the app thread after the corresponding
timeline update was triggered. This ensures that seek operations see the
updated timelines and are therefore valid, even if the seek is performed into a
window which didn't exist before.

GitHub:#3407

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=175136187
@ojw28 ojw28 closed this as completed Nov 13, 2017
@google google locked and limited conversation to collaborators Mar 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants