Téléchargement de contenu multimédia

ExoPlayer propose une fonctionnalité permettant de télécharger des contenus multimédias pour les lire hors connexion. Dans la plupart des cas les téléchargements, il est souhaitable que les téléchargements se poursuivent même lorsque votre application se trouve dans en arrière-plan. Pour ces cas d'utilisation, votre application doit sous-classer DownloadService et envoyer des commandes au service pour ajouter, supprimer et contrôler les téléchargements. La Le schéma suivant illustre les principales classes impliquées.

Classes pour le téléchargement de contenus multimédias Le sens des flèches indique le flux de données.

  • DownloadService: encapsule un DownloadManager et lui transmet les commandes. La permet à DownloadManager de continuer à s'exécuter même lorsque l'application est dans en arrière-plan.
  • DownloadManager: gère plusieurs téléchargements, et charge (et stocke) leurs états depuis (et vers) un DownloadIndex, le démarrage et l'arrêt des téléchargements en fonction en fonction d'exigences telles que la connectivité réseau, etc. Pour télécharger contenus, le gestionnaire lit généralement les données téléchargées HttpDataSource, puis écrivez-le dans une Cache.
  • DownloadIndex: conserve les états des téléchargements.

Créer un DownloadService

Pour créer un DownloadService, sous-classez-le et implémentez ses méthodes abstraites:

  • getDownloadManager(): renvoie l'élément DownloadManager à utiliser.
  • getScheduler(): renvoie un Scheduler facultatif, qui peut redémarrer le lorsque les conditions requises pour la progression des téléchargements en attente sont remplies. ExoPlayer fournit les implémentations suivantes: <ph type="x-smartling-placeholder">
      </ph>
    • PlatformScheduler, qui utilise JobScheduler (niveau d'API minimal : 21). Voir Javadocs PlatformScheduler pour connaître les exigences concernant les autorisations des applications
    • WorkManagerScheduler, qui utilise WorkManager.
  • getForegroundNotification(): renvoie une notification à afficher lorsque le s'exécute au premier plan. Vous pouvez utiliser DownloadNotificationHelper.buildProgressNotification pour créer un dans le style par défaut.

Enfin, définissez le service dans votre fichier 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>

Consultez DemoDownloadService et AndroidManifest.xml dans ExoPlayer de démonstration pour obtenir un exemple concret.

Créer un DownloadManager

L'extrait de code suivant montre comment instancier un DownloadManager, qui peut être renvoyé par getDownloadManager() dans votre DownloadService:

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);

Consultez DemoUtil dans l'application de démonstration pour obtenir un exemple concret.

Ajouter un téléchargement

Pour ajouter un téléchargement, créez un fichier DownloadRequest et envoyez-le à votre DownloadService Pour les flux adaptatifs, utilisez DownloadHelper pour créer un DownloadRequest ; Les éléments suivants : L'exemple suivant montre comment créer une requête de téléchargement:

Kotlin

val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()

Java

DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();

Dans cet exemple, contentId est un identifiant unique pour le contenu. Dans des cas simples, le contentUri peut souvent être utilisé comme contentId, mais les applications sont libres d'utiliser le schéma d'ID qui convient le mieux à leur cas d'utilisation. DownloadRequest.Builder a également des setters facultatifs. Par exemple, setKeySetId et setData peuvent servir à définir une DRM et les données personnalisées que l'application souhaite associer au téléchargement, respectivement. Le type MIME du contenu peut également être spécifié à l'aide de setMimeType, comme indice pour les cas où le type de contenu ne peut pas être déduit à partir de contentUri.

Une fois créée, la demande peut être envoyée à DownloadService pour ajouter le télécharger:

Kotlin

DownloadService.sendAddDownload(
  context,
  MyDownloadService::class.java,
  downloadRequest,
  /* foreground= */ false
)

Java

DownloadService.sendAddDownload(
    context, MyDownloadService.class, downloadRequest, /* foreground= */ false);

Dans cet exemple, MyDownloadService est la sous-classe DownloadService de l'application. Le paramètre foreground contrôle si le service sera démarré dans le au premier plan. Si votre application est déjà au premier plan, foreground doit normalement être défini sur false, car DownloadService se mettre au premier plan s'il détermine qu'il a du travail à faire.

Suppression des téléchargements…

Vous pouvez supprimer un téléchargement en envoyant une commande de suppression à DownloadService. où contentId identifie le téléchargement à supprimer:

Kotlin

DownloadService.sendRemoveDownload(
  context,
  MyDownloadService::class.java,
  contentId,
  /* foreground= */ false
)

Java

DownloadService.sendRemoveDownload(
    context, MyDownloadService.class, contentId, /* foreground= */ false);

Vous pouvez également supprimer toutes les données téléchargées avec DownloadService.sendRemoveAllDownloads

Démarrer et arrêter des téléchargements

Un téléchargement ne pourra progresser que si les quatre conditions suivantes sont remplies:

  • Le téléchargement n'a pas de motif d'arrêt.
  • Les téléchargements ne sont pas suspendus.
  • Les conditions requises pour que les téléchargements puissent progresser sont remplies. Les exigences peuvent spécifier sur les types de réseau autorisés et si l'appareil doit être inactif ou connecté à un chargeur.
  • Le nombre maximal de téléchargements parallèles n'est pas dépassé.

Vous pouvez contrôler toutes ces conditions en envoyant des commandes DownloadService

Définir et effacer les motifs d'arrêt des téléchargements

Vous pouvez définir un motif pour l'arrêt d'un ou de tous les téléchargements:

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 peut être n'importe quelle valeur non nulle (Download.STOP_REASON_NONE = 0 est égal à une valeur spéciale indiquant que le téléchargement n'est pas arrêté. Les applications qui ont Plusieurs raisons peuvent expliquer l'arrêt des téléchargements. Vous pouvez utiliser différentes valeurs pourquoi chaque téléchargement s'arrête. Définir et effacer le motif de l'arrêt pour tous fonctionne de la même manière que lorsque vous définissez et effacez le motif d'arrêt d'une téléchargement unique, sauf que contentId doit être défini sur null.

Lorsqu'un téléchargement présente un motif d'arrêt non nul, il est placé dans le Download.STATE_STOPPED. Les motifs d'arrêt sont conservés dans le champ DownloadIndex, et sont donc conservés si le processus d'application est arrêté et redémarré ensuite.

Interrompre et reprendre tous les téléchargements

Vous pouvez suspendre et reprendre tous les téléchargements comme suit:

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);

Lorsque les téléchargements sont suspendus, ils sont à l'état Download.STATE_QUEUED. Contrairement à la définition des motifs d'arrêt, cette approche ne conserve aucun état des modifications. Elle n'affecte que l'état d'exécution de DownloadManager.

Définir les conditions requises pour la progression des téléchargements

Requirements permet de spécifier les contraintes devant être respectées pour téléchargements pour continuer. Ces exigences peuvent être définies en appelant DownloadManager.setRequirements() lors de la création de DownloadManager, comme dans l'exemple ci-dessus. Ils peuvent également être modifiés de manière dynamique en envoyant une commande à 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);

Lorsqu'un téléchargement ne peut pas être effectué parce que les conditions ne sont pas remplies, sont à l'état Download.STATE_QUEUED. Vous pouvez interroger la valeur avec DownloadManager.getNotMetRequirements().

Définir le nombre maximal de téléchargements parallèles

Le nombre maximal de téléchargements parallèles peut être défini en appelant DownloadManager.setMaxParallelDownloads() Cette opération est normalement effectuée lorsque en créant DownloadManager, comme dans l'exemple ci-dessus.

Lorsqu'un téléchargement ne peut pas être effectué en raison du nombre maximal de téléchargements parallèles sont déjà en cours, son état sera Download.STATE_QUEUED.

Interroger des téléchargements

Le DownloadIndex d'un DownloadManager peut être interrogé pour l'état de tous téléchargements, y compris ceux qui se terminent ou ont échoué. DownloadIndex vous pouvez l'obtenir en appelant DownloadManager.getDownloadIndex(). Un curseur qui des itérations sur tous les téléchargements peuvent alors être obtenues en appelant DownloadIndex.getDownloads() L'état d'un téléchargement unique peuvent être interrogées en appelant DownloadIndex.getDownload().

DownloadManager fournit également DownloadManager.getCurrentDownloads(), qui ne renvoie que l'état des téléchargements en cours (non terminés ou ayant échoué). Ce est utile pour mettre à jour les notifications et autres composants d'UI qui affichent la progression et l'état des téléchargements en cours.

Écouter les téléchargements

Vous pouvez ajouter un écouteur à DownloadManager pour être informé lorsque des les téléchargements changent d'état:

Kotlin

downloadManager.addListener(
  object : DownloadManager.Listener { // Override methods of interest here.
  }
)

Java

downloadManager.addListener(
    new DownloadManager.Listener() {
      // Override methods of interest here.
    });

Consultez DownloadManagerListener dans la classe DownloadTracker de l'application de démonstration pour un exemple concret.

Lecture du contenu téléchargé

La lecture de contenus téléchargés est semblable à la lecture de contenus en ligne, à ceci près que les données sont lues à partir du fichier Cache téléchargé au lieu du réseau.

Pour lire le contenu téléchargé, créez un CacheDataSource.Factory en utilisant la même Cache utilisée pour le téléchargement et l'injecter dans DefaultMediaSourceFactory lors de la création du lecteur:

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();

Si la même instance de lecteur doit également être utilisée pour lire du contenu non téléchargé CacheDataSource.Factory doit alors être configuré en lecture seule pour éviter et télécharger ce contenu pendant la lecture.

Une fois le lecteur configuré avec CacheDataSource.Factory, il ont accès au contenu téléchargé pour pouvoir être lu. Je lance un téléchargement, il suffit de transmettre le MediaItem correspondant au joueur. MediaItem peut être obtenue à partir d'un Download en utilisant Download.request.toMediaItem ; ou directement à partir d'un DownloadRequest à l'aide de DownloadRequest.toMediaItem.

Configuration de MediaSource

L'exemple précédent rend le cache de téléchargement disponible pour la lecture MediaItems. Vous pouvez aussi rendre le cache de téléchargement disponible Instances MediaSource individuelles, qui peuvent être transmises directement au joueur:

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();

Télécharger et lire des flux adaptatifs

Les flux adaptatifs (DASH, SmoothStreaming et HLS, par exemple) contiennent généralement plusieurs des pistes multimédias. Il y a souvent plusieurs pistes contenant le même contenu dans différentes qualités (par exemple, des pistes vidéo SD, HD et 4K) Il peut également y avoir plusieurs pistes du même type avec des contenus différents (par exemple, plusieurs pistes des pistes audio dans différentes langues).

Pour les lectures en streaming, vous pouvez utiliser un sélecteur de piste pour choisir titres sont lus. De même, pour le téléchargement, un DownloadHelper peut être utilisé pour choisir les pistes à télécharger. Utilisation habituelle d'un DownloadHelper procédez comme suit:

  1. Créez un DownloadHelper à l'aide de l'un des DownloadHelper.forMediaItem. méthodes. Préparez l'application auxiliaire et attendez le rappel.

    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);
    
  2. Si vous le souhaitez, inspecter les pistes sélectionnées par défaut à l'aide de getMappedTrackInfo et getTrackSelections, puis effectuer des ajustements à l'aide de clearTrackSelections replaceTrackSelections et addTrackSelection.
  3. Créez un DownloadRequest pour les titres sélectionnés en appelant getDownloadRequest La demande peut être transmise à votre DownloadService pour ajoutez le téléchargement, comme décrit ci-dessus.
  4. Libérez l'application auxiliaire à l'aide de release().

La lecture du contenu adaptatif téléchargé nécessite de configurer le lecteur et en transmettant le MediaItem correspondant, comme décrit ci-dessus.

Lors de la création de MediaItem, MediaItem.localConfiguration.streamKeys doit être définie pour correspondre à celles du DownloadRequest afin que le joueur n'essaie lire le sous-ensemble de titres qui ont été téléchargés. En utilisant Download.request.toMediaItem et DownloadRequest.toMediaItem pour créer MediaItem s'en chargera pour vous.