CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-androidx-media3--media3-exoplayer

ExoPlayer module that provides the core ExoPlayer implementation for local media playback on Android, supporting various media formats and streaming protocols

Pending
Overview
Eval results
Files

offline-support.mddocs/

Offline Support

ExoPlayer provides comprehensive offline playback capabilities through download management, enabling users to download media content for offline viewing. The offline system handles adaptive stream downloads, progress tracking, and resume functionality.

DownloadManager

The central component that manages media downloads for offline playback.

public final class DownloadManager {
    /**
     * Listener for download manager events.
     */
    public interface Listener {
        /**
         * Called when the download state changes.
         * 
         * @param downloadManager The download manager
         * @param download The download
         * @param finalException The final exception, if the download failed
         */
        default void onDownloadChanged(DownloadManager downloadManager, Download download, @Nullable Exception finalException) {}
        
        /**
         * Called when downloads are removed.
         * 
         * @param downloadManager The download manager
         * @param downloads The removed downloads
         */
        default void onDownloadRemoved(DownloadManager downloadManager, Download download) {}
        
        /**
         * Called when the requirements state changes.
         * 
         * @param downloadManager The download manager
         * @param requirements The requirements
         * @param notMetRequirements The requirements that are not met
         */
        default void onRequirementsStateChanged(DownloadManager downloadManager, Requirements requirements, @RequirementsWatcher.RequirementFlags int notMetRequirements) {}
        
        /**
         * Called when waiting for requirements changes.
         * 
         * @param downloadManager The download manager
         * @param waitingForRequirements Whether waiting for requirements
         */
        default void onWaitingForRequirementsChanged(DownloadManager downloadManager, boolean waitingForRequirements) {}
        
        /**
         * Called when the idle state changes.
         * 
         * @param downloadManager The download manager
         * @param idle Whether the download manager is idle
         */
        default void onIdle(DownloadManager downloadManager, boolean idle) {}
    }
    
    /**
     * Creates a DownloadManager.
     * 
     * @param context The context
     * @param downloadIndex The download index
     * @param downloaderFactory The downloader factory
     */
    public DownloadManager(Context context, DownloadIndex downloadIndex, DownloaderFactory downloaderFactory);
    
    /**
     * Creates a DownloadManager with requirements.
     * 
     * @param context The context
     * @param downloadIndex The download index
     * @param downloaderFactory The downloader factory
     * @param requirements The requirements for downloads
     * @param executor The executor for download operations
     */
    public DownloadManager(Context context, DownloadIndex downloadIndex, DownloaderFactory downloaderFactory,
                          Requirements requirements, Executor executor);
    
    /**
     * Adds a listener.
     * 
     * @param listener The listener to add
     */
    public void addListener(Listener listener);
    
    /**
     * Removes a listener.
     * 
     * @param listener The listener to remove
     */
    public void removeListener(Listener listener);
    
    /**
     * Starts the download manager.
     */
    public void start();
    
    /**
     * Stops the download manager.
     */
    public void stop();
    
    /**
     * Pauses downloads.
     */
    public void pause();
    
    /**
     * Resumes downloads.
     */
    public void resume();
    
    /**
     * Returns the download index.
     * 
     * @return The download index
     */
    public DownloadIndex getDownloadIndex();
    
    /**
     * Returns whether the download manager is initialized.
     * 
     * @return Whether initialized
     */
    public boolean isInitialized();
    
    /**
     * Returns whether the download manager is idle.
     * 
     * @return Whether idle
     */
    public boolean isIdle();
    
    /**
     * Returns whether waiting for requirements.
     * 
     * @return Whether waiting for requirements
     */
    public boolean isWaitingForRequirements();
    
    /**
     * Returns the current downloads.
     * 
     * @return List of current downloads
     */
    public List<Download> getCurrentDownloads();
    
    /**
     * Adds a download.
     * 
     * @param request The download request
     */
    public void addDownload(DownloadRequest request);
    
    /**
     * Adds a download with stop reason.
     * 
     * @param request The download request
     * @param stopReason The stop reason
     */
    public void addDownload(DownloadRequest request, int stopReason);
    
    /**
     * Removes a download.
     * 
     * @param id The download ID
     */
    public void removeDownload(String id);
    
    /**
     * Removes all downloads.
     */
    public void removeAllDownloads();
    
    /**
     * Sets the stop reason for a download.
     * 
     * @param id The download ID
     * @param stopReason The stop reason
     */
    public void setStopReason(@Nullable String id, int stopReason);
    
    /**
     * Sets the maximum parallel downloads.
     * 
     * @param maxParallelDownloads The maximum parallel downloads
     */
    public void setMaxParallelDownloads(int maxParallelDownloads);
    
    /**
     * Sets the minimum retry count.
     * 
     * @param minRetryCount The minimum retry count
     */
    public void setMinRetryCount(int minRetryCount);
    
    /**
     * Sets the requirements.
     * 
     * @param requirements The requirements
     */
    public void setRequirements(Requirements requirements);
    
    /**
     * Releases the download manager.
     */
    public void release();
}

DownloadRequest

Represents a request to download media content.

public final class DownloadRequest implements Parcelable {
    /**
     * The download ID.
     */
    public final String id;
    
    /**
     * The media URI.
     */
    public final Uri uri;
    
    /**
     * The MIME type, or null if unknown.
     */
    @Nullable public final String mimeType;
    
    /**
     * The stream keys for adaptive downloads.
     */
    public final List<StreamKey> streamKeys;
    
    /**
     * Custom cache key, or null to use default.
     */
    @Nullable public final String customCacheKey;
    
    /**
     * Application data associated with the request.
     */
    public final byte[] data;
    
    /**
     * Creates a DownloadRequest.
     * 
     * @param id The download ID
     * @param uri The media URI
     * @param mimeType The MIME type
     * @param streamKeys The stream keys
     * @param customCacheKey The custom cache key
     * @param data The application data
     */
    public DownloadRequest(String id, Uri uri, @Nullable String mimeType, List<StreamKey> streamKeys,
                          @Nullable String customCacheKey, @Nullable byte[] data);
    
    /**
     * Creates a DownloadRequest with generated ID.
     * 
     * @param uri The media URI
     * @return A DownloadRequest with generated ID
     */
    public static DownloadRequest fromUri(Uri uri);
    
    /**
     * Creates a DownloadRequest with generated ID and MIME type.
     * 
     * @param uri The media URI
     * @param mimeType The MIME type
     * @return A DownloadRequest
     */
    public static DownloadRequest fromUri(Uri uri, @Nullable String mimeType);
    
    /**
     * Returns a copy with the specified stream keys.
     * 
     * @param streamKeys The stream keys
     * @return A copy with the specified stream keys
     */
    public DownloadRequest copyWithStreamKeys(List<StreamKey> streamKeys);
    
    /**
     * Returns a copy with the specified ID.
     * 
     * @param id The ID
     * @return A copy with the specified ID
     */
    public DownloadRequest copyWithId(String id);
}

Download

Represents the state of a download.

public final class Download {
    /**
     * Download states.
     */
    @IntDef({STATE_QUEUED, STATE_STOPPED, STATE_DOWNLOADING, STATE_COMPLETED, STATE_FAILED, STATE_REMOVING, STATE_RESTARTING})
    @interface State {}
    
    public static final int STATE_QUEUED = 0;
    public static final int STATE_STOPPED = 1;
    public static final int STATE_DOWNLOADING = 2;
    public static final int STATE_COMPLETED = 3;
    public static final int STATE_FAILED = 4;
    public static final int STATE_REMOVING = 5;
    public static final int STATE_RESTARTING = 6;
    
    /**
     * Failure reasons.
     */
    @IntDef({FAILURE_REASON_NONE, FAILURE_REASON_UNKNOWN})
    @interface FailureReason {}
    
    public static final int FAILURE_REASON_NONE = C.INDEX_UNSET;
    public static final int FAILURE_REASON_UNKNOWN = 0;
    
    /**
     * Stop reasons.
     */
    public static final int STOP_REASON_NONE = 0;
    
    /**
     * The download request.
     */
    public final DownloadRequest request;
    
    /**
     * The download state.
     */
    @State public final int state;
    
    /**
     * The start time in milliseconds since epoch.
     */
    public final long startTimeMs;
    
    /**
     * The update time in milliseconds since epoch.
     */
    public final long updateTimeMs;
    
    /**
     * The content length in bytes, or C.LENGTH_UNSET if unknown.
     */
    public final long contentLength;
    
    /**
     * The stop reason.
     */
    public final int stopReason;
    
    /**
     * The failure reason.
     */
    @FailureReason public final int failureReason;
    
    /**
     * The download progress.
     */
    public final DownloadProgress progress;
    
    /**
     * Creates a Download.
     */
    public Download(DownloadRequest request, @State int state, long startTimeMs, long updateTimeMs,
                   long contentLength, int stopReason, @FailureReason int failureReason,
                   DownloadProgress progress);
    
    /**
     * Returns the percentage downloaded.
     * 
     * @return The percentage downloaded (0-100)
     */
    public float getPercentDownloaded();
    
    /**
     * Returns the downloaded bytes.
     * 
     * @return The downloaded bytes
     */
    public long getBytesDownloaded();
}

DownloadProgress

Tracks the progress of a download.

public final class DownloadProgress {
    /**
     * The number of bytes downloaded.
     */
    public final long bytesDownloaded;
    
    /**
     * The content length in bytes, or C.LENGTH_UNSET if unknown.
     */
    public final long contentLength;
    
    /**
     * The percentage downloaded (0-100).
     */
    public final float percentDownloaded;
    
    /**
     * Creates a DownloadProgress.
     * 
     * @param bytesDownloaded The bytes downloaded
     * @param contentLength The content length
     * @param percentDownloaded The percentage downloaded
     */
    public DownloadProgress(long bytesDownloaded, long contentLength, float percentDownloaded);
}

Downloader Interface

Interface for downloading media content.

public interface Downloader {
    /**
     * Progress listener for download operations.
     */
    interface ProgressListener {
        /**
         * Called when download progress changes.
         * 
         * @param contentLength The content length
         * @param bytesDownloaded The bytes downloaded
         * @param percentDownloaded The percentage downloaded
         */
        void onProgress(long contentLength, long bytesDownloaded, float percentDownloaded);
    }
    
    /**
     * Downloads the content.
     * 
     * @param progressListener The progress listener
     * @throws IOException If an IO error occurs
     * @throws InterruptedException If the download is interrupted
     */
    void download(@Nullable ProgressListener progressListener) throws IOException, InterruptedException;
    
    /**
     * Cancels the download.
     */
    void cancel();
    
    /**
     * Removes the downloaded content.
     * 
     * @throws IOException If an IO error occurs
     */
    void remove() throws IOException;
}

DownloadHelper

Helper class for creating download requests with track selection.

public abstract class DownloadHelper {
    /**
     * Callback for DownloadHelper operations.
     */
    public interface Callback {
        /**
         * Called when the helper is prepared.
         * 
         * @param helper The download helper
         */
        void onPrepared(DownloadHelper helper);
        
        /**
         * Called when preparation fails.
         * 
         * @param helper The download helper
         * @param e The error
         */
        void onPrepareError(DownloadHelper helper, IOException e);
    }
    
    /**
     * Creates a DownloadHelper for the given media item.
     * 
     * @param mediaItem The media item
     * @param renderersFactory The renderers factory
     * @param dataSourceFactory The data source factory
     * @return The download helper
     */
    public static DownloadHelper forMediaItem(MediaItem mediaItem, RenderersFactory renderersFactory,
                                            @Nullable DataSource.Factory dataSourceFactory);
    
    /**
     * Creates a DownloadHelper for the given media item with track selector.
     * 
     * @param mediaItem The media item
     * @param renderersFactory The renderers factory
     * @param trackSelector The track selector
     * @param dataSourceFactory The data source factory
     * @return The download helper
     */
    public static DownloadHelper forMediaItem(MediaItem mediaItem, RenderersFactory renderersFactory,
                                            @Nullable TrackSelector trackSelector,
                                            @Nullable DataSource.Factory dataSourceFactory);
    
    /**
     * Prepares the helper asynchronously.
     * 
     * @param callback The callback
     */
    public void prepare(Callback callback);
    
    /**
     * Releases the helper.
     */
    public void release();
    
    /**
     * Returns the number of periods.
     * 
     * @return The number of periods
     */
    public int getPeriodCount();
    
    /**
     * Returns the mapped track info for a period.
     * 
     * @param periodIndex The period index
     * @return The mapped track info
     */
    public MappedTrackInfo getMappedTrackInfo(int periodIndex);
    
    /**
     * Returns the track selection for a period.
     * 
     * @param periodIndex The period index
     * @return The track selection
     */
    public List<ExoTrackSelection> getTrackSelections(int periodIndex);
    
    /**
     * Adds track selections for a period.
     * 
     * @param periodIndex The period index
     * @param overrides The track selection overrides
     */
    public void addTrackSelections(int periodIndex, TrackSelectionOverride... overrides);
    
    /**
     * Replaces track selections for a period.
     * 
     * @param periodIndex The period index
     * @param overrides The track selection overrides
     */
    public void replaceTrackSelections(int periodIndex, TrackSelectionOverride... overrides);
    
    /**
     * Adds track selections for all periods.
     * 
     * @param overrides The track selection overrides
     */
    public void addTrackSelections(TrackSelectionOverride... overrides);
    
    /**
     * Clears track selections for a period.
     * 
     * @param periodIndex The period index
     */
    public void clearTrackSelections(int periodIndex);
    
    /**
     * Gets download requests for the selected tracks.
     * 
     * @param data Application data for the requests
     * @return List of download requests
     */
    public List<DownloadRequest> getDownloadRequests(@Nullable byte[] data);
    
    /**
     * Gets a download request for the selected tracks.
     * 
     * @param data Application data for the request
     * @return The download request
     */
    public DownloadRequest getDownloadRequest(@Nullable byte[] data);
}

Requirements

Defines requirements for downloads (network, charging, etc.).

public final class Requirements implements Parcelable {
    /**
     * Requirement flags.
     */
    @IntDef(flag = true, value = {NETWORK, NETWORK_UNMETERED, DEVICE_CHARGING, DEVICE_IDLE})
    @interface RequirementFlags {}
    
    public static final int NETWORK = 1;
    public static final int NETWORK_UNMETERED = 1 << 1;
    public static final int DEVICE_CHARGING = 1 << 2;
    public static final int DEVICE_IDLE = 1 << 3;
    
    /**
     * The requirement flags.
     */
    @RequirementFlags public final int requirements;
    
    /**
     * Creates Requirements.
     * 
     * @param requirements The requirement flags
     */
    public Requirements(@RequirementFlags int requirements);
    
    /**
     * Returns the requirements that are not met.
     * 
     * @param context The context
     * @return The unmet requirements
     */
    @RequirementFlags public int getNotMetRequirements(Context context);
    
    /**
     * Returns whether the requirements are met.
     * 
     * @param context The context
     * @return Whether the requirements are met
     */
    public boolean areRequirementsMet(Context context);
}

Usage Examples

Basic Download Setup

// Create download database
DatabaseProvider databaseProvider = new StandaloneDatabaseProvider(context);
DownloadIndex downloadIndex = new DefaultDownloadIndex(databaseProvider);

// Create downloader factory
DownloaderFactory downloaderFactory = new DefaultDownloaderFactory(
    new CacheDataSource.Factory()
        .setCache(downloadCache)
        .setUpstreamDataSource(httpDataSourceFactory),
    /* executor= */ Runnable::run
);

// Create requirements (Wi-Fi and charging)
Requirements requirements = new Requirements(
    Requirements.NETWORK_UNMETERED | Requirements.DEVICE_CHARGING
);

// Create download manager
DownloadManager downloadManager = new DownloadManager(
    context, downloadIndex, downloaderFactory, requirements, 
    Executors.newFixedThreadPool(2)
);

// Add listener
downloadManager.addListener(new DownloadProgressListener());

// Start download manager
downloadManager.start();

Creating Download Requests

// Simple download request
Uri mediaUri = Uri.parse("https://example.com/video.mp4");
DownloadRequest simpleRequest = DownloadRequest.fromUri(mediaUri);

// Add to download manager
downloadManager.addDownload(simpleRequest);

// Download with specific tracks using DownloadHelper
MediaItem mediaItem = MediaItem.fromUri("https://example.com/manifest.mpd");
RenderersFactory renderersFactory = new DefaultRenderersFactory(context);

DownloadHelper downloadHelper = DownloadHelper.forMediaItem(
    mediaItem, renderersFactory, httpDataSourceFactory
);

downloadHelper.prepare(new DownloadHelper.Callback() {
    @Override
    public void onPrepared(DownloadHelper helper) {
        // Select specific tracks
        for (int periodIndex = 0; periodIndex < helper.getPeriodCount(); periodIndex++) {
            MappedTrackInfo mappedTrackInfo = helper.getMappedTrackInfo(periodIndex);
            
            // Add video track selection (720p max)
            TrackSelectionOverride videoOverride = new TrackSelectionOverride(
                selectVideoTrack(mappedTrackInfo, 1280, 720)
            );
            helper.addTrackSelections(periodIndex, videoOverride);
            
            // Add audio track selection (English)
            TrackSelectionOverride audioOverride = new TrackSelectionOverride(
                selectAudioTrack(mappedTrackInfo, "en")
            );
            helper.addTrackSelections(periodIndex, audioOverride);
        }
        
        // Create download request
        DownloadRequest request = helper.getDownloadRequest("my_download".getBytes());
        downloadManager.addDownload(request);
        
        helper.release();
    }
    
    @Override
    public void onPrepareError(DownloadHelper helper, IOException e) {
        Log.e(TAG, "Failed to prepare download", e);
        helper.release();
    }
});

Download Progress Monitoring

public class DownloadProgressListener implements DownloadManager.Listener {
    private static final String TAG = "DownloadProgress";
    
    @Override
    public void onDownloadChanged(DownloadManager downloadManager, Download download, 
                                @Nullable Exception finalException) {
        switch (download.state) {
            case Download.STATE_QUEUED:
                Log.d(TAG, "Download queued: " + download.request.id);
                updateDownloadUI(download.request.id, "Queued", 0);
                break;
                
            case Download.STATE_DOWNLOADING:
                float progress = download.getPercentDownloaded();
                long downloaded = download.getBytesDownloaded();
                Log.d(TAG, String.format("Downloading %s: %.1f%% (%d bytes)",
                                        download.request.id, progress, downloaded));
                updateDownloadUI(download.request.id, "Downloading", (int) progress);
                break;
                
            case Download.STATE_COMPLETED:
                Log.d(TAG, "Download completed: " + download.request.id);
                updateDownloadUI(download.request.id, "Completed", 100);
                break;
                
            case Download.STATE_FAILED:
                Log.e(TAG, "Download failed: " + download.request.id, finalException);
                updateDownloadUI(download.request.id, "Failed", 0);
                break;
                
            case Download.STATE_STOPPED:
                Log.d(TAG, "Download stopped: " + download.request.id);
                updateDownloadUI(download.request.id, "Stopped", (int) download.getPercentDownloaded());
                break;
        }
    }
    
    @Override
    public void onDownloadRemoved(DownloadManager downloadManager, Download download) {
        Log.d(TAG, "Download removed: " + download.request.id);
        removeDownloadUI(download.request.id);
    }
    
    @Override
    public void onRequirementsStateChanged(DownloadManager downloadManager, Requirements requirements,
                                         @RequirementsWatcher.RequirementFlags int notMetRequirements) {
        if (notMetRequirements == 0) {
            Log.d(TAG, "All requirements met, downloads can proceed");
            showRequirementsStatus("Ready to download");
        } else {
            List<String> unmetRequirements = new ArrayList<>();
            if ((notMetRequirements & Requirements.NETWORK) != 0) {
                unmetRequirements.add("Network");
            }
            if ((notMetRequirements & Requirements.NETWORK_UNMETERED) != 0) {
                unmetRequirements.add("Wi-Fi");
            }
            if ((notMetRequirements & Requirements.DEVICE_CHARGING) != 0) {
                unmetRequirements.add("Charging");
            }
            
            String message = "Waiting for: " + String.join(", ", unmetRequirements);
            Log.d(TAG, message);
            showRequirementsStatus(message);
        }
    }
    
    @Override
    public void onIdle(DownloadManager downloadManager, boolean idle) {
        Log.d(TAG, "Download manager idle: " + idle);
        if (idle) {
            showDownloadStatus("All downloads completed");
        }
    }
    
    private void updateDownloadUI(String downloadId, String status, int progress) {
        // Update UI with download progress
    }
    
    private void removeDownloadUI(String downloadId) {
        // Remove download from UI
    }
    
    private void showRequirementsStatus(String message) {
        // Show requirements status in UI
    }
    
    private void showDownloadStatus(String message) {
        // Show general download status
    }
}

Playing Downloaded Content

public class OfflinePlaybackManager {
    private final DownloadManager downloadManager;
    private final Cache downloadCache;
    
    public OfflinePlaybackManager(DownloadManager downloadManager, Cache downloadCache) {
        this.downloadManager = downloadManager;
        this.downloadCache = downloadCache;
    }
    
    /**
     * Checks if content is available offline.
     * 
     * @param mediaUri The media URI
     * @return Whether the content is available offline
     */
    public boolean isAvailableOffline(Uri mediaUri) {
        try {
            Download download = downloadManager.getDownloadIndex().getDownload(mediaUri.toString());
            return download != null && download.state == Download.STATE_COMPLETED;
        } catch (DatabaseIOException e) {
            Log.e(TAG, "Failed to check offline availability", e);
            return false;
        }
    }
    
    /**
     * Creates a media source for offline playback.
     * 
     * @param mediaItem The media item
     * @return The media source configured for offline playback
     */
    public MediaSource createOfflineMediaSource(MediaItem mediaItem) {
        // Create data source factory that uses cache
        DataSource.Factory dataSourceFactory = new CacheDataSource.Factory()
            .setCache(downloadCache)
            .setUpstreamDataSource(new FileDataSource.Factory())
            .setCacheWriteDataSinkFactory(null); // Read-only for offline playback
        
        // Create media source factory
        return new DefaultMediaSourceFactory(dataSourceFactory)
            .createMediaSource(mediaItem);
    }
    
    /**
     * Gets download progress for a media item.
     * 
     * @param mediaUri The media URI
     * @return The download progress, or null if not found
     */
    @Nullable
    public DownloadProgress getDownloadProgress(Uri mediaUri) {
        try {
            Download download = downloadManager.getDownloadIndex().getDownload(mediaUri.toString());
            return download != null ? download.progress : null;
        } catch (DatabaseIOException e) {
            Log.e(TAG, "Failed to get download progress", e);
            return null;
        }
    }
}

Advanced Download Management

public class AdvancedDownloadManager {
    private final DownloadManager downloadManager;
    private final Map<String, DownloadRequest> pendingDownloads = new HashMap<>();
    
    public AdvancedDownloadManager(DownloadManager downloadManager) {
        this.downloadManager = downloadManager;
    }
    
    /**
     * Downloads with quality selection.
     * 
     * @param mediaItem The media item
     * @param maxVideoHeight Maximum video height (e.g., 720 for 720p)
     * @param preferredLanguage Preferred audio language
     */
    public void downloadWithQuality(MediaItem mediaItem, int maxVideoHeight, String preferredLanguage) {
        DownloadHelper helper = DownloadHelper.forMediaItem(
            mediaItem, 
            new DefaultRenderersFactory(context),
            httpDataSourceFactory
        );
        
        helper.prepare(new DownloadHelper.Callback() {
            @Override
            public void onPrepared(DownloadHelper helper) {
                try {
                    selectTracksForDownload(helper, maxVideoHeight, preferredLanguage);
                    
                    DownloadRequest request = helper.getDownloadRequest(
                        createDownloadMetadata(mediaItem, maxVideoHeight, preferredLanguage)
                    );
                    
                    downloadManager.addDownload(request);
                    
                } finally {
                    helper.release();
                }
            }
            
            @Override
            public void onPrepareError(DownloadHelper helper, IOException e) {
                Log.e(TAG, "Failed to prepare download for quality selection", e);
                helper.release();
            }
        });
    }
    
    /**
     * Pauses a specific download.
     * 
     * @param downloadId The download ID
     */
    public void pauseDownload(String downloadId) {
        downloadManager.setStopReason(downloadId, 1); // Custom stop reason
    }
    
    /**
     * Resumes a paused download.
     * 
     * @param downloadId The download ID
     */
    public void resumeDownload(String downloadId) {
        downloadManager.setStopReason(downloadId, Download.STOP_REASON_NONE);
    }
    
    /**
     * Removes a download and deletes the content.
     * 
     * @param downloadId The download ID
     */
    public void removeDownload(String downloadId) {
        downloadManager.removeDownload(downloadId);
    }
    
    /**
     * Gets all completed downloads.
     * 
     * @return List of completed downloads
     */
    public List<Download> getCompletedDownloads() {
        return downloadManager.getCurrentDownloads().stream()
            .filter(download -> download.state == Download.STATE_COMPLETED)
            .collect(Collectors.toList());
    }
    
    /**
     * Gets total storage used by downloads.
     * 
     * @return Total bytes used by downloads
     */
    public long getTotalDownloadSize() {
        return downloadManager.getCurrentDownloads().stream()
            .filter(download -> download.state == Download.STATE_COMPLETED)
            .mapToLong(Download::getBytesDownloaded)
            .sum();
    }
    
    private void selectTracksForDownload(DownloadHelper helper, int maxVideoHeight, String preferredLanguage) {
        for (int periodIndex = 0; periodIndex < helper.getPeriodCount(); periodIndex++) {
            MappedTrackInfo mappedTrackInfo = helper.getMappedTrackInfo(periodIndex);
            
            // Select video track with quality limit
            TrackGroup videoTrackGroup = selectBestVideoTrack(mappedTrackInfo, maxVideoHeight);
            if (videoTrackGroup != null) {
                helper.addTrackSelections(periodIndex, 
                    new TrackSelectionOverride(videoTrackGroup, Arrays.asList(0)));
            }
            
            // Select audio track with language preference
            TrackGroup audioTrackGroup = selectAudioTrackByLanguage(mappedTrackInfo, preferredLanguage);
            if (audioTrackGroup != null) {
                helper.addTrackSelections(periodIndex,
                    new TrackSelectionOverride(audioTrackGroup, Arrays.asList(0)));
            }
        }
    }
    
    private byte[] createDownloadMetadata(MediaItem mediaItem, int maxVideoHeight, String preferredLanguage) {
        // Create metadata for the download
        return String.format("quality=%dp,lang=%s", maxVideoHeight, preferredLanguage).getBytes();
    }
    
    private TrackGroup selectBestVideoTrack(MappedTrackInfo mappedTrackInfo, int maxVideoHeight) {
        // Implementation to select best video track within height limit
        return null; // Simplified for example
    }
    
    private TrackGroup selectAudioTrackByLanguage(MappedTrackInfo mappedTrackInfo, String preferredLanguage) {
        // Implementation to select audio track by language
        return null; // Simplified for example
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-androidx-media3--media3-exoplayer

docs

analytics.md

audio-rendering.md

core-player.md

drm-support.md

index.md

media-sources.md

offline-support.md

track-selection.md

video-rendering.md

tile.json