Home » Android » offline caching – Android ExoPlayer – downloading video (non DASH / HLS) and streaming at the same time

offline caching – Android ExoPlayer – downloading video (non DASH / HLS) and streaming at the same time

Posted by: admin May 14, 2020 Leave a comment

Questions:

I would like to download a video that is streaming in ExoPlayer.

As an aside and even before using ExoPlayer I downloaded a file from an input stream provided by HttpURLConnection and played the file from local storage. This is ok, however it does not solve my problem of simultaneous streaming and caching.

ExoPlayer also provides a caching system and these seem to work only for DASH or HLS stream types. I am using none of these and want to cache mp4 with the ExtractorRendererBuilder. (This topic is covered quite extensively here: https://github.com/google/ExoPlayer/issues/420).

DefaultHttpDataSource does have an api that exposes HttpURLConnection but I am not sure I am reusing the stream. Here is the code from the samples provided in ExoPlayer.

    @Override
    public void buildRenderers(DemoPlayer player) {
        Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
        Handler mainHandler = player.getMainHandler();

        // Build the video and audio renderers.
        DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, null);
        DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter,userAgent);
        ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri,dataSource,allocator,
                BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE, mainHandler, player, 0);
        MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
                sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
                mainHandler, player, 50);
        MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
                MediaCodecSelector.DEFAULT, null, true, mainHandler, player,
                AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
        TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player,
                mainHandler.getLooper());

        // Invoke the callback.
        TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT];
        renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
        renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
        renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
        player.onRenderers(renderers, bandwidthMeter);
    }

What I have done now is extend DefaultHttpDataSourceand use HttpURLConnection to get an InputStream, write to a file in getCacheDir(). Here is the class that extends DefaultHttpDataSource:

public class CachedHttpDataSource extends DefaultHttpDataSource {
    public CachedHttpDataSource(String userAgent, Predicate<String> contentTypePredicate) {
        super(userAgent, contentTypePredicate);
    }

    public CachedHttpDataSource(String userAgent, Predicate<String> contentTypePredicate, TransferListener listener) {
        super(userAgent, contentTypePredicate, listener);
    }

    public CachedHttpDataSource(String userAgent, Predicate<String> contentTypePredicate, TransferListener listener, int connectTimeoutMillis, int readTimeoutMillis) {
        super(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis);
    }

    public CachedHttpDataSource(String userAgent, Predicate<String> contentTypePredicate, TransferListener listener, int connectTimeoutMillis, int readTimeoutMillis, boolean allowCrossProtocolRedirects) {
        super(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis, allowCrossProtocolRedirects);
    }

    public HttpURLConnection getURLConnection(){
        HttpURLConnection connection = getConnection();

        return getConnection();
    }
}

I can now get an InputStream via getURLConnection() to save video into a file but I am not really happy using an InputStream again to cache the video. Is there any other API/ or method that gives access to a byte array that I can write to a file while streaming takes place ?

My other stackoverflow searches have not given a solution yet:

Using cache in ExoPlayer

ExoPlayer cache

Thanks for your time and patience.

How to&Answers:

I found a solution with just an issue: seeking backward or forward is not working.

This is the piece of code:

public void preparePlayer(String videoUri) {
    MediaSource videoSource =
            new ExtractorMediaSource( Uri.parse( videoUri ), dataSourceFactory, extractorsFactory, handler, null );
    exoPlayer.prepare( videoSource );
    exoPlayer.setPlayWhenReady( true );
}

public DataSource.Factory buildDataSourceFactory() {
    return new DataSource.Factory() {
        @Override
        public DataSource createDataSource() {
            LeastRecentlyUsedCacheEvictor evictor = new LeastRecentlyUsedCacheEvictor( CACHE_SIZE_BYTES );
            File cacheDir = //Your cache dir
            SimpleCache simpleCache = new SimpleCache( cacheDir, evictor );
            DataSource dataSource = buildMyDataSourceFactory().createDataSource();
            int cacheFlags = CacheDataSource.FLAG_BLOCK_ON_CACHE | CacheDataSource.FLAG_CACHE_UNBOUNDED_REQUESTS;
            return new CacheDataSource( simpleCache, dataSource, cacheFlags, CACHE_SIZE_BYTES );
        }
    };
}

private DefaultDataSource.Factory buildMyDataSourceFactory() {
    return new DefaultDataSourceFactory( context, "jesty-android", new DefaultBandwidthMeter() );
}

Source: https://github.com/google/ExoPlayer/issues/420#issuecomment-244652023