Hi,
Is download and/or progressive download of adaptive streaming presentations a feature that is planned for ExoPlayer?
Note I am referring to 'up front' acquisition of the content rather than local HTTP caching.
Thanks.
Hi @kidmiracleman, yes we're working on DASH downloading currently. It should be published in a few weeks. HLS and SmoothStreaming support will follow.
@erdemguven Thank you for the update. Looking forward to the release...
Let's use this as an overall tracking issue for offline support.
So, are you going to release any offline donwload manager kind of thing
Can you give me a hint as to what interface you are providing to show the progress of the download?
@ram992 yes there will be a download manager.
@KiminRyu There will be a listener interface and broadcast events depending on the download method you use. int totalSegments, int downloadedSegments, long downloadedBytes are provided but this might change.
Wow, but I had to implement all these things on my own,
but thanks for inducing such incredible features, might help other fresh exo guys to use them.
Can you say anything on when to expect the download Manager. Casue I want to use that.
@ram992 Sorry I can't as it's hard to estimate at the moment.
I've recently configured this on iOS, you may of already seen it, but it could be a good resource for you to reference - https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/MediaPlaybackGuide/Contents/Resources/en.lproj/HTTPLiveStreaming/HTTPLiveStreaming.html
sure no problem,
Can you make a comment once it is available in dev branch
@erdemguven Is this commit to meet the dependency? I want to know how roughly it is completed!
https://github.com/google/ExoPlayer/commit/42e4100c0511b8dbd405f143e0d8aca84e2ea50b
No. We will update this issue when significant progress is made. You may see some commits that look odd from time to time, where the commit message and actual content of the commit don't align well. This includes the one you reference. This is a side effect of the way we use internal tools for development work + mirror to GitHub.
Any updates on this @ojw28 ? Just deciding if we need to code this ourselves or if we should wait
I'll keep tracking this issue. Once implemented, I'll create a demo and full "doc" so everyone can use it with full offline mode and cache using DASH or similar.
@mkiisoft @ojw28
I need this feature in my app, can you guys provide us some info by when can we get the offline mode?
@hishamMuneer I think you can do this yourself if you need it for DASH content, as in the latest exoplayer there is a class OfflineLicenseHelper to handle license issues
@ram992 I dont have licence issues, for now I am trying to download the video for offline viewing, even if it is not protected by licence. A single mp4 file can be downloaded easily but what about a segmented dashed file. https://stackoverflow.com/questions/45418817/saving-a-video-offline-while-playing-in-exoplayer
@hishamMuneer One question : why do you want to use a segmented DASH for offline use,
This stuff is for online and to switch bitrate dynamically (depending on the network speed).
And a single mp4 video file is enough for offline case as there will be any network calls.
Any way the problem with DASH formatt (as I know) is that it will have all the audio and video files with in the manifest and if you want to play the segmented one you need to have all of them available in some form(online/offline).
So make a call. Download all the mp4 files or simply ask the user what quality he wants and download them.
@ram992 But that will double my server size as I have to store both full mp4 and segmented files of the same video. Right now i have segmented files (along with mpd) stored on server which i am able to play without any problem.
I know, I faced that problem, but I don't know any other way. That is how the DASH formatt works.
I hope someone from google can help us in this matter I guess. And I think all other famous stream apps does the same thing.
@mkiisoft @ojw28 Please guide how should I proceed? "Right now i have segmented files (along with mpd) stored on server which i am able to play without any problem. But how segmented videos running through mpd files can be saved offline for later use. Videos are unencrypted for now and no DRM is being used."
@hishamMuneer We're planning to release some of the downloaders in August. We'll update this thread when it's done. You'll be able to download your DASH streams.
Hello @erdemguven, can I know how it is possible to download a segmented DASH file (Multiple ones) for offline use.
And am I assuming correctly for a Single mp4 file we get the DASH manifest give it to a parser extract all the video url's and pic what is best for us and leave the rest
Any chance HLS is also in that list?
On Aug 1, 2017, at 06:03, Erdem Guven notifications@github.com wrote:
@hishamMuneer We're planning to release some of the downloaders in August. We'll update this thread when it's done. You'll be able to download your DASH streams.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.
@ram992 Using DASH downloader you'll be able to select the representations you want and download them.
@matclayton HLS is WIP, it might be released as well.
@erdemguven Thanks for the update.
@hishamMuneer I think you got your answer, I'll wait for the release
@erdemguven amazing thanks!
Ahah, I wish I saw this thread before taking 2 full weeks to implement HLS download !
Would love to see your implementation @q-litzler
@bnussey
There are three kinds of files to consider when downloading HLS:
1) Download the master playlist and write its content to a local file.
2) Download the media playlists referenced in the master playlist, write its content to a local file.
Before writing said content into local files, don't forget to replace remote urls by local uri.
3) Download binaries and write its content to a local file.
When creating a file and writing content to it, make sure it has the proper uri --> the one you are replacing the urls with.
To play a local HLS playlist instead of a remote one:
Easier said than done of course.
Using Kotlin and coroutines made handling all the asynchronous parts enjoyable
@erdemguven or @ojw28 Why is the OfflineLicenseHelper class got changed. Previously I was using ExoPlayer v2.2.0, and I just had to pass the httpStream object and the DashManifestUrl that I want to download the license for, now in the latest ExoPlayer v2.5.1 the class is changed and now the method downloadFile()( previously in 2.2.0 it was just download() ) is asking for DrmInitData.
Where can I get the DrmInitData from ??? is it available within the exoplayer or I have to get the DashManifest, parse it and then extract the data from it.
@ram992 you can use com.google.android.exoplayer2.source.dash.DashUtil#loadDrmInitData(DataSource dataSource, Period period)
Don't use loadDrmInitData(DataSource dataSource, DashManifest dashManifest) as it's about to be removed.
Thanks @erdemguven for the info. If you say that loadDrmInitData(DataSource dataSource, DashManifest dashManifest) this method is to be removed, are you going to make any more releases to v2.5.x or will be a new v2.x.x one, cause I will wait if it is 2.5.x
@erdemguven I'm planning to use this functionality soon. Download DASH DRM protected content and store it for some time.
The downloaders that you are implementing seems the right choice to do it, just a few questions:
@erdemguven Do you have any updates? Is there a chance that downloading of HLS streams will be released during September?
Thanks!
There are already DashDownloader, HlsDownloader, SsDownloader and ProgressiveDownloader components in the dev-v2 branch, which can be used to perform the required downloads for each stream type. You can use these directly on a background thread.
Our next steps are to (a) introduce a DownloadManager for managing the downloads, and (b) wrapping that in a DownloadService that can accept instructions via Intents.
@ojw28 I have Downloaded the Dash stream to the local file using the DashDownloaderas mentioned in the above comment. But I don't know how to use the cached data to play content.
I have tried using ExtractorMediaSource like below:
```
new ExtractorMediaSource(
uri,
new CacheDataSourceFactory(
cache, getHttpDataSourceFactory(false), CacheDataSource.FLAG_BLOCK_ON_CACHE),
new DefaultExtractorsFactory(),
null,
null);
and also DashMediaSource:
```
new DashMediaSource(
uri,
new CacheDataSourceFactory(
cache, getHttpDataSourceFactory(false), CacheDataSource.FLAG_BLOCK_ON_CACHE),
new DefaultDashChunkSource.Factory(getDataSourceFactory(false)),
null,
null);
Neither of the implementation is playing and also don't know whether above implementation is correct or not.
It would be helpful if you tell me how to use the CacheDataSourceto play the video Offline.
Thanks in advance.
Hey @mraj0045 have you tried using instructions from @q-litzler above
@bnussey, @erdemguven I tried using the instructions you pointed out for the Sample URL: http://yt-dash-mse-test.commondatastorage.googleapis.com/media/feelings_vp9-20130806-manifest.mpd. It is working perfectly.
But in my application, it is not working. It is throwing,
I/ExoPlayerImpl: Init bb98758 [ExoPlayerLib/2.5.2] [lux_uds, XT1562, motorola, 23]
D/EventLogger: state [0.00, true, I]
D/EventLogger: state [0.09, true, B]
E/EventLogger: internalError [0.18, loadError]
E/EventLogger: internalError [0.18, loadError]
D/OpenGLRenderer: endAllActiveAnimators on 0xb8b759d8 (RippleDrawable) with handle 0xb877b5c8
E/EventLogger: internalError [1.07, loadError]
E/EventLogger: internalError [3.08, loadError]
E/ExoPlayerImplInternal: Source error.
E/EventLogger: playerFailed [3.08]
D/EventLogger: state [3.08, true, I]
The difference with the Sample URLand my URL is,
Sample URL not DRM protected and mine is DRM protected.
Sample URL contains multiple video formats only and mine have both multiple video and audio URL.
My Code:
private DataSource.Factory getDataSourceFactory(boolean useBandwidthMeter) {
File file = Utility.getOfflineDir(this, "test");
SimpleCache cache = new SimpleCache(file, new NoOpCacheEvictor());
return new CacheDataSourceFactory(
cache, new DefaultDataSourceFactory(this, userAgent), CacheDataSource.FLAG_BLOCK_ON_CACHE);
}
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
int type =
TextUtils.isEmpty(overrideExtension)
? Util.inferContentType(uri)
: Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_DASH:
return new DashMediaSource(
uri,
mediaDataSourceFactory,
new DefaultDashChunkSource.Factory(getDataSourceFactory(false)),
mHandler,
eventLogger);
case C.TYPE_OTHER:
return new ExtractorMediaSource(
uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), mHandler, eventLogger);
default:
{
throw new IllegalStateException("Unsupported type: " + type);
}
}
}
public DefaultRenderersFactory getRenderersFactory() {
UUID drmSchemeUuid = C.WIDEVINE_UUID;
if (drmSchemeUuid != null) {
// PersistentCookieStore persistentCookieStore = new PersistentCookieStore(this);
// List<HttpCookie> cookies = persistentCookieStore.getCookies();
// HashMap<String, String> keyRequestProperties;
// if (cookies.isEmpty()) {
// keyRequestProperties = null;
// } else {
// keyRequestProperties = new HashMap<>();
// String phpSession = "";
// for (HttpCookie cookie : cookies) {
// phpSession = cookie.getName() + "=" + cookie.getValue();
// Log.e("Session", phpSession);
// }
// keyRequestProperties.put("Cookie", phpSession);
// }
// boolean preferExtensionDecoders = false;
@DefaultRenderersFactory.ExtensionRendererMode
int extensionRendererMode = DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER;
try {
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
drmSessionManager =
buildDrmSessionManager(drmSchemeUuid, "https://widevine-proxy.appspot.com/proxy", null);
return new DefaultRenderersFactory(this, drmSessionManager, extensionRendererMode);
} catch (UnsupportedDrmException | DrmSession.DrmSessionException e) {
e.printStackTrace();
}
}
return new DefaultRenderersFactory(this);
}
private DrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManager(
UUID uuid, String licenseUrl, HashMap<String, String> keyRequestPropertiesArray)
throws UnsupportedDrmException, DrmSession.DrmSessionException {
HttpMediaDrmCallback drmCallback =
new HttpMediaDrmCallback(licenseUrl, getHttpDataSourceFactory(false));
if (keyRequestPropertiesArray != null)
for (Map.Entry<String, String> entry : keyRequestPropertiesArray.entrySet()) {
drmCallback.setKeyRequestProperty(entry.getKey(), entry.getValue());
}
drmSessionManager =
new DefaultDrmSessionManager<>(
uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback, null, mHandler, this);
drmSessionManager.setMode(
DefaultDrmSessionManager.MODE_QUERY, Utility.decode("a3NpZEIxNkE5MTU1"));
// Utility.decode("a3NpZEY0MDc0MzBE")
// offlineLicenseHelper.downloadLicense()
return drmSessionManager;
}
Need help in implementing it. Thanks in advance.
@ojw28 Do you mean 'DownloadManager' is from android framework? or own implementation on this library?
@mraj0045 could you please share how did you manage to use the CacheDataSource to play the video Offline? I am constructing DashMediaSource after I have downloaded it this way:
new DashMediaSource(Uri.parse(DASH_TEST_URL), getDataSourceFactory(false),
new DefaultDashChunkSource.Factory(getDataSourceFactory(false)), mainHandler,
eventLogger);
private DataSource.Factory getDataSourceFactory(boolean useBandwidthMeter) {
return new CacheDataSourceFactory(
cache, buildDataSourceFactory(useBandwidthMeter), 0);
}
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
return buildDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null);
}
private DataSource.Factory buildDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) {
return new DefaultDataSourceFactory(this, bandwidthMeter,
buildHttpDataSourceFactory(bandwidthMeter));
}
private HttpDataSource.Factory buildHttpDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) {
return new DefaultHttpDataSourceFactory(userAgent, bandwidthMeter);
}
where cache is the one I used to download previously.
How to download DASH DRM video on custom button click instead of caching it while playing?
1.How DRM enabled video are downloaded using exoplayer methods?
2.Where do we store and maintain the list downloaded videos?
Hi all,
Can someone share/refer code for downloading video and playing the same video in offline mode. I went through the demo and able to play the contents online. But I want to learn how to download the contents and play offline. My video url will be a MP4 file , no constraints of license . Some guidance will be appreciated . Thanks in advanced.
@rabindrachhachan You may find Erdem's blog post on pre-caching/downloading progressive streams useful.
Hi all,
I highly appreciate the effort you put into developing this great library, but I'm a bit confused on how to use the offline functionality:
Thanks in advance!
@FD- CacheUtils can be used to download any single content, ProgressiveDownloader uses it to download progressive streams. To download adaptive streams, you need to use Downloaders which handles multiple segments in the stream automatically. You can also use DownloadService for background downloads. DownloadService uses DownloadManager which handles multiple downloads and continue download when the app restarts. It's also possible to configure the downloads to be run only when there is wifi or the device is charging. As you can see Downloaders are for more complex cases.
A demo app showing how to use DownloadService will be released in a few weeks.
@erdemguven Thanks for the clarification! I managed to implement caching for HLS and DASH. For selecting a subset of available qualities I use HlsPlaylistParser and DashManifestParser to parse the manifests, then select qualities. For later only playing the cached qualities, I use custom parsers that filter the manifest using the copy() function. Works great!
One thing I'd love to see supported is downloading parts of live streams. I've created a new feature request for that (#3877).
Please see my Downloading Streams blog post on Medium.
I tried to integrate new offline feature with locally imported ExoPlayer. Do you have any plan to improve download speed? The average download speed is measured as 1MB(or lower) per second. Dash Downloader's implementation is designed to receive chunks sequentially so that downloads are slow even if the internet speed is fast. Although simultaneous downloads are possible (currently set to 4 in consideration of background service restrictions), this does not seem to improve user experience. It took 40 minutes to download a 50 minute 720p video. The total downloaded bytes are 700MB. It is clear that it takes 40 minutes to receive 700MB to be improved. I used Giga WiFi and Galaxy s9 Plus as download environment.
@erdemguven thanks for the post on Medium.
-Any updates about Live stream download?
If I'm not mistaken, it is going to be handled by this class: ProgressiveDownloader.java ? it seems method download() is not yet fully implemented to prepare downloaders for the next non yet existing segments.
-And could you define "Progressive Stream"? are you referring to a live Streaming with segments that do not exist yet? (I could only find clear definitions for "Streaming" and "Progressive Download")
@erdemguven is out on holiday at the moment. To answer your questions:
Any updates about Live stream download?
No, sorry
If I'm not mistaken, it is going to be handled by this class: ProgressiveDownloader.java ? it seems method download() is not yet fully implemented to prepare downloaders for the next non yet existing segments. And could you define "Progressive Stream"? are you referring to a live Streaming with segments that do not exist yet? (I could only find clear definitions for "Streaming" and "Progressive Download")
This is not correct. ProgressDownloader is just for any regular media file (i.e. not DASH, SmoothStreaming or HLS). For example an mp3 file.
ok, thanks @ojw28
@KiminRyu Maybe that can help you: I had the same download issue, it was extremly slow, and actually it was not related to the DashDownloader or the connection speed! I just replaced my external storage by a faster one and I just downloaded 1GB in 4min
@kvillnv
Thank you for answer. I also dig up a lot about this issue, but it seems like our media cdn is a little slow. Have you ever done a remove action? This is separate from the network, and the manifest I'm using for testing has about 2,000 to 3,000 chunks. As Cache erased the chunks one by one, it took quite a while to delete them. I hope it will improve in this part as well.
@ojw28 @erdemguven is there any way to limit the bandwidth in the download service?
If I need to play a stream while I am downloading, on small connections it might be an issue.
@KiminRyu yes I have done remove actions, it is not instantaneous but it's not that long.
It seems that when the cache becomes voluminous it becomes slower to start playback:
(I only downloaded Dash streams, with only 1 audio and 1 video representation)
When I have for example 2 medias or less already downloaded in the cache, I play one of them, it starts instantly, no problem.
But when I have more, my player shows a black screen for a few seconds before starting. The more medias in cache the longer it is. Up to 45 seconds, when I have a dozen of medias.
Here is how I instantiate the cache and MediaSource for reading:
val upstreamDataSourceFactory = DefaultDataSourceFactory(this, userAgent)
cacheFile = RecorderService.getCacheFile(this)
val cacheDataSourceFactory = CacheDataSourceFactory(cacheFile,upstreamDataSourceFactory)
return DashMediaSource.Factory(
DefaultDashChunkSource.Factory(cacheDataSourceFactory), cacheDataSourceFactory)
.setManifestParser(FilteringManifestParser(parser, keys))
.createMediaSource(mediaUri)
Here is my method to get the cacheFile:
fun getCacheFile(context: Context) = SimpleCache(File(getMediaFolder(context), CACHE_FOLDER), NoOpCacheEvictor())
On the CacheDataSourceFactory I also tried to use CacheDataSource.FLAG_BLOCK_ON_CACHE and to set the maxBytes, I didn't notice a difference
Everything is working full offline (DRM and Media)
Any suggestions?
Thanks
@kvillnv How about follow this way to initiate CacheDataSourceFactory? It is from the latest release(2.8.0)'s demo application. It works fine even I downloaded more than 20 dash streams. uhmm...and I think you will get SimpleCache Error(it should be singleton over application scope. It is updated just a couple of days ago) if you get SimpleCache like that. I recommend to provide your simple cache in application scope by dagger or something.
val appComponent = (application as ECApplication).component()
val downloadCache = if (useExternal) appComponent.externalCache() else appComponent.internalCache()
val component: MediaDownloadComponent = DaggerMediaDownloadComponent.builder()
.mediaDownloadModule(MediaDownloadModule(this, downloadCache, useExternal))
.build()
component.inject(this)
Here is my example for injecting application scope SimpleCache to MediaDownloadService(which I implement DownloadService!)
@KiminRyu thank you, problem solved by reviewing my SimpleCache
@ojw28 @erdemguven is there any way to limit the bandwidth in the download service?
If I need to play a stream while I am downloading, on small connections it might be an issue.
@kvillnv, please look at PriorityTaskManager. If you create a PriorityTaskManager and use it with your DefaultLoadControl and DownloadManager (you should pass it DownloaderConstructorHelper and use it while contructing DownloadManager), it will make downloads pause while player is buffering.
Closing this issue as now we support downloading adaptive streams. Please open separate issue for future problems.
@erdemguven is there any support to download and offline play protected DRM content? I've been trying the Exoplayer2 recent demo. Added my urls to the assets, but on download I got This demo app does not support downloading protected content. However I made some changes in the example and managed to download videos, but when I try to play the downloaded videos, the player shows video duration, video play progress is also moving but there is no audio and video.
05-28 16:45:32.436 9006-9204/sonmobile.schoolofnet.com W/AudioCapabilities: Unsupported mime audio/ape
05-28 16:45:32.438 9006-9204/sonmobile.schoolofnet.com W/AudioCapabilities: Unsupported mime audio/x-adpcm-ms
05-28 16:45:32.439 9006-9204/sonmobile.schoolofnet.com W/AudioCapabilities: Unsupported mime audio/x-adpcm-dvi-ima
05-28 16:45:32.445 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unrecognized profile/level 1/32 for video/mp4v-es
05-28 16:45:32.445 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unrecognized profile/level 32768/2 for video/mp4v-es
05-28 16:45:32.445 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unrecognized profile/level 32768/64 for video/mp4v-es
05-28 16:45:32.458 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unsupported mime video/x-ms-wmv
05-28 16:45:32.462 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unsupported mime video/divx
05-28 16:45:32.465 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unsupported mime video/divx3
05-28 16:45:32.469 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unsupported mime video/xvid
05-28 16:45:32.472 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unsupported mime video/flv1
05-28 16:45:32.475 9006-9204/sonmobile.schoolofnet.com W/VideoCapabilities: Unrecognized profile/level 1/32 for video/mp4v-es
05-28 16:45:32.528 9006-9204/sonmobile.schoolofnet.com I/VideoCapabilities: Unsupported profile 4 for video/mp4v-es
To be able to play offline DRM protected content you need to download licenses too. You can use OfflineLicenseHelper to download the licenses. There can be multiple licenses. To keep the demo app simple we didn't deliberately disabled downloading DRM content.
If this answer isn't enough please open a new issue with all information asked in the template.
Most helpful comment
@bnussey
There are three kinds of files to consider when downloading HLS:
1) Download the master playlist and write its content to a local file.
2) Download the media playlists referenced in the master playlist, write its content to a local file.
Before writing said content into local files, don't forget to replace remote urls by local uri.
3) Download binaries and write its content to a local file.
When creating a file and writing content to it, make sure it has the proper uri --> the one you are replacing the urls with.
To play a local HLS playlist instead of a remote one:
Easier said than done of course.
Using Kotlin and coroutines made handling all the asynchronous parts enjoyable