-
Notifications
You must be signed in to change notification settings - Fork 6k
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
Allow specifying a Looper when instantiating ExoPlayer #4278
Comments
The stack trace of this crash looks really unusual. It doesn't really point a valid order of methods. Do you have any more details about what happened at the time this exception occurred? One thing worth checking is that you only access the player from one single thread (the one you called newSimpleInstance on). But that's just a guess because we've seen problems in the past where this caused problems at unexpected locations. And it would also explain why it's difficult to reproduce. |
@tonihei as of the moment we don't have any other info besides that as we can't replicate the crash on all of our test devices. I'm going to take a look at the how we access the player. I'm quite sure we only do it on a single thread though. And one more thing, I've said that the issue didn't occur when we were using SDK version 25 and Exoplayer version 2.7.3, but it seems I was mistaken, we just didn't notice it as it's not the top crash for the app during that time. (so I'm gonna rule out the possibility of problems with the SDK version) |
@tonihei Just got the same crash after updating to 2.8.1 in prod (skipped 2.8.0) but previously no issue with 2.7.3, no target SDK change. Same situation, can't reproduce very rare crash
|
This stack trace is much better as it seems to point to a valid code position. However, the call seems utterly impossible unless some form of parallel change is happening.
The stack trace indicates we end up in the else case and then call into an empty timeline, but that should have been caught by the if condition. Side note: This problem is likely not new, but is only surfaced because we added an internal event listener (the AnalyticsCollector referenced in the stack trace) which also accesses the player state. |
Well reading the stack trace it seems your listener is listening on main thread independently of what unique thread we may work :) Furthermore is there anywhere in the documentation anything stating that exoplayer creation + all calls needs to happens on the same thread ? |
The documentation is here: http://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/ExoPlayer.html As you pointed out, the stack trace above seems to call the listeners on the main thread. That means the player was created on the main thread and all other calls to the player should happen on the main thread too. |
I confirm that part of the DOC is well hidden and recommended quite different than mandatory :) Creating the player on mainthread + using threadhandler is quite common, specially since support library mediasession should be created on main thread :( If it's now mandatory and you can't allow such basic use case it should be in red here too http://google.github.io/ExoPlayer/guide.html And changelog for 2.8.0 should be updated too as I now need to completely rewrite my code due to that little change and it would have been cool to know before pushing to prod :( |
As I said, this is not new and we always recommended to use one thread only. The problem only showed up because of our additional internal listener. I see that it's potentially unwanted to use the main thread, but I think this can be solved by moving the ExoPlayerFactory call to your custom HandlerThread. Alternatively, we may provide an option for the ExoPlayerFactory to specify the Looper for all player operations after construction. Would that solve your issue? |
It was recommended but with proper handling was working now it can't due to a change that is not clearly visible in the changelog. (Not saying that I'm right about how I did implement just that the change that render non recommended ways that could work to will not work at all for sure is not visible enough). I'm not native English speaker so don't take my words too hard :) And this could also explains why recommended for me is very far from forbidden. HandlerThread was ok before migrating to exoplayer and as it did work with exoplayer and starting guide did not says it should not I keep it that way. The main question is about the handling of ConcatenatingMediaSource that I use as dynamic...., Does all the calls to the source also need to be done on same thread? As it's the consuming problematic part for main thread. Providing the looper to get control over your AnalyticsCollector is a nice addition for this use case. I still have to repost from Player.EventListener to main thread but at least I do not have to change everything. But this would propose an issue for those in my case. |
Yes, "recommended" may be misleading in this case. The guide says "strongly recommended" though ;) For the rest of the question:
|
I meant that since I created the player on MainThread the listener was called on mainthread. But I did not do anything in it that could be non thread safe. Just that I had the events on the thread I wanted and it was nice. Since your code needs to access the player the same way as my non thread safe call the looper will migrate your AnalyticsCollector code to that thread and fix the issue, but it will also migrate my listener. When I read http://google.github.io/ExoPlayer/guide.html I do not see anything talking about threading is there something else you call guide? For the interface documentation that most won't read it's also said: Anyway now I know, but you may want to do something so that everybody know before migrating to 2.8. |
We'll reword some parts of the guide to make that more explicit. And we are adding support for setting a custom Looper to run all player commands and callbacks on. Marking this as an enhancement until this is done. |
For the record I also got one
And this one is called on the right thread and since I'm calling stop if the gathering is made after the stop there's no way that there's a timeline change after that. |
It's the same problem. The stack trace you show can only possibly happen when two concurrent modifications are happening. |
Yes but all modifications are from the same thread this stop call occurs in my thread handler and your collector is also called in that handler thread and not in the main thread like the other stack trace, only thing that could make change on another thread would be something inside ExoPlayer. And the app for sure does nothing after a stop in a short delay except clearing the playlist too. I really want to be sure that the looper param will fully fix that of if I should already start to completely rewrite that part to have everything in a single thread. |
There are two sources of events:
So for the example above, the callback for player.stop() happens in your handler thread. And clearing the playlist (or something else), sends a simultaneous message to the main thread, because the player was created on the main thread. That's why you're seeing the concurrent access. When we add the support for the Looper param, the internal calls will happen on this specified Looper. So, if you can ensure that all player methods are called on the same Looper, no concurrent access should happen. And for the ConcatenatingMediaSource, it still means that you can call its methods on any thread because the resulting player callbacks will be directed to the specified Looper. |
Ok thanks, then the looper should be enough and the refactor not that big. Will it be a 2.8.x or a 2.9 thing? The crash are rare but just to know what to answer to users that encounter them. |
Difficult to say. Depends on how many other changes we have. Not sure how difficult it is to do in your code, but you might move the player creation to your custom handler thread in the meantime as this is equivalent to specifying the Looper at the time of creation. |
Well the initialisation part date from very long time with MediaPlayer and Android 2.x ;) Seeing that you recommend to use main thread for player access, if I have to update / rewrite that part I'll go full main thread road to be safer. (I currently only do music but should add video at some point so better be clean). The exoplayer part of my app is currently only used by about 8% of the users and the race condition is rare so there's no urgency. Just need to know where I'm going as time planing as indie dev is the most complex part :( |
Just a quick update on my part. There was indeed a line of code accessing the SimpleExoPlayer that is running on a separate thread. I have updated that part and made sure every other access is indeed in the main thread. So far, the issue hasn't resurfaced 2 days in to our new version. |
This makes the requirement that all calls are made on one thread more explicit and also mentions this in the Getting Started guide. Issue:#4278 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=198694579
This makes the requirement that all calls are made on one thread more explicit and also mentions this in the Getting Started guide. Issue:#4278 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=198694579
Currently, the looper of the thread the player is created on is used (or the main looper if this thread doesn't have a looper). To allow more control over the threading, this change lets users specificy the looper which must be used to call player methods and which is used for event callbacks. Issue:#4278 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=201331564
Issue description
java.lang.IndexOutOfBoundsException
on Timeline.getWindowWe're using SimpleExoPlayer to playback audio on our app. We didn't have any problems on our initial launch with the integration. However, when we upgrade the target SDK version to 27 (previously it was 25), then we started getting crashes. Also note that we were using version 2.7.3 of Exoplayer on our previous version.
For the usage, this is how we initialize the player:
and then this is how we prepare for playback
We are not able to replicate the crash on our end so its a bit tricky. We only have the crash log (see below).
Version of ExoPlayer being used
2.8.0
Crash Log
The text was updated successfully, but these errors were encountered: