मीडिया डाउनलोड किया जा रहा है

ExoPlayer, ऑफ़लाइन रहने पर मीडिया चलाने के लिए उसे डाउनलोड करने की सुविधा देता है. ज़्यादातर मामलों में, यह ज़रूरी होता है कि ऐप्लिकेशन के बैकग्राउंड में होने पर भी डाउनलोड जारी रहें. इन इस्तेमाल के उदाहरणों के लिए, आपके ऐप्लिकेशन को DownloadService का सबक्लास बनाना चाहिए. साथ ही, डाउनलोड जोड़ने, हटाने, और कंट्रोल करने के लिए, सेवा को निर्देश भेजने चाहिए. इस डायग्राम में, शामिल मुख्य क्लास दिखाई गई हैं.

मीडिया डाउनलोड करने के लिए क्लास. ऐरो की दिशाओं से, डेटा के फ़्लो का पता चलता है.

  • DownloadService: यह DownloadManager को रैप करता है और उस पर कमांड फ़ॉरवर्ड करता है. इस सेवा की मदद से, ऐप्लिकेशन के बैकग्राउंड में चलने के दौरान भी DownloadManager काम करता रहता है.
  • DownloadManager: यह कुकी, एक साथ कई डाउनलोड मैनेज करती है. साथ ही, DownloadIndex से उनकी स्थितियां लोड (और सेव) करती है. इसके अलावा, यह नेटवर्क कनेक्टिविटी जैसी ज़रूरी शर्तों के आधार पर डाउनलोड शुरू और बंद करती है. कॉन्टेंट डाउनलोड करने के लिए, मैनेजर आम तौर पर HttpDataSource से डाउनलोड किए जा रहे डेटा को पढ़ेगा और उसे Cache में लिखेगा.
  • DownloadIndex: डाउनलोड की स्थिति को बनाए रखता है.

DownloadService बनाना

DownloadService बनाने के लिए, इसे सबक्लास करें और इसके ऐब्स्ट्रैक्ट तरीके लागू करें:

  • getDownloadManager(): इस्तेमाल की जाने वाली DownloadManager वैल्यू दिखाता है.
  • getScheduler(): यह एक वैकल्पिक Scheduler दिखाता है. इससे सेवा को फिर से शुरू किया जा सकता है. ऐसा तब किया जा सकता है, जब डाउनलोड पूरे करने के लिए ज़रूरी शर्तें पूरी हो गई हों. ExoPlayer, इन सुविधाओं को लागू करने के तरीके उपलब्ध कराता है:
    • PlatformScheduler का इस्तेमाल करता है. यह JobScheduler का इस्तेमाल करता है. इसके लिए, कम से कम एपीआई 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 बनाना

नीचे दिए गए कोड स्निपेट में, DownloadManager को इंस्टैंशिएट करने का तरीका बताया गया है. इसे DownloadService में getDownloadManager() से वापस लाया जा सकता है:

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 पर भेजें. अडैप्टिव स्ट्रीम के लिए, DownloadRequest बनाने में मदद पाने के लिए DownloadHelper का इस्तेमाल करें. यहां दिए गए उदाहरण में, डाउनलोड करने का अनुरोध बनाने का तरीका बताया गया है:

Kotlin

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

Java

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

इस उदाहरण में, contentId कॉन्टेंट के लिए यूनीक आइडेंटिफ़ायर है. सामान्य मामलों में, contentUri को अक्सर contentId के तौर पर इस्तेमाल किया जा सकता है. हालांकि, ऐप्लिकेशन अपने इस्तेमाल के उदाहरण के हिसाब से, किसी भी आईडी स्कीम का इस्तेमाल कर सकते हैं. DownloadRequest.Builder में कुछ ऐसे सेटर भी हैं जिनका इस्तेमाल करना ज़रूरी नहीं है. उदाहरण के लिए, setKeySetId और setData का इस्तेमाल, ऐप्लिकेशन के लिए डीआरएम और कस्टम डेटा सेट करने के लिए किया जा सकता है. इस डेटा को ऐप्लिकेशन, डाउनलोड से जोड़ना चाहता है. कॉन्टेंट के MIME टाइप को setMimeType का इस्तेमाल करके भी तय किया जा सकता है. इसका इस्तेमाल उन मामलों में किया जाता है जहाँ contentUri से कॉन्टेंट टाइप का पता नहीं लगाया जा सकता.

अनुरोध बनाने के बाद, 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 की मदद से, डाउनलोड किया गया सारा डेटा भी हटाया जा सकता है.

डाउनलोड शुरू और बंद करना

डाउनलोड सिर्फ़ तब पूरा होगा, जब ये चार शर्तें पूरी होंगी:

  • डाउनलोड रुकने की कोई वजह नहीं है.
  • डाउनलोड रुकते नहीं हैं.
  • डाउनलोड की प्रोसेस पूरी करने के लिए ज़रूरी शर्तें पूरी की गई हों. ज़रूरी शर्तों में, अनुमति वाले नेटवर्क टाइप पर पाबंदियां तय की जा सकती हैं. साथ ही, यह भी तय किया जा सकता है कि डिवाइस को आइडल मोड में होना चाहिए या चार्जर से कनेक्ट होना चाहिए.
  • समानांतर डाउनलोड की अधिकतम संख्या पार नहीं की गई है.

इन सभी शर्तों को, DownloadService को निर्देश भेजकर कंट्रोल किया जा सकता है.

डाउनलोड बंद होने की वजहें सेट करना और उन्हें हटाना

एक या सभी डाउनलोड रोके जाने की वजह सेट की जा सकती है:

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 एक खास वैल्यू है. इसका मतलब है कि डाउनलोड नहीं रोका गया है). जिन ऐप्लिकेशन के डाउनलोड रुकने की कई वजहें होती हैं वे अलग-अलग वैल्यू का इस्तेमाल करके यह ट्रैक कर सकते हैं कि हर डाउनलोड क्यों रुका. सभी डाउनलोड के लिए, डाउनलोड रोकने की वजह सेट करने और उसे हटाने का तरीका, किसी एक डाउनलोड के लिए डाउनलोड रोकने की वजह सेट करने और उसे हटाने के तरीके जैसा ही होता है. हालांकि, इसमें contentId को null पर सेट करना होता है.

अगर किसी डाउनलोड के रुकने की वजह शून्य नहीं है, तो वह 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 से डेटा पढ़ा जाता है.

डाउनलोड किए गए कॉन्टेंट को चलाने के लिए, उसी CacheDataSource.Factory का इस्तेमाल करके एक CacheDataSource.Factory बनाएं जिसका इस्तेमाल डाउनलोड करने के लिए किया गया था. इसके बाद, प्लेयर बनाते समय इसे DefaultMediaSourceFactory में डालें:Cache

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, स्मूथस्ट्रीमिंग और HLS) में सामान्यतः एकाधिक मीडिया ट्रैक होते हैं. अक्सर एक ही कॉन्टेंट के कई ट्रैक होते हैं, जिनकी क्वालिटी अलग-अलग होती है. जैसे, एसडी, एचडी, और 4K वीडियो ट्रैक. ऐसा भी हो सकता है कि एक ही तरह के कई ट्रैक में अलग-अलग कॉन्टेंट हो. उदाहरण के लिए, अलग-अलग भाषाओं में कई ऑडियो ट्रैक.

स्ट्रीमिंग प्लेबैक के लिए, ट्रैक सिलेक्टर का इस्तेमाल करके यह चुना जा सकता है कि कौनसे ट्रैक चलाए जाएं. इसी तरह, डाउनलोड करने के लिए DownloadHelper का इस्तेमाल किया जा सकता है. इससे यह चुना जा सकता है कि कौनसे ट्रैक डाउनलोड किए जाएं. DownloadHelper का सामान्य इस्तेमाल करने के लिए, यह तरीका अपनाएं:

  1. DownloadHelper.Factory इंस्टेंस का इस्तेमाल करके DownloadHelper बनाएं. सहायता करने वाले व्यक्ति को तैयार करें और कॉलबैक का इंतज़ार करें.

    Kotlin

    val downloadHelper =
         DownloadHelper.Factory()
          .setRenderersFactory(DefaultRenderersFactory(context))
          .setDataSourceFactory(dataSourceFactory)
          .create(MediaItem.fromUri(contentUri))
    downloadHelper.prepare(callback)

    Java

    DownloadHelper downloadHelper =
       new DownloadHelper.Factory()
            .setRenderersFactory(new DefaultRenderersFactory(context))
            .setDataSourceFactory(dataSourceFactory)
            .create(MediaItem.fromUri(contentUri));
    downloadHelper.prepare(callback);
  2. आपके पास डिफ़ॉल्ट रूप से चुने गए ट्रैक की जांच करने का विकल्प होता है. इसके लिए, getMappedTrackInfo और getTrackSelections का इस्तेमाल करें. साथ ही, clearTrackSelections, replaceTrackSelections, और addTrackSelection का इस्तेमाल करके बदलाव करें.
  3. getDownloadRequest पर कॉल करके चयनित ट्रैक के लिए DownloadRequest बनाएं. ऊपर बताए गए तरीके से, डाउनलोड करने का अनुरोध आपके DownloadService को भेजा जा सकता है.
  4. release() का इस्तेमाल करके, हेल्पर को रिलीज़ करें.

डाउनलोड किए गए अडैप्टिव कॉन्टेंट को चलाने के लिए, प्लेयर को कॉन्फ़िगर करना और ऊपर बताए गए तरीके से, उससे जुड़ा MediaItem पास करना ज़रूरी है.

MediaItem बनाते समय, MediaItem.localConfiguration.streamKeys को DownloadRequest में मौजूद वैल्यू से मैच करने के लिए सेट किया जाना चाहिए, ताकि प्लेयर सिर्फ़ उन ट्रैक के सबसेट को चलाने की कोशिश करे जिन्हें डाउनलोड किया गया है. MediaItem बनाने के लिए Download.request.toMediaItem और DownloadRequest.toMediaItem का इस्तेमाल करने से, यह समस्या अपने-आप ठीक हो जाएगी.