Search code examples
androidcachingvideo-streamingexoplayerexoplayer2.x

DummyDataSource cannot be opened : Android ExoPlayer


I am trying to stream videos from URLs and save them in cache during the same time.

I am using the DataBaseInspector of Android Studio to check the cache and it seems to work but not all the time.

Sometimes i have this error when i try to play a video :

2021-01-21 10:27:12.956 26648-28069/test.com.test E/ExoPlayerImplInternal: Playback error
      com.google.android.exoplayer2.ExoPlaybackException: Source error
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:554)
        at android.os.Handler.dispatchMessage(Handler.java:103)
        at android.os.Looper.loop(Looper.java:238)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.io.IOException: DummyDataSource cannot be opened
        at com.google.android.exoplayer2.upstream.DummyDataSource.open(DummyDataSource.java:39)
        at com.google.android.exoplayer2.upstream.cache.CacheDataSource.openNextSource(CacheDataSource.java:764)
        at com.google.android.exoplayer2.upstream.cache.CacheDataSource.open(CacheDataSource.java:579)
        at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:84)
        at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1017)
        at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:415)
        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:919)

Reading the error we can see an IOException, so i have checked the path in cache and again it's working sometimes... The keys i register in the exoplayer internal db are the URLs (i let the exoplayer do all the job but i checked myself after).

Here is my code :

    @Inject
    public VideoCacheManager(@NonNull SimpleCache simpleCache, @ActivityContext Context context) {
        this.simpleCache = simpleCache;
        this.context = context;
        cacheDataSource = new CacheDataSource.Factory();
        cacheDataSource.setCache(simpleCache);
        Log.d(TAG, "VideoCacheManager: cache size left : " + simpleCache.getCacheSpace());
        cacheDataSource.setEventListener(new CacheDataSource.EventListener() {
            @Override
            public void onCachedBytesRead(long cacheSizeBytes, long cachedBytesRead) {
                Log.d(TAG, "onCachedBytesRead: cached bytes read : " + cachedBytesRead);
            }

            @Override
            public void onCacheIgnored(int reason) {
                Log.d(TAG, "onCacheIgnored: cache ignored : " + reason);
            }
        });
    }

    public MediaSource buildMediaSource(MediaItem mediaItem) {
        return new ProgressiveMediaSource.Factory(cacheDataSource).createMediaSource(mediaItem);
    }

And SimpleCache is a Singleton injected with Hilt :

    @Provides
    @Singleton
    public static SimpleCache simpleCache(@ApplicationContext Context context) {
        return new SimpleCache(context.getApplicationContext().getCacheDir(), leastRecentlyUsedCacheEvictor(), exoDatabaseProvider(context));
    }

    @Provides
    @Singleton
    public static LeastRecentlyUsedCacheEvictor leastRecentlyUsedCacheEvictor() {
        return new LeastRecentlyUsedCacheEvictor(exoPlayerCacheSize());
    }

    @Provides
    @Singleton
    public static Long exoPlayerCacheSize() {
        return (long) (500 * 1024 * 1024);
    }

    @Provides
    @Singleton
    public static ExoDatabaseProvider exoDatabaseProvider(@ApplicationContext Context context) {
        return new ExoDatabaseProvider(context);
    }

And in my ExoPlayer class i have a simple method :

    public void prepare(MediaItem mediaItem) {
        lastMediaItem = mediaItem;
        Log.d(TAG, "prepare: media item : " + mediaItem.playbackProperties);
        MediaSource mediaSource = videoCacheManager.buildMediaSource(mediaItem);
        Log.d(TAG, "prepare: source : " + mediaSource.getInitialTimeline());
        simpleExoPlayer.prepare(mediaSource);
    }

I have read enough about the exoplayer and it is supposed to do download to the cache the video if it does not exist, during the first streaming session... So what is this error and how to fix it ?

The first time i have used this code it worked, then i have cleared everything and retried without success.


Solution

  • Okay i simply must have removed the DefaultDataSourceFactory by mistake and forgot to set back an UpstreamDataSource to the CacheDataSource instance.

    Everything works fine now, hope this helps others.

    In code we need to add this :

    DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(context, Util.getUserAgent(context, context.getString(R.string.app_name)));
    cacheDataSource.setUpstreamDataSourceFactory(defaultDataSourceFactory);
    

    I have also learn a lot about ExoPlayer watching this video, despite it was in 2018 with the exoplayer version 2.8.

    I am using the latest at the moment, meaning 2.12.3.