Can I use the Android SDK to download videos from a source that has no internet access?

I’m currently integrating the Bitmovin Player into a mobile app that connects to the Wi-Fi access point of a kiosk that does not provide internet access. Part of the service offered by the app is to allow our users to download content from the kiosk as well.

The team has done a great job adding the Bitmovin player into our mobile app, and we have successfully tested streaming from the kiosk with no issues.

We are now using the Bitmovin SDK to add the functionality to allow a user to download a video from the kiosk.

We are, however, hitting a barrier. In the scenario that one of our customers, who has no internet access on their phone, and there is no internet access from our kiosk, chooses to download a video, it seems that the SDK makes a request to the Bitmovin licensing server, which is timing out (obviously if there’s no internet access) and downloading is unable to take place.

Would it be possible to clarify with your team that, given we have a valid license key in our app, we are able to download videos from a kiosk that has no internet access using the Bitmovin SDK?

Any assistance here would be greatly appreciated.

1 Like

Hi Andrew. Welcome :wave:

Interesting question. Can your users install your app with no internet and be offline throughout their usage?

Based on your answer, I can check if this is supported by the Android SDK. If it’s not, we can turn this into a feature request and figure out a temporary solution for you.

Thanks for the welcome, Matheus.

The majority of our users will be able to install our app with no internet access. More often than not, they get the app by tethering to another device, or they will download an APK directly from the offline kiosk. Our users will be offline throughout their usage.

Any workarounds or suggestions would be greatly appreciated.

Hi @andrew.mccaughan

We have done a few tests today using our Android sample Offline app (bitmovin-player-android-samples/OfflinePlayback at main · bitmovin/bitmovin-player-android-samples · GitHub) - and at the same time blocking access to licensing.bitmovin.com - and unfortunately, we are not able to replicate the same error that you are seeing. Can you confirm if the request you notice timing out is requesting this address? Would it be possible for you to send over a network log?

I will continue to try and setup an environment similar to yours (internal network - but no public Internet) and test via the sample app. However - would it be possible for you to check if you see the same error when using the above sample app?

You should be able to test by changing the URLs of the non-drm asset:

        // Initialize a SourceConfig
        SourceConfig artOfMotion = new SourceConfig("https://bitmovin-a.akamaihd.net/content/MI201109210084_1/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd", SourceType.Dash);
        artOfMotion.setThumbnailTrack(new ThumbnailTrack("https://bitmovin-a.akamaihd.net/content/MI201109210084_1/thumbnails/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.vtt"));
        artOfMotion.setTitle("Art of Motion");

BR
Peder

Hi @peder.borg

Thanks for looking into my issue. The problem appears to be when we try to initiate a download from the kiosk in offline mode. In our Android app, we have a method that is called when a user requests to download a video:

public void downloadVideo(String videoManifestUrl) {
            SourceConfig sourceConfig = new SourceConfig(videoManifestUrl, SourceType.Dash);
            OfflineContentManager offlineContentManager = OfflineContentManager.getOfflineContentManager(sourceConfig,
                    _file.getPath(),
                    videoManifestUrl,
                    this,
                    _context);
            offlineContentManager.getOptions();
}

In our overridden onOptionsAvailable method, we have the following:

    // ...
            entry.setAction(OfflineOptionEntryAction.Download);
            offlineContentManager.process(offlineContentOptions);
    // ...

Based on our TCP dump from our emulator, we believe at this point a call to licensing.bitmovin.com is hanging or is failing silently.

Here’s that segment of TCP dump from our app:

13:00:17.531863 IP 10.0.2.16.30895 > 10.0.2.3.domain: 43527+ AAAA? licensing.bitmovin.com. (40)
13:00:17.589062 IP 10.0.2.16.18394 > 10.0.2.3.domain: 63146+ A? licensing.bitmovin.com. (40)
13:00:19.529317 ARP, Request who-has 10.0.2.3 tell 10.0.2.16, length 28
13:01:07.729347 IP6 fe80::2 > ff02::1: ICMP6, router advertisement, length 56
13:07:35.886286 IP6 fe80::2 > ff02::1: ICMP6, router advertisement, length 56
13:14:59.388960 IP6 fe80::2 > ff02::1: ICMP6, router advertisement, length 56
13:18:01.845379 IP 10.0.2.16.7485 > 10.0.2.3.domain: 64133+ AAAA? play.googleapis.com. (37)
13:18:01.882870 IP 10.0.2.16.2171 > 10.0.2.3.domain: 10972+ A? play.googleapis.com. (37)
13:18:03.869154 IP 10.0.2.16.6142 > 10.0.2.3.domain: 59701+ A? accounts.google.com. (37)
13:18:03.870939 IP 10.0.2.16.63654 > 10.0.2.3.domain: 26687+ AAAA? accounts.google.com. (37)

As you suggested, I have run the OfflinePlayback app from the samples repo on an Android emulator.

To set up the environment, I blocked licensing.bitmovin.com on my network through a Pi-hole. Next, I attempted to download one of the video files listed in the app. The network inspector in Android Studio records a request being made to licensing.bitmovin.com but does not show the request being completed. And downloading fails.

Logcat also shows the following error being thrown in the app:

2022-05-30 23:25:49.503 13472-13542/com.bitmovin.player.samples.offline.playback E/DownloadManager: Task failed: application/dash+xml:/data/user/0/com.bitmovin.player.samples.offline.playback/app_offline/YXJ0T2ZNb3Rpb240:0:0, false
      java.lang.NullPointerException: wrapped.getRequestProperty(field) must not be null
        at com.android.tools.appinspection.network.httpurl.TrackedHttpURLConnection.getRequestProperty(TrackedHttpURLConnection.kt:235)
        at com.android.tools.appinspection.network.httpurl.HttpsURLConnectionWrapper.getRequestProperty(HttpsURLConnectionWrapper.kt:274)
        at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:625)
        at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:517)
        at com.bitmovin.player.q0.s.a(SourceFile:10)
        at com.bitmovin.player.q0.s.makeConnection(SourceFile:2)
        at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:359)
        at com.bitmovin.player.q0.s.open(SourceFile:4)
        at com.bitmovin.player.q0.e.open(SourceFile:12)
        at com.google.android.exoplayer2.upstream.TeeDataSource.open(TeeDataSource.java:52)
        at com.google.android.exoplayer2.upstream.cache.CacheDataSource.openNextSource(CacheDataSource.java:776)
        at com.google.android.exoplayer2.upstream.cache.CacheDataSource.open(CacheDataSource.java:589)
        at com.google.android.exoplayer2.upstream.cache.CacheWriter.readBlockToCache(CacheWriter.java:172)
        at com.google.android.exoplayer2.upstream.cache.CacheWriter.cache(CacheWriter.java:134)
        at com.google.android.exoplayer2.offline.SegmentDownloader$SegmentDownloadRunnable.doWork(SegmentDownloader.java:477)
        at com.google.android.exoplayer2.offline.SegmentDownloader$SegmentDownloadRunnable.doWork(SegmentDownloader.java:454)
        at com.google.android.exoplayer2.util.RunnableFutureTask.run(RunnableFutureTask.java:125)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

Hopefully, this information is useful.

Hi @andrew.mccaughan

Thank you for posting your findings.

One of my colleagues and I did independent tests yesterday, and came to the same conclusion as to why the download fails. Additionally my colleague was able to also provide a workaround, which hopefully also helps within your application.

Network setup
Setup a local HTTP server(Ngnix) on Mac connected to Wifi network at my home
Android phone connected to same Wifi network as Mac
Removed the network cable from Wifi router.
This should mimic the setup for your system/application.

Test
Downloaded and hosted Art of Motion progressive MP4 file on the Nginx HTTP server setup on Mac
On Android device installed the Offline sample application and attempted download.
The Offline sample app sent a license request to license.bitmovin.com and the download does not start until this request is in progress. I confirmed from code that for offline download, Bitmovin SDK wait for request to either succeed or fail before starting download.
The license request fails with HTTP 503 after some time as DNS for license.bitmovin.com could not be resolved.
The download is started after license request fails but it gets stuck at 0%
It does not progress even after a long wait.

Root Cause
Bitmovin download is based on Exo player downloader which allows applications to set some requirements which govern when the download is allowed and when not. Seem that Requirements.Network is the default configuration which expects internet access for download to start.

Solution
Good news is that this is configurable using OfflineConfig.requirements field. After setting it as empty using following code in OnCreate method of MainActivity the offline sample application works. Please see sample code below.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // set OfflineConfig.requirements as empty to
        // allow download in local network when no internet
        val offlineConfig = OfflineConfig()
        offlineConfig.requirements = Requirements(0)
        OfflineContentManager.setOfflineConfig(this, offlineConfig)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
}

I hope this helps.

2 Likes