ExoPlayer は、オフライン再生用にメディアをダウンロードする機能を提供します。ほとんどの場合、アプリがバックグラウンドで動作している場合でもダウンロードを続行することをおすすめします。このようなユースケースでは、アプリで DownloadService
をサブクラス化し、ダウンロードの追加、削除、制御を行うコマンドをサービスに送信する必要があります。次の図は、関連する主なクラスを示しています。
DownloadService
:DownloadManager
をラップしてコマンドを転送します。このサービスにより、アプリがバックグラウンドであっても、DownloadManager
の実行を継続できます。DownloadManager
: 複数のダウンロードを管理します。また、DownloadIndex
との間で状態を読み込んで保存し、ネットワーク接続などの要件に基づいてダウンロードを開始および停止します。コンテンツをダウンロードするために、マネージャーは通常、ダウンロードされるデータをHttpDataSource
から読み取り、Cache
に書き込みます。DownloadIndex
: ダウンロードの状態を保持します。
DownloadService の作成
DownloadService
を作成するには、サブクラスを作成して抽象メソッドを実装します。
getDownloadManager()
: 使用されるDownloadManager
を返します。getScheduler()
: オプションのScheduler
を返します。これは、保留中のダウンロードの進行状況に必要な要件が満たされるとサービスを再起動できます。ExoPlayer は、次の実装を提供します。PlatformScheduler
。JobScheduler を使用します(最小 API は 21)。アプリの権限の要件については、PlatformScheduler の javadocs をご覧ください。WorkManagerScheduler
。WorkManager を使用します。
getForegroundNotification()
: サービスがフォアグラウンドで実行されているときに表示される通知を返します。DownloadNotificationHelper.buildProgressNotification
を使用すると、デフォルトのスタイルで通知を作成できます。
最後に、AndroidManifest.xml
ファイルでサービスを定義します。
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<application>
<service android:name="com.myapp.MyDownloadService"
android:exported="false"
android:foregroundServiceType="dataSync">
<!-- This is needed for Scheduler -->
<intent-filter>
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
</application>
具体的な例については、ExoPlayer デモアプリの DemoDownloadService
と AndroidManifest.xml
をご覧ください。
DownloadManager の作成
次のコード スニペットは、DownloadService
で getDownloadManager()
によって返される DownloadManager
をインスタンス化する方法を示しています。
Kotlin
// Note: This should be a singleton in your app. val databaseProvider = StandaloneDatabaseProvider(context) // A download cache should not evict media, so should use a NoopCacheEvictor. val downloadCache = SimpleCache(downloadDirectory, NoOpCacheEvictor(), databaseProvider) // Create a factory for reading the data from the network. val dataSourceFactory = DefaultHttpDataSource.Factory() // Choose an executor for downloading data. Using Runnable::run will cause each download task to // download data on its own thread. Passing an executor that uses multiple threads will speed up // download tasks that can be split into smaller parts for parallel execution. Applications that // already have an executor for background downloads may wish to reuse their existing executor. val downloadExecutor = Executor(Runnable::run) // Create the download manager. val downloadManager = DownloadManager(context, databaseProvider, downloadCache, dataSourceFactory, downloadExecutor) // Optionally, properties can be assigned to configure the download manager. downloadManager.requirements = requirements downloadManager.maxParallelDownloads = 3
Java
// Note: This should be a singleton in your app. databaseProvider = new StandaloneDatabaseProvider(context); // A download cache should not evict media, so should use a NoopCacheEvictor. downloadCache = new SimpleCache(downloadDirectory, new NoOpCacheEvictor(), databaseProvider); // Create a factory for reading the data from the network. dataSourceFactory = new DefaultHttpDataSource.Factory(); // Choose an executor for downloading data. Using Runnable::run will cause each download task to // download data on its own thread. Passing an executor that uses multiple threads will speed up // download tasks that can be split into smaller parts for parallel execution. Applications that // already have an executor for background downloads may wish to reuse their existing executor. Executor downloadExecutor = Runnable::run; // Create the download manager. downloadManager = new DownloadManager( context, databaseProvider, downloadCache, dataSourceFactory, downloadExecutor); // Optionally, setters can be called to configure the download manager. downloadManager.setRequirements(requirements); downloadManager.setMaxParallelDownloads(3);
具体的な例については、デモアプリの DemoUtil
をご覧ください。
ダウンロードの追加
ダウンロードを追加するには、DownloadRequest
を作成して DownloadService
に送信します。アダプティブ ストリームの場合は、DownloadHelper
を使用して DownloadRequest
を作成します。次の例は、ダウンロード リクエストを作成する方法を示しています。
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
この例では、contentId
はコンテンツの一意の識別子です。単純なケースでは多くの場合、contentUri
を contentId
として使用できますが、アプリはユースケースに最適な ID スキームを自由に使用できます。DownloadRequest.Builder
には、オプションのセッターもあります。たとえば、setKeySetId
と setData
を使用して、アプリがダウンロードに関連付ける DRM とカスタムデータをそれぞれ設定できます。コンテンツの MIME タイプは setMimeType
を使用して指定できます。コンテンツ タイプが contentUri
から推測できない場合のヒントとして、setMimeType
を使用します。
作成されたら、リクエストを DownloadService
に送信してダウンロードを追加できます。
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
この例では、MyDownloadService
はアプリの DownloadService
サブクラスであり、foreground
パラメータはサービスをフォアグラウンドで開始するかどうかを制御します。アプリがすでにフォアグラウンドにある場合、foreground
パラメータは通常 false
に設定する必要があります。これは、DownloadService
が処理を行う必要があると判断した場合、自身をフォアグラウンドにするためです。
ダウンロードを削除しています
ダウンロードを削除するには、削除コマンドを DownloadService
に送信します。ここで、contentId
には削除するダウンロードを指定します。
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
DownloadService.sendRemoveAllDownloads
を使用して、ダウンロードされたすべてのデータを削除することもできます。
ダウンロードの開始と停止
ダウンロードは、次の 4 つの条件が満たされた場合にのみ進行します。
- ダウンロードに停止理由がありません。
- 一時保存は一時停止されません。
- ダウンロードの進行要件を満たしている。要件では、許可されるネットワーク タイプに関する制約や、デバイスをアイドル状態にするか、充電器に接続するかについての制約を指定できます。
- 並列ダウンロードの最大数を超えていない。
これらの条件はすべて、DownloadService
にコマンドを送信することで制御できます。
ダウンロード停止理由の設定と消去
1 つまたはすべてのダウンロードを停止する理由を設定できます。
Kotlin
// Set the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService::class.java, contentId, stopReason, /* foreground= */ false ) // Clear the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService::class.java, contentId, Download.STOP_REASON_NONE, /* foreground= */ false )
Java
// Set the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService.class, contentId, stopReason, /* foreground= */ false); // Clear the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService.class, contentId, Download.STOP_REASON_NONE, /* foreground= */ false);
stopReason
にはゼロ以外の任意の値を指定できます(Download.STOP_REASON_NONE = 0
は、ダウンロードが停止しないことを示す特別な値です)。ダウンロードを停止する理由が複数ある場合は、さまざまな値を使用して、各ダウンロードが停止された理由を追跡できます。すべてのダウンロードの停止理由の設定とクリアは、1 回のダウンロードの停止理由の設定とクリアと同じですが、contentId
を null
に設定する必要があります。
ダウンロードに 0 以外の停止理由があると、Download.STATE_STOPPED
状態になります。停止理由は DownloadIndex
に保持されるため、アプリプロセスが強制終了されて後で再起動された場合も保持されます。
すべてのダウンロードを一時停止 / 再開する
すべてのダウンロードは、次の手順で一時停止または再開できます。
Kotlin
// Pause all downloads. DownloadService.sendPauseDownloads( context, MyDownloadService::class.java, /* foreground= */ false ) // Resume all downloads. DownloadService.sendResumeDownloads( context, MyDownloadService::class.java, /* foreground= */ false )
Java
// Pause all downloads. DownloadService.sendPauseDownloads(context, MyDownloadService.class, /* foreground= */ false); // Resume all downloads. DownloadService.sendResumeDownloads(context, MyDownloadService.class, /* foreground= */ false);
ダウンロードを一時停止すると、Download.STATE_QUEUED
状態になります。停止理由の設定とは異なり、このアプローチでは状態変更が保持されません。DownloadManager
のランタイム状態にのみ影響します。
ダウンロードの進行要件を設定する
Requirements
を使用すると、ダウンロードを続行するために満たす必要がある制約を指定できます。要件を設定するには、上記の例のように、DownloadManager
の作成時に DownloadManager.setRequirements()
を呼び出します。また、DownloadService
にコマンドを送信して動的に変更することもできます。
Kotlin
// Set the download requirements. DownloadService.sendSetRequirements( context, MyDownloadService::class.java, requirements, /* foreground= */ false)
Java
// Set the download requirements. DownloadService.sendSetRequirements( context, MyDownloadService.class, requirements, /* foreground= */ false);
要件を満たしていないためにダウンロードを続行できない場合は、Download.STATE_QUEUED
状態になります。満たされていない要件は DownloadManager.getNotMetRequirements()
を使用してクエリできます。
並列ダウンロードの最大数の設定
並列ダウンロードの最大数は DownloadManager.setMaxParallelDownloads()
を呼び出して設定できます。これは通常、上記の例のように、DownloadManager
の作成時に行われます。
並列ダウンロードの最大数がすでに進行中であるためにダウンロードを続行できない場合は、Download.STATE_QUEUED
状態になります。
ダウンロードのクエリ
DownloadManager
の DownloadIndex
をクエリして、完了または失敗したすべてのダウンロードの状態を確認できます。DownloadIndex
を取得するには、DownloadManager.getDownloadIndex()
を呼び出します。すべてのダウンロードに対して反復処理を行うカーソルは、DownloadIndex.getDownloads()
を呼び出すことで取得できます。または、DownloadIndex.getDownload()
を呼び出して、単一のダウンロードの状態をクエリすることもできます。
DownloadManager
は DownloadManager.getCurrentDownloads()
も提供します。これは現在のダウンロードの状態(未完了または失敗)のみを返します。このメソッドは、現在のダウンロードの進行状況とステータスを表示する通知やその他の UI コンポーネントの更新に役立ちます。
一時保存したコンテンツの視聴
DownloadManager
にリスナーを追加すると、現在のダウンロードの状態が変化したときに通知を受け取ることができます。
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
具体的な例については、デモアプリの DownloadTracker
クラスの DownloadManagerListener
をご覧ください。
ダウンロードしたコンテンツの再生
ダウンロードしたコンテンツの再生はオンライン コンテンツの再生と似ていますが、データがネットワーク経由ではなくダウンロード Cache
から読み取られる点が異なります。
ダウンロードしたコンテンツを再生するには、ダウンロードに使用したものと同じ Cache
インスタンスを使用して CacheDataSource.Factory
を作成し、プレーヤーをビルドするときに DefaultMediaSourceFactory
に挿入します。
Kotlin
// Create a read-only cache data source factory using the download cache. val cacheDataSourceFactory: DataSource.Factory = CacheDataSource.Factory() .setCache(downloadCache) .setUpstreamDataSourceFactory(httpDataSourceFactory) .setCacheWriteDataSinkFactory(null) // Disable writing. val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory) ) .build()
Java
// Create a read-only cache data source factory using the download cache. DataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory() .setCache(downloadCache) .setUpstreamDataSourceFactory(httpDataSourceFactory) .setCacheWriteDataSinkFactory(null); // Disable writing. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory)) .build();
ダウンロードされていないコンテンツの再生に同じプレーヤー インスタンスも使用する場合は、再生中にそのコンテンツがダウンロードされないように、CacheDataSource.Factory
を読み取り専用として設定する必要があります。
プレーヤーに CacheDataSource.Factory
を設定すると、ダウンロードしたコンテンツにアクセスして再生できるようになります。そうすれば、対応する MediaItem
をプレーヤーに渡すだけで、ダウンロードの再生が簡単になります。MediaItem
は、Download.request.toMediaItem
を使用して Download
から、または DownloadRequest.toMediaItem
を使用して DownloadRequest
から直接取得できます。
MediaSource の構成
上記の例では、すべての MediaItem
の再生でダウンロード キャッシュを利用できるようになっています。また、個々の MediaSource
インスタンスでダウンロード キャッシュを利用し、プレーヤーに直接渡すこともできます。
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)) player.setMediaSource(mediaSource) player.prepare()
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)); player.setMediaSource(mediaSource); player.prepare();
アダプティブ ストリームのダウンロードと再生
通常、アダプティブ ストリーム(DASH、SmoothStreaming、HLS など)には複数のメディア トラックが含まれます。多くの場合、異なる品質の同じコンテンツ(SD、HD、4K 動画トラックなど)を含むトラックが複数存在する場合があります。また、異なるコンテンツを含む同じタイプの複数のトラック(異なる言語の複数の音声トラックなど)が存在する場合もあります。
ストリーミング再生では、トラック セレクタを使用して、再生するトラックを選択できます。同様に、ダウンロードの場合は、DownloadHelper
を使用してダウンロードするトラックを選択できます。DownloadHelper
の一般的な使用方法は次のとおりです。
DownloadHelper.forMediaItem
メソッドのいずれかを使用してDownloadHelper
を作成します。ヘルパーを準備し、コールバックを待ちます。Kotlin
val downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), DefaultRenderersFactory(context), dataSourceFactory ) downloadHelper.prepare(callback)
Java
DownloadHelper downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), new DefaultRenderersFactory(context), dataSourceFactory); downloadHelper.prepare(callback);
- 必要に応じて、
getMappedTrackInfo
とgetTrackSelections
を使用してデフォルトで選択されているトラックを検査し、clearTrackSelections
、replaceTrackSelections
、addTrackSelection
を使用して調整を行います。 getDownloadRequest
を呼び出して、選択したトラックのDownloadRequest
を作成します。前述のように、リクエストをDownloadService
に渡してダウンロードを追加できます。release()
を使用してヘルパーを解放します。
ダウンロードしたアダプティブ コンテンツを再生するには、前述のように、プレーヤーを構成して、対応する MediaItem
を渡す必要があります。
MediaItem
をビルドする際、MediaItem.localConfiguration.streamKeys
を DownloadRequest
内の値と一致するように設定し、プレーヤーがダウンロード済みのトラックのサブセットのみの再生を試みるようにする必要があります。Download.request.toMediaItem
と DownloadRequest.toMediaItem
を使用して MediaItem
をビルドすると、この処理が自動的に行われます。