-
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
Invitation to comment: Player threading model #4463
Comments
We have two use cases for a multithreaded player:
We ran into a few issues:
For our purposes a single-threaded player works fine, as long as that thread isn't mandated to be the main thread. Also note that Android thinks it's OK to interact with UI components from a non-main thread, as long as the components aren't tied directly to an external view hierarchy. I don't know how that interacts with input messages, but it might be possible to resolve the UI component thread problem by rendering them on a custom drawing thread and posting the render to a View asynchronously. We did this in apps a couple of times to allow for custom cached rendering. Another thought... would it be reasonable to design a thread-safe wrapper that minimizes the complexity of the problem through a single (or a few) immutable |
@markmckenna Thanks for the feedback! Just some initial thoughts:
|
@tonihei all good points, thank you :)
|
Wanted to chime in on mark's suggestion:
We use a similar pattern in our app, but it is to interact with our class that ultimately manages interactions with exoplayers. Any call to that class is done through a wrapper that always posts messages to a dedicated thread for the manager and works well to hide the threading concerns from the clients of our manager. About our threading model: We have 3 main threads to worry about: 1 - Player internal thread (the one described above), all actions to our manager are posted to this thread and all exoplayer messages are delivered on this thread. Most of the large interactions with the player occur on this thread. (play/pause/stop/etc...) 2 - ExoPlayer creation thread. We create the exoplayer on a thread that's literally dedicated for this in the app (lives for the entire life of the app and used exclusively for this purpose). This is most likely not required anymore, but we introduced it for a few legacy reasons. The first reason was the thread safety of MediaCodec access on certain older devices (i believe Galaxy S5), we would see native crashes in the media stack if we did not create all of the Android MediaPlayer objects and do any MediaCodec interrogation (checking of codec supports main/high profile, etc...) and also release any android MediaPlayer instances. We used to have both the exoplayer and MediaPlayer alongside each other, so this has carried over. 3 - Ticking thread. We spawn a thread that is responsible for firing a precisely timed event that we use to track analytics. This thread calls getCurrentPosition() on the player VERY frequently. I am fairly certain that this is the reason why we're seeing the threading issues linked in this thread. I think we could get rid of thread number 2 already, but thread 1 is definitely staying. We'd love to get rid of thread 3, but exoplayer does not have a supported way to notify when time passes, so we had to implement something on our own. I'd love to see exoplayer support events on a cadence and then we could get rid of this. |
@natez0r Thanks for the insight. Really helpful to see how ExoPlayer is used - especially the parts we never intended to be used like that :) The short answer is: All three threads should be just one. The player is simply not written to be safely accessed from multiple threads. The slightly longer answer:
|
Awesome discussion! @tonihei regarding using the background thread, note that we actually use that to take asynchronous methods and make them synchronous--for example pairing up a seek event with the event that indicates the seek has completed successfully, so we don't run into situations such as the user requests a seek just as content is changing, and the seek action ends up carrying over into the new piece of content because things happened in an unexpected order. This is a bit of a trivial example in that I can see other ways to fix it, but it gets more complex with SSAI and corrective seeking, our plugin architecture, when handing the player lib off to other developers to control who may not be familiar with the ins and outs of player development, and so on. It's probable that we could've solved everything by queueing on the main thread, but I'm not sure it would've been a better solution (...I'm also not sure it wouldn't be a better solution, mind you). |
@tonihei thanks for the response! (sorry I was OOO since I posed my message). We call getCurrentPosition every 10ms in our "ticking" code. There are a bunch of components that are based on the Ticks. We convert them into events and all of our underlying analytics code uses the ticks to collect various statistics on a set cadence as well as simpler things like a throttled (roughly 20FPS) event that the UI elements use to be informed of the progress of the player. We're not married to the 10ms cadence, it was just a value that was chosen across platforms so we'd have similar accuracy on iOS/Android. Could you you elaborate on "listening for how long a video has been played" using the |
@natez0r |
We also decided to move forward with option 1 above, but without actual assertions for now.
|
…ead. This doesn't break apps which violate this policy. But it creates a clear warning which is also likely to be reported in analytics tools. Issue:#4463 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=213442510
Closing this issue because we settled on an implementation option, added documentation to reduce confusion, and added warnings if player is used from the wrong thread unintentionally. |
The current threading model came up in multiple issues recently and we are considering options to improve the current situation. If someone feels strongly for/against a solution, please let us know.
In particular, if you are using ExoPlayer on a background thread we would be interested why you feel this is needed.
Background
The current player threading model requires that the player is accessed from a single thread. The player also calls all listener callbacks on this thread. This thread is the thread the player is created on or the main thread if that thread doesn't have a Looper.
This model may cause problems in the following situations:
This may happen if:
The first example causes problems, although the player is used in the documented way and only ExoPlayer's own UI components violate the contract. The second example violates the documented rules outright, but it's easy to get wrong and developers may feel that having to post onto the correct thread is unnecessarily complicated.
Options for improvement
There are four main options to improve the current model:
Option 4 is the least preferred option, as it involves many changes and may introduce new problems.
Options 1-3 are all feasible. We need to establish whether using the player on a background thread is (a) necessary for any case or (b) widely used. If neither is true, option 2 or 3 may be best.
The text was updated successfully, but these errors were encountered: