-
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
DASH/HLS/SS downloads: Parallelize & merge requests to improve download speed #5978
Comments
What is the progress of this issue. the performance of our product offline download feature is extremely poor. because of the low ts cdn, we need parallelize segment downloads to download more ts files in parallel. |
This is really serious issue. For a bare 600MB (in 8000 chunks) player needs about 40 minutes to download (over 300 MBIT network)... |
@Stijak - just measured a download on my side: Your numbers sounds strange. Can you please share some details about your device and implementation? I do however also see the need for parallelized downloads since ExoPlayer download is still quite a bit slower than our 3rdparty player/downloader. |
Ok, thanks for feedback. Judging on your responses, on my test Galaxy S9+ speed should be high even on exoplayer 2.9, and yet, still slow, and there are no CPU bottlenecks or anything like that. I even transferred DASH from CDN to my local macbook apache to eliminate eventual CDN problems with small chunks. |
Update: I used red bull sample, since it was longest, and using same bitrate we aim for our content, and our production app for testing (so it would be apple to apple comparison), I was able to test 15 second segment size and 1 second segment size downloads. 15 second segments download finished in less than 2 minutes, while 1 second segment took about 8 minutes. So here clear differences in download speed. 1 second is about 5 000 segments and still noticeably faster than our comparable content. Is it some other encoding option, or DRM (our content is using Widewine) I am not sure, but will try to reach solution with content providers... |
Are there any updates for this issue? The parallelized downloads feature is really important especially for poor segments downloads' speed. |
@ojw28 is this something you're currently working on? We're looking to move from our current download solution to ExoPlayer's built-in, and this is a serious blocker. |
Unfortunately no progress yet. But acknowledged the renewed interest and we should probably take a look soon. |
what is the status of this issue? |
Is there are any update? |
This speeds up downloads where segments have the same URL with different byte ranges. We limit the merged segments to 20 seconds to ensure the download progress of demuxed streams is roughly in line with the playable media duration. Issue:#5978 PiperOrigin-RevId: 280410761
We still have a lot of interest in this issue. Is anyone actively working it? |
The merging part is already done and released from 2.11.0 onwards (see commit above). The parallelization is also implemented but not submitted yet due to pending reviews and it will likely be in the next major release. |
Correction: The merging part was not actually included in 2.11.0 (or any tagged release to date). It will probably land together with the parallelization improvement in 2.12.0. |
Bitrates in SmoothStreaming manifests are average bitrates, as per the ref'd issue. Issue: #5978 PiperOrigin-RevId: 296467667
Bitrates in the DASH manifest are peak bitrates, as per the ref'd issue. Issue: #5978 PiperOrigin-RevId: 296478812
Issue: #5978 PiperOrigin-RevId: 297876336
@ojw28 Is there a branch where one could test this out prior to the 2.12.0 release becoming available? |
I second this question ☝️ We are really interested in testing this feature |
Changes will appear on the Note that merging segment fetches is already committed, for the (ideal) case where consecutive segments use the same URL and adjacent byte ranges. Parallel segment fetches is being worked on currently, and you should see commits appearing over the next two weeks. |
Thanks very much for the update and transparency ! |
This speeds up downloads where segments have the same URL with different byte ranges. We limit the merged segments to 20 seconds to ensure the download progress of demuxed streams is roughly in line with the playable media duration. Issue:google#5978 PiperOrigin-RevId: 280410761
I developed my own implementation of ExoPlayer parallelization segments here (https://github.com/jruesga/ExoPlayer/commits/parallel-chunks-downloads), just in case anyone want to test it. It's currently based on r2.10.8, but includes the "merge segment" patch, so I think is easy portable to the stable and dev branches. Seems to work quite nice, and I'm observing systematically lower download times. I measured the patch with some DASH and HSL resources from the demo app. |
Issue: #5978 PiperOrigin-RevId: 308076851
- Executor is a superclass of ExecutorService, so this is arguably a little more flexible. - It removes the need to use null for direct execution, because Runnable::run is a direct executor that can be trivially used instead. - Removing the error-prone "cannot be a direct executor" restriction in the parallel version of SegmentDownloader requires not relying on the futures returned from ExecutorService.submit() anyway. Issue: #5978 PiperOrigin-RevId: 308586620
This will make it a bit easier to push manifest loads to an Executor. Issue: #5978 PiperOrigin-RevId: 308608155
- Stop throwing InterruptedException from CacheUtil. When a CacheUtil operation throws or returns, the caller should always check its own state to determine whether they canceled the operation. If a caller is trying to catch InterruptedException separately to IOException to do something different in that case, they're probably doing the wrong thing. So it's simpler, and probably less error prone, just to throw an IOException in the case of interruption. - Throwing InterruptedIOException is also consistent with what our Extractor and DataSource implementations do. Issue: #5978 PiperOrigin-RevId: 309411556
DownloadManager doesn't know enough about the Downloader implementations to know that interrupting the thread is the right thing to do. In particular, if a Downloader implementation wants to delegate work to additional worker threads, then it will probably not want its main thread to be interrupted on cancelation. Instead, it will want to cancel/interrupt its worker threads, and keep its main thread blocked until work on those worker threads has stopped (download() must not return whilst the Downloader is still touching the cache). This change moves control over what happens to the individual Downloader implementations. Issue: #5978 PiperOrigin-RevId: 309419177
It looks like the getManifest function got removed from the SegmentDownloader implementation classes after these changes. We are overriding this abstract function to filter the playlist & copy stream keys currently. Can we provide access to the getManifest in dependent classes like we have now or remove the final keyword in getManifest method in SegmentDownloader? Thanks ExoPlayer/library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java Line 227 in 535e14c
|
I don't really understand what you're doing, or why. Playlist filtering based on the stream keys already happens automatically in |
We've the video bitrate available before start of download & not stream keys. In getManifest, I am loading the manifest & selecting suitable stream keys before filtering.
|
I see. I think the way we'd support that is to let you inject your own
It's possible you already have such a |
I believe FilteringManifestParser should work in our case with this logic. Thanks for the prompt reply. Appreciate it. |
- To make it clear that cache keys are for whole resources - To explicitly make it clear to implementors that deriving a cache key from position and length is invalid. We've seen at least one developer trying to do this [1], so it seems worthwhile to be explicit! [1] #5978 (comment) Issue: #5978 PiperOrigin-RevId: 312643930
A subsequent change will rename it to CacheWriter and make it instantiable. Issue: #5978 PiperOrigin-RevId: 312648623
Sometimes it's useful to be able to block until something on some other thread "really has finished". This will be needed for moving caching operations onto the executor in Downloader implementations, since we need to guarantee that Downloader.download doesn't return until it's no longer modifying the underlying cache. One solution to this is of course just to not interrupt the thread that's blocking on the condition variable, but there are cases where you do need to do this in case the thread is at some other point in its execution. This is true for Downloader implementations, where the Download.download thread will also be blocking on PriorityTaskManager.proceed. Arranging to conditionally interrupt the thread based on where it's got to is probably possible, but seems complicated and error prone. Issue: #5978 PiperOrigin-RevId: 313152413
- The new CacheWriter is simplified somewhat - Blocking on PriorityTaskManager.proceed is moved out of CacheWriter and into the Downloader tasks. This is because we want to shift only the caching parts of the Downloaders onto their Executors, whilst keeping the blocking parts on the main Downloader threads. Else we can end up "using" the Executor threads indefinitely whilst they're blocked. Issue: #5978 PiperOrigin-RevId: 313222923
Issue: #5978 PiperOrigin-RevId: 313802629
@jruesga - We were able to make multiple write locks work. This is merged in 235df09. Hopefully we'll get the change that actually parallelises everything in next week. It's turned out to be a much bigger task than was initially anticipated. |
This comment has been minimized.
This comment has been minimized.
…mentations Issue: #5978 PiperOrigin-RevId: 314175257
Issue: #5978 PiperOrigin-RevId: 315941765
- Deprecate constructors that don't take an executor, to direct developers toward the new ones. Callers can trivially pass Runnable::run to one of the new ones if they want old behaviour. - Add comment explaining warning suppression added in the CL that added parallelised download support. Issue: #5978 PiperOrigin-RevId: 318803296
This is fully implemented in Note: Additional commit not ref'd above: c9717f6 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Do you have release date for 2.12.0? Or raw estimation? |
We're actively working on burning down pending issues for 2.12. I think late August is probably our best estimate as of now. More generally, we'd like to avoid having so much work sitting around unreleased for a long time going forward, which has happened with |
Closing since this will be released in 2.12 any day now! |
ExoPlayer currently requests segments one at a time when downloading segmented media. This means that the network is not fully utilized, particularly in cases where there's significant latency between client and server. There are two approaches for improving this:
In both cases, any implementation should ensure that demuxed tracks are downloaded at approximately equal rates, because users expect that if something is ~33% downloaded, approximately the first ~33% of that piece of content will be playable.
The text was updated successfully, but these errors were encountered: