पसंद के मुताबिक बनाएं

ExoPlayer लाइब्रेरी का मुख्य हिस्सा, Player इंटरफ़ेस है. Player में मीडिया प्लेयर के सामान्य फ़ंक्शन होते हैं. जैसे, मीडिया को बफ़र करना, चलाना, रोकना, और आगे-पीछे करना. डिफ़ॉल्ट रूप से लागू किए गए ExoPlayer को इस तरह से डिज़ाइन किया गया है कि यह कुछ ही बातों को ध्यान में रखता है. इसलिए, यह कुछ ही पाबंदियां लगाता है. जैसे, किस तरह का मीडिया चलाया जा रहा है, उसे कैसे और कहां सेव किया गया है, और उसे कैसे रेंडर किया गया है. मीडिया को सीधे तौर पर लोड और रेंडर करने के बजाय, ExoPlayer इस काम को उन कॉम्पोनेंट को सौंपते हैं जिन्हें प्लेयर बनाते समय या प्लेयर को नए मीडिया सोर्स पास करते समय इंजेक्ट किया जाता है. सभी ExoPlayer इंटिग्रेशन में ये कॉम्पोनेंट शामिल होते हैं:

  • MediaSource ऐसे इंस्टेंस होते हैं जो मीडिया को चलाने, मीडिया को लोड करने, और लोड किए गए मीडिया को पढ़ने के लिए तय करते हैं. MediaSource इंस्टेंस, प्लेयर में मौजूद MediaSource.Factory की मदद से MediaItem से बनाया जाता है. इन्हें मीडिया सोर्स पर आधारित प्लेलिस्ट एपीआई का इस्तेमाल करके, सीधे प्लेयर को भी पास किया जा सकता है.
  • MediaSource.Factory का एक ऐसा उदाहरण जो MediaItem को MediaSource में बदलता है. प्लेयर बनाए जाने पर, MediaSource.Factory को इंजेक्ट किया जाता है.
  • Renderer ऐसे इंस्टेंस जो मीडिया के अलग-अलग कॉम्पोनेंट रेंडर करते हैं. इन्हें प्लेयर बनाते समय इंजेक्ट किया जाता है.
  • एक TrackSelector, जो MediaSource के उपलब्ध कराए गए ट्रैक चुनता है, ताकि हर उपलब्ध Renderer उनका इस्तेमाल कर सके. प्लेयर बनाते समय, TrackSelector को इंजेक्ट किया जाता है.
  • एक LoadControl, जो यह कंट्रोल करता है कि MediaSource कब ज़्यादा मीडिया बफ़र करे और कितना मीडिया बफ़र करे. खिलाड़ी के बनाए जाने पर, LoadControl को इंजेक्ट किया जाता है.
  • यह एक LivePlaybackSpeedControl है. यह लाइव स्ट्रीम के दौरान, वीडियो चलाने की स्पीड को कंट्रोल करता है, ताकि प्लेयर को कॉन्फ़िगर किए गए लाइव ऑफ़सेट के आस-पास रखा जा सके. खिलाड़ी बनाए जाने पर, A LivePlaybackSpeedControl को इंजेक्ट किया जाता है.

लाइब्रेरी में, प्लेयर की सुविधाओं को लागू करने वाले कॉम्पोनेंट को इंजेक्ट करने का कॉन्सेप्ट मौजूद है. कुछ कॉम्पोनेंट के डिफ़ॉल्ट तरीके, काम को आगे इंजेक्ट किए गए कॉम्पोनेंट को सौंपते हैं. इससे कई उप-कंपोनेंट को अलग-अलग तरीके से लागू किया जा सकता है. इन्हें कस्टम तरीके से कॉन्फ़िगर किया जाता है.

प्लेयर को पसंद के मुताबिक बनाना

कॉम्पोनेंट इंजेक्ट करके प्लेयर को पसंद के मुताबिक बनाने के कुछ सामान्य उदाहरण यहां दिए गए हैं.

नेटवर्क स्टैक को कॉन्फ़िगर करना

हमने ExoPlayer के इस्तेमाल किए गए नेटवर्क स्टैक को पसंद के मुताबिक बनाने के बारे में एक पेज बनाया है.

नेटवर्क से लोड किए गए डेटा को कैश मेमोरी में सेव करना

कुछ समय के लिए, तुरंत कैश मेमोरी में सेव करने की सुविधा और मीडिया डाउनलोड करने से जुड़ी गाइड देखें.

सर्वर इंटरैक्शन को पसंद के मुताबिक बनाना

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

यहां दिए गए उदाहरण में, DefaultMediaSourceFactory में कस्टम DataSource.Factory इंजेक्ट करके, इन व्यवहारों को लागू करने का तरीका बताया गया है:

Kotlin

val dataSourceFactory =
  DataSource.Factory {
    val dataSource = httpDataSourceFactory.createDataSource()
    // Set a custom authentication request header.
    dataSource.setRequestProperty("Header", "Value")
    dataSource
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

DataSource.Factory dataSourceFactory =
    () -> {
      HttpDataSource dataSource = httpDataSourceFactory.createDataSource();
      // Set a custom authentication request header.
      dataSource.setRequestProperty("Header", "Value");
      return dataSource;
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

ऊपर दिए गए कोड स्निपेट में, इंजेक्ट किया गया HttpDataSource, हर एचटीटीपी अनुरोध में हेडर "Header: Value" को शामिल करता है. एचटीटीपी सोर्स के साथ हर इंटरैक्शन के लिए, यह तरीका तय होता है.

ज़्यादा बेहतर तरीके से काम करने के लिए, ResolvingDataSource का इस्तेमाल करके, ज़रूरत के हिसाब से व्यवहार को इंजेक्ट किया जा सकता है. यहां दिए गए कोड स्निपेट में, एचटीटीपी सोर्स के साथ इंटरैक्ट करने से ठीक पहले, अनुरोध के हेडर इंजेक्ट करने का तरीका बताया गया है:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time request headers.
    dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))
  }

Java

    DataSource.Factory dataSourceFactory =
        new ResolvingDataSource.Factory(
            httpDataSourceFactory,
            // Provide just-in-time request headers.
            dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));

यूआरआई में तुरंत बदलाव करने के लिए, ResolvingDataSource का इस्तेमाल भी किया जा सकता है. इसके बारे में यहां दिए गए स्निपेट में बताया गया है:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time URI resolution logic.
    dataSpec.withUri(resolveUri(dataSpec.uri))
  }

Java

DataSource.Factory dataSourceFactory =
    new ResolvingDataSource.Factory(
        httpDataSourceFactory,
        // Provide just-in-time URI resolution logic.
        dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));

गड़बड़ी ठीक करने की सुविधा को पसंद के मुताबिक बनाना

कस्टम LoadErrorHandlingPolicy लागू करने से, ऐप्लिकेशन को यह तय करने की अनुमति मिलती है कि ExoPlayer, लोड करने से जुड़ी गड़बड़ियों पर कैसे प्रतिक्रिया देगा. उदाहरण के लिए, कोई ऐप्लिकेशन कई बार कोशिश करने के बजाय, तुरंत फ़ेल हो सकता है. इसके अलावा, वह बैक-ऑफ़ लॉजिक को पसंद के मुताबिक बना सकता है. यह लॉजिक कंट्रोल करता है कि हर बार कोशिश करने के बीच, प्लेयर को कितने समय तक इंतज़ार करना होगा. यहां दिए गए स्निपेट में, कस्टम बैक-ऑफ़ लॉजिक को लागू करने का तरीका बताया गया है:

Kotlin

val loadErrorHandlingPolicy: LoadErrorHandlingPolicy =
  object : DefaultLoadErrorHandlingPolicy() {
    override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long {
      // Implement custom back-off logic here.
      return 0
    }
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
    )
    .build()

Java

LoadErrorHandlingPolicy loadErrorHandlingPolicy =
    new DefaultLoadErrorHandlingPolicy() {
      @Override
      public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
        // Implement custom back-off logic here.
        return 0;
      }
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy))
        .build();

LoadErrorInfo आर्ग्युमेंट में, लोड न हो पाने की वजह के बारे में ज़्यादा जानकारी होती है. इससे गड़बड़ी के टाइप या अनुरोध पूरा न होने के आधार पर लॉजिक को पसंद के मुताबिक बनाया जा सकता है.

एक्सट्रैक्टर फ़्लैग को पसंद के मुताबिक बनाना

एक्सट्रैक्टर फ़्लैग का इस्तेमाल करके, यह तय किया जा सकता है कि प्रोग्रेसिव मीडिया से अलग-अलग फ़ॉर्मैट कैसे निकाले जाएं. इन्हें DefaultExtractorsFactory पर सेट किया जा सकता है, जो DefaultMediaSourceFactory को दिया जाता है. इस उदाहरण में, एक फ़्लैग पास किया गया है. इससे MP3 स्ट्रीम के लिए इंडेक्स के आधार पर सीकिंग की सुविधा चालू होती है.

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING)
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory))
    .build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING);

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

कॉन्स्टेंट बिटरेट सीकिंग की सुविधा चालू करना

MP3, ADTS, और AMR स्ट्रीम के लिए, FLAG_ENABLE_CONSTANT_BITRATE_SEEKING फ़्लैग का इस्तेमाल करके, अनुमानित सीकिंग की सुविधा चालू की जा सकती है. इसके लिए, आपको बिटरेट को स्थिर रखना होगा. इन फ़्लैग को अलग-अलग एक्सट्रैक्टर के लिए सेट किया जा सकता है. इसके लिए, ऊपर बताए गए individual DefaultExtractorsFactory.setXyzExtractorFlags तरीकों का इस्तेमाल करें. जिन एक्सट्रैक्टर में कॉन्स्टेंट बिटरेट सीकिंग की सुविधा काम करती है उन सभी के लिए इसे चालू करने के लिए, DefaultExtractorsFactory.setConstantBitrateSeekingEnabled का इस्तेमाल करें.

Kotlin

val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);

इसके बाद, ExtractorsFactory को DefaultMediaSourceFactory के ज़रिए इंजेक्ट किया जा सकता है. इसके बारे में, ऊपर एक्सट्रैक्टर फ़्लैग को पसंद के मुताबिक बनाने के लिए बताया गया है.

एसिंक्रोनस बफ़र क्यूइंग की सुविधा चालू करना

एसिंक्रोनस बफ़र क्यूइंग, ExoPlayer की रेंडरिंग पाइपलाइन में किया गया एक सुधार है. यह MediaCodec इंस्टेंस को एसिंक्रोनस मोड में चलाता है. साथ ही, डेटा को डिकोड और रेंडर करने के लिए अतिरिक्त थ्रेड का इस्तेमाल करता है. इसे चालू करने पर, फ़्रेम ड्रॉप होने और ऑडियो अंडररन की समस्या कम हो सकती है.

एसिंक्रोनस बफ़र क्यूइंग की सुविधा, Android 12 (एपीआई लेवल 31) और इसके बाद के वर्शन वाले डिवाइसों पर डिफ़ॉल्ट रूप से चालू होती है. इसे Android 6.0 (एपीआई लेवल 23) और इसके बाद के वर्शन वाले डिवाइसों पर मैन्युअल तरीके से चालू किया जा सकता है. उन डिवाइसों पर इस सुविधा को चालू करें जिन पर आपको फ़्रेम ड्रॉप होने या ऑडियो में रुकावट आने की समस्या दिखती है. खास तौर पर, डीआरएम से सुरक्षित या ज़्यादा फ़्रेम रेट वाला कॉन्टेंट चलाते समय ऐसा करें.

सबसे आसान मामले में, आपको प्लेयर में DefaultRenderersFactory को इस तरह इंजेक्ट करना होगा:

Kotlin

val renderersFactory = 
  DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing()
val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()

Java

DefaultRenderersFactory renderersFactory =
    new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing();
ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();

अगर रेंडरर को सीधे तौर पर इंस्टैंटिएट किया जा रहा है, तो MediaCodecVideoRenderer और MediaCodecAudioRenderer कंस्ट्रक्टर को new DefaultMediaCodecAdapter.Factory(context).forceEnableAsynchronous() पास करें.

ForwardingSimpleBasePlayer की मदद से कार्रवाइयों को पसंद के मुताबिक बनाना

ForwardingSimpleBasePlayer की सबक्लास में रैप करके, Player इंस्टेंस के कुछ व्यवहारों को पसंद के मुताबिक बनाया जा सकता है. इस क्लास की मदद से, सीधे तौर पर Player के तरीकों को लागू करने के बजाय, कुछ खास 'ऑपरेशन' को इंटरसेप्ट किया जा सकता है. इससे यह पक्का किया जाता है कि play(), pause(), और setPlayWhenReady(boolean) जैसे कॉम्पोनेंट एक जैसा काम करें. इससे यह भी पक्का होता है कि स्टेटस में हुए सभी बदलाव, रजिस्टर किए गए Player.Listener इंस्टेंस को सही तरीके से भेजे गए हैं. ज़्यादातर मामलों में, एक जैसे नतीजे मिलने की गारंटी की वजह से, ForwardingSimpleBasePlayer को ForwardingPlayer के मुकाबले प्राथमिकता दी जानी चाहिए. ऐसा इसलिए, क्योंकि ForwardingPlayer में गड़बड़ी होने की आशंका ज़्यादा होती है.

उदाहरण के लिए, इमेज के क्रम वाला ऐनिमेशन शुरू या बंद होने पर, कुछ कस्टम लॉजिक जोड़ने के लिए:

Kotlin

class PlayerWithCustomPlay(player: Player) : ForwardingSimpleBasePlayer(player) {
  override fun handleSetPlayWhenReady(playWhenReady: Boolean): ListenableFuture<*> {
    // Add custom logic
    return super.handleSetPlayWhenReady(playWhenReady)
  }
}

Java

class PlayerWithCustomPlay extends ForwardingSimpleBasePlayer {

  public PlayerWithCustomPlay(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
    // Add custom logic
    return super.handleSetPlayWhenReady(playWhenReady);
  }
}

इसके अलावा, SEEK_TO_NEXT कमांड को अनुमति न देने के लिए (और यह पक्का करने के लिए कि SEEK_TO_NEXT कोई कार्रवाई नहीं करता है):Player.seekToNext

Kotlin

class PlayerWithoutSeekToNext(player: Player) : ForwardingSimpleBasePlayer(player) {
  override fun getState(): State {
    val state = super.getState()
    return state
      .buildUpon()
      .setAvailableCommands(
        state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build()
      )
      .build()
  }

  // We don't need to override handleSeek, because it is guaranteed not to be called for
  // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable.
}

Java

class PlayerWithoutSeekToNext extends ForwardingSimpleBasePlayer {

  public PlayerWithoutSeekToNext(Player player) {
    super(player);
  }

  @Override
  protected State getState() {
    State state = super.getState();
    return state
        .buildUpon()
        .setAvailableCommands(
            state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build())
        .build();
  }

  // We don't need to override handleSeek, because it is guaranteed not to be called for
  // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable.
}

MediaSource को पसंद के मुताबिक बनाना

ऊपर दिए गए उदाहरणों में, सभी MediaItem ऑब्जेक्ट के लिए पसंद के मुताबिक बनाए गए कॉम्पोनेंट डाले जाते हैं. इन ऑब्जेक्ट को प्लेयर को पास किया जाता है, ताकि उन्हें चलाया जा सके. अगर आपको ज़्यादा बारीकी से बदलाव करने हैं, तो अलग-अलग MediaSource इंस्टेंस में पसंद के मुताबिक बनाए गए कॉम्पोनेंट भी जोड़े जा सकते हैं. इन्हें सीधे तौर पर प्लेयर को पास किया जा सकता है. यहां दिए गए उदाहरण में, कस्टम DataSource.Factory, ExtractorsFactory, और LoadErrorHandlingPolicy का इस्तेमाल करने के लिए, ProgressiveMediaSource को पसंद के मुताबिक बनाने का तरीका बताया गया है:

Kotlin

val mediaSource =
  ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
    .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
    .createMediaSource(MediaItem.fromUri(streamUri))

Java

ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
        .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
        .createMediaSource(MediaItem.fromUri(streamUri));

कस्टम कॉम्पोनेंट बनाना

लाइब्रेरी, इस्तेमाल के सामान्य उदाहरणों के लिए, इस पेज पर सबसे ऊपर दिए गए कॉम्पोनेंट के डिफ़ॉल्ट वर्शन उपलब्ध कराती है. ExoPlayer इन कॉम्पोनेंट का इस्तेमाल कर सकता है. हालांकि, अगर गैर-स्टैंडर्ड व्यवहारों की ज़रूरत है, तो कस्टम तरीके से लागू करने के लिए भी बनाया जा सकता है. कस्टम तरीके से लागू करने के कुछ उदाहरण यहां दिए गए हैं:

  • Renderer – ऐसा हो सकता है कि आपको कोई कस्टम Renderer लागू करना हो, ताकि किसी ऐसे मीडिया टाइप को हैंडल किया जा सके जिसे लाइब्रेरी में डिफ़ॉल्ट तौर पर लागू करने की सुविधा नहीं मिलती.
  • TrackSelector – कस्टम TrackSelector लागू करने से, ऐप्लिकेशन डेवलपर को यह बदलने की अनुमति मिलती है कि MediaSource से दिखाए गए ट्रैक, उपलब्ध Renderer में से हर एक के लिए कैसे चुने जाते हैं.
  • LoadControl – कस्टम LoadControl लागू करने से, ऐप्लिकेशन डेवलपर को प्लेयर की बफ़रिंग नीति बदलने की अनुमति मिलती है.
  • Extractor – अगर आपको किसी ऐसे कंटेनर फ़ॉर्मैट के लिए सहायता चाहिए जो फ़िलहाल लाइब्रेरी के साथ काम नहीं करता है, तो कस्टम Extractor क्लास लागू करें.
  • MediaSource – अगर आपको रेंडरर को कस्टम तरीके से मीडिया सैंपल देने हैं या कस्टम MediaSource कंपोज़िटिंग का तरीका लागू करना है, तो कस्टम MediaSource क्लास लागू करना सही हो सकता है.
  • MediaSource.Factory – कस्टम MediaSource.Factory लागू करने से, ऐप्लिकेशन को यह तय करने की अनुमति मिलती है कि MediaItem से MediaSource कैसे बनाया जाए.
  • DataSource – ExoPlayer के अपस्ट्रीम पैकेज में, इस्तेमाल के अलग-अलग उदाहरणों के लिए पहले से ही कई DataSource लागू किए गए हैं. आपको डेटा को किसी दूसरे तरीके से लोड करने के लिए, अपनी DataSource क्लास लागू करनी पड़ सकती है. जैसे, कस्टम प्रोटोकॉल पर, कस्टम एचटीटीपी स्टैक का इस्तेमाल करके या कस्टम परसिस्टेंट कैश मेमोरी से.

कस्टम कॉम्पोनेंट बनाते समय, हमारा सुझाव है कि आप ये काम करें:

  • अगर किसी कस्टम कॉम्पोनेंट को इवेंट की जानकारी वापस ऐप्लिकेशन को देनी है, तो हमारा सुझाव है कि आप ExoPlayer के मौजूदा कॉम्पोनेंट की तरह ही मॉडल का इस्तेमाल करें. उदाहरण के लिए, EventDispatcher क्लास का इस्तेमाल करें या कॉम्पोनेंट के कंस्ट्रक्टर को लिसनर के साथ Handler पास करें.
  • हमारा सुझाव है कि कस्टम कॉम्पोनेंट, मौजूदा ExoPlayer कॉम्पोनेंट के तौर पर ही मॉडल का इस्तेमाल करें, ताकि ऐप्लिकेशन, वीडियो चलाने के दौरान उन्हें फिर से कॉन्फ़िगर कर सके. इसके लिए, कस्टम कॉम्पोनेंट को PlayerMessage.Target लागू करना चाहिए. साथ ही, handleMessage तरीके से कॉन्फ़िगरेशन में हुए बदलावों को पाना चाहिए. ऐप्लिकेशन कोड को, ExoPlayer के createMessage तरीके को कॉल करके कॉन्फ़िगरेशन में हुए बदलावों को पास करना चाहिए. साथ ही, मैसेज को कॉन्फ़िगर करना चाहिए और PlayerMessage.send का इस्तेमाल करके कॉम्पोनेंट को भेजना चाहिए. प्लेबैक थ्रेड पर डिलीवर किए जाने वाले मैसेज भेजने से यह पक्का होता है कि उन्हें प्लेयर पर किए जा रहे किसी भी अन्य ऑपरेशन के साथ क्रम से लागू किया जाए.