खिलाड़ी से जुड़े इवेंट

खिलाड़ी की स्थिति में बदलाव होने पर (जैसे, वीडियो चलना शुरू होना, बफ़र होना या गड़बड़ियां), इवेंट ट्रिगर होते हैं. ये इवेंट, रजिस्टर किए गए Player.Listener इंस्टेंस को भेजे जाते हैं. इन इवेंट को पूर्णांक स्थिरांकों से दिखाया जाता है. इन्हें Player.Event और Player.Events से तय किया जाता है.

Player.Listener को रजिस्टर करना

खिलाड़ी से जुड़े इवेंट, रजिस्टर किए गए Player.Listener इंस्टेंस को रिपोर्ट किए जाते हैं. इस तरह के इवेंट पाने के लिए लिसनर रजिस्टर करने का तरीका:

Kotlin

// Add a listener to receive events from the player.
player.addListener(listener)

Java

// Add a listener to receive events from the player.
player.addListener(listener);

अगर Kotlin का इस्तेमाल किया जा रहा है, तो media3-common-ktx मॉड्यूल के उपलब्ध कराए गए सस्पेंडिंग एक्सटेंशन फ़ंक्शन का इस्तेमाल करके भी, कोरूटीन का इस्तेमाल करके इवेंट सुने जा सकते हैं. ऐसे में, आपको Player.Listener को रजिस्टर या अनरजिस्टर करने की ज़रूरत नहीं होगी.

Player.Listener का इस्तेमाल करके, वीडियो चलाने से जुड़े इवेंट सुनना

Player.Listener में डिफ़ॉल्ट तरीके खाली हैं. इसलिए, आपको सिर्फ़ उन तरीकों को लागू करना होगा जिनमें आपकी दिलचस्पी है. तरीकों और उन्हें कब कॉल किया जाता है, इसके बारे में पूरी जानकारी के लिए Javadoc देखें. यहाँ कुछ सबसे अहम तरीकों के बारे में ज़्यादा जानकारी दी गई है.

लिसनर के पास, अलग-अलग इवेंट के लिए कॉलबैक लागू करने या एक सामान्य onEvents कॉलबैक लागू करने का विकल्प होता है. इस कॉलबैक को एक या उससे ज़्यादा इवेंट एक साथ होने के बाद कॉल किया जाता है. अलग-अलग कामों के लिए, कौनसे विकल्प को प्राथमिकता दी जानी चाहिए, इसके बारे में जानने के लिए Individual callbacks vs onEvents देखें.

वीडियो चलाने की स्थिति में बदलाव

प्लेयर की स्थिति में हुए बदलावों की जानकारी पाने के लिए, रजिस्टर किए गए Player.Listener में onPlaybackStateChanged(@State int state) लागू करें. प्लेयर, मीडिया चलाने की इन चार स्थितियों में से किसी एक में हो सकता है:

  • Player.STATE_IDLE: यह शुरुआती स्थिति है. यह स्थिति तब होती है, जब प्लेयर बंद हो जाता है और जब वीडियो नहीं चल पाता. इस स्थिति में, प्लेयर के पास सिर्फ़ सीमित संसाधन होंगे.
  • Player.STATE_BUFFERING: प्लेयर, वीडियो को अपनी मौजूदा जगह से तुरंत नहीं चला सकता. आम तौर पर, ऐसा इसलिए होता है, क्योंकि ज़्यादा डेटा लोड करना होता है.
  • Player.STATE_READY: खिलाड़ी, गेम को अपनी मौजूदा स्थिति से तुरंत खेल सकता है.
  • Player.STATE_ENDED: प्लेयर ने सभी मीडिया आइटम चला लिए हैं.

इन राज्यों के अलावा, प्लेयर में playWhenReady फ़्लैग भी होता है. इससे पता चलता है कि उपयोगकर्ता वीडियो चलाने के लिए तैयार है. इस फ़्लैग में हुए बदलावों को onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) लागू करके पाया जा सकता है.

कोई वीडियो तब चलता है, जब ये तीनों शर्तें पूरी होती हैं:

  • प्लेयर Player.STATE_READY स्थिति में है
  • playWhenReady की कीमत अब true है
  • Player.getPlaybackSuppressionReason से मिली जानकारी के आधार पर, वीडियो चलाने की सुविधा को बंद नहीं किया गया है

इन प्रॉपर्टी की अलग-अलग जाँच करने के बजाय, Player.isPlaying को कॉल किया जा सकता है. इस स्थिति में हुए बदलावों को onIsPlayingChanged(boolean isPlaying) लागू करके पाया जा सकता है:

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onIsPlayingChanged(isPlaying: Boolean) {
      if (isPlaying) {
        // Active playback.
      } else {
        // Not playing because playback is paused, ended, suppressed, or the player
        // is buffering, stopped or failed. Check player.playWhenReady,
        // player.playbackState, player.playbackSuppressionReason and
        // player.playerError for details.
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onIsPlayingChanged(boolean isPlaying) {
        if (isPlaying) {
          // Active playback.
        } else {
          // Not playing because playback is paused, ended, suppressed, or the player
          // is buffering, stopped or failed. Check player.getPlayWhenReady,
          // player.getPlaybackState, player.getPlaybackSuppressionReason and
          // player.getPlaybackError for details.
        }
      }
    });

प्‍लेबैक त्रुटियां

मीडिया चलाने में आने वाली गड़बड़ियों की सूचना पाने के लिए, रजिस्टर किए गए Player.Listener में onPlayerError(PlaybackException error) को लागू करें. जब कोई गड़बड़ी होती है, तो इस तरीके को तुरंत कॉल किया जाएगा. ऐसा तब होगा, जब वीडियो चलाने की स्थिति Player.STATE_IDLE में बदल जाएगी. अगर वीडियो नहीं चल रहा है या रुक गया है, तो ExoPlayer.prepare को कॉल करके वीडियो को फिर से चलाने का अनुरोध किया जा सकता है.

ध्यान दें कि कुछ Player लागू करने के तरीके, गड़बड़ी के बारे में ज़्यादा जानकारी देने के लिए PlaybackException की सबक्लास के इंस्टेंस पास करते हैं. उदाहरण के लिए, ExoPlayer ExoPlaybackException को पास करता है. इसमें type, rendererIndex, और ExoPlayer से जुड़े अन्य फ़ील्ड होते हैं.

यहां दिए गए उदाहरण में, यह पता लगाने का तरीका बताया गया है कि एचटीटीपी नेटवर्क से जुड़ी समस्या की वजह से, मीडिया नहीं चल पाया:

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onPlayerError(error: PlaybackException) {
      val cause = error.cause
      if (cause is HttpDataSourceException) {
        // An HTTP error occurred.
        val httpError = cause
        // It's possible to find out more about the error both by casting and by querying
        // the cause.
        if (httpError is InvalidResponseCodeException) {
          // Cast to InvalidResponseCodeException and retrieve the response code, message
          // and headers.
        } else {
          // Try calling httpError.getCause() to retrieve the underlying cause, although
          // note that it may be null.
        }
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onPlayerError(PlaybackException error) {
        @Nullable Throwable cause = error.getCause();
        if (cause instanceof HttpDataSourceException) {
          // An HTTP error occurred.
          HttpDataSourceException httpError = (HttpDataSourceException) cause;
          // It's possible to find out more about the error both by casting and by querying
          // the cause.
          if (httpError instanceof HttpDataSource.InvalidResponseCodeException) {
            // Cast to InvalidResponseCodeException and retrieve the response code, message
            // and headers.
          } else {
            // Try calling httpError.getCause() to retrieve the underlying cause, although
            // note that it may be null.
          }
        }
      }
    });

प्लेलिस्ट के ट्रांज़िशन

जब भी प्लेयर, प्लेलिस्ट में मौजूद किसी नए मीडिया आइटम पर स्विच करता है, तब रजिस्टर किए गए Player.Listener ऑब्जेक्ट पर onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) को कॉल किया जाता है. इस वजह से पता चलता है कि ट्रांज़िशन अपने-आप हुआ, सीक किया गया (उदाहरण के लिए, player.next() को कॉल करने के बाद), एक ही आइटम को दोहराया गया या प्लेलिस्ट में बदलाव की वजह से हुआ (उदाहरण के लिए, अगर अभी चल रहे आइटम को हटा दिया जाता है).

मेटाडेटा

player.getCurrentMediaMetadata() से मिले मेटाडेटा में कई वजहों से बदलाव हो सकता है. जैसे: प्लेलिस्ट ट्रांज़िशन, इन-स्ट्रीम मेटाडेटा के अपडेट या मौजूदा MediaItem को वीडियो के बीच में अपडेट करना.

अगर आपको मेटाडेटा में हुए बदलावों के बारे में जानना है, तो onMediaMetadataChanged को सुनें. उदाहरण के लिए, मौजूदा टाइटल दिखाने वाले यूज़र इंटरफ़ेस (यूआई) को अपडेट करने के लिए.

वीडियो नियंत्रण चालू है

Player.seekTo तरीकों को कॉल करने से, रजिस्टर किए गए Player.Listener इंस्टेंस को कॉलबैक की एक सीरीज़ मिलती है:

  1. onPositionDiscontinuity में reason=DISCONTINUITY_REASON_SEEK की सदस्यता लें. यह Player.seekTo को कॉल करने का नतीजा है. कॉल बैक में, सीक करने से पहले और बाद की पोज़िशन के लिए PositionInfo फ़ील्ड होते हैं.
  2. onPlaybackStateChanged के साथ, तुरंत स्थिति में बदलाव करने से जुड़ी कोई भी कार्रवाई. ध्यान दें कि ऐसा बदलाव नहीं भी हो सकता है.

व्यक्तिगत कॉलबैक बनाम onEvents

पॉडकास्ट सुनने वाले लोग, अलग-अलग कॉलबैक लागू करने का विकल्प चुन सकते हैं. जैसे, onIsPlayingChanged(boolean isPlaying) और सामान्य onEvents(Player player, Events events) कॉलबैक. जेनेरिक कॉलबैक, Player ऑब्जेक्ट को ऐक्सेस करने की सुविधा देता है. साथ ही, एक साथ होने वाले events का सेट तय करता है. यह कॉलबैक, हमेशा उन कॉलबैक के बाद कॉल किया जाता है जो अलग-अलग इवेंट से जुड़े होते हैं.

Kotlin

override fun onEvents(player: Player, events: Player.Events) {
  if (
    events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) ||
      events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)
  ) {
    uiModule.updateUi(player)
  }
}

Java

@Override
public void onEvents(Player player, Events events) {
  if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)
      || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
    uiModule.updateUi(player);
  }
}

इन मामलों में, अलग-अलग इवेंट का इस्तेमाल करना बेहतर होता है:

  • लिसनर को बदलावों की वजहों के बारे में जानना है. उदाहरण के लिए, onPlayWhenReadyChanged या onMediaItemTransition के लिए दिए गए कारण.
  • लिसनर, सिर्फ़ कॉलबैक पैरामीटर के ज़रिए दी गई नई वैल्यू पर काम करता है या किसी ऐसी चीज़ को ट्रिगर करता है जो कॉलबैक पैरामीटर पर निर्भर नहीं होती.
  • लिसनर को लागू करने के लिए, यह ज़रूरी है कि इवेंट को ट्रिगर करने वाले कॉम्पोनेंट का नाम, तरीके के नाम में साफ़ तौर पर लिखा हो.
  • लिसनर, उस Analytics सिस्टम को रिपोर्ट करता है जिसे सभी अलग-अलग इवेंट और स्थिति में हुए बदलावों के बारे में जानना होता है.

इन मामलों में, सामान्य onEvents(Player player, Events events) का इस्तेमाल करना चाहिए:

  • लिसनर को कई इवेंट के लिए एक ही लॉजिक ट्रिगर करना है. उदाहरण के लिए, onPlaybackStateChanged और onPlayWhenReadyChanged, दोनों के लिए यूज़र इंटरफ़ेस (यूआई) को अपडेट करना.
  • लिसनर को Player ऑब्जेक्ट को ऐक्सेस करने की ज़रूरत होती है, ताकि वह आगे के इवेंट ट्रिगर कर सके. उदाहरण के लिए, मीडिया आइटम के ट्रांज़िशन के बाद ढूंढना.
  • लिसनर को एक साथ कई स्टेट वैल्यू का इस्तेमाल करना है. ये वैल्यू, अलग-अलग कॉलबैक के ज़रिए रिपोर्ट की जाती हैं. इसके अलावा, लिसनर को Player getter तरीकों के साथ इनका इस्तेमाल करना है. उदाहरण के लिए, onTimelineChanged में दिए गए Timeline के साथ Player.getCurrentWindowIndex() का इस्तेमाल सिर्फ़ onEvents कॉलबैक के अंदर सुरक्षित होता है.
  • लिसनर को यह पता लगाने में दिलचस्पी है कि इवेंट एक साथ हुए हैं या नहीं. उदाहरण के लिए, मीडिया आइटम के ट्रांज़िशन की वजह से onPlaybackStateChanged से STATE_BUFFERING तक.

कुछ मामलों में, सुनने वालों को अलग-अलग कॉलबैक को सामान्य onEvents कॉलबैक के साथ जोड़ना पड़ सकता है. उदाहरण के लिए, onEvents के साथ मीडिया आइटम में बदलाव की वजहें रिकॉर्ड करने के लिए. हालांकि, सभी स्थितियों में बदलावों का इस्तेमाल एक साथ onEvents में किया जा सकता है.onMediaItemTransition

कोरूटीन का इस्तेमाल करके, वीडियो चलाने से जुड़े इवेंट सुनना

इसके अलावा, Player.listenTo का इस्तेमाल करके Kotlin कोरूटीन लॉन्च किया जा सकता है. साथ ही, इससे जुड़ा Player.Event तय किया जा सकता है:

ध्यान दें कि Player.listen और Player.listenTo को किसी भी थ्रेड से कॉल किया जा सकता है. वहीं, कॉलबैक लैम्डा को हमेशा Player.getApplicationLooper से जुड़ी थ्रेड पर लागू किया जाता है. इसलिए, कॉलबैक लैम्डा के अंदर Player के तरीकों और स्थिति की प्रॉपर्टी को ऐक्सेस करना सुरक्षित है. भले ही, कोरूटीन को किसी दूसरे थ्रेड पर लॉन्च किया गया हो.

वीडियो चलाने की स्थिति में बदलाव

coroutineScope.launch {
  player.listenTo(Player.EVENT_IS_PLAYING_CHANGED) {
    // `Player` is a receiver scope for this trailing lambda
    if (isPlaying) {
      // Active playback.
    } else {
      // Not playing.
    }
  }
}

प्‍लेबैक त्रुटियां

coroutineScope.launch {
  player.listenTo(Player.EVENT_PLAYER_ERROR) {
    val error = playerError ?: return@listenTo
    val cause = error.cause
    if (cause is HttpDataSourceException) {
      // An HTTP error occurred.
      if (cause is InvalidResponseCodeException) {
        // Retrieve the response code, message and headers
      } else {
        // Try calling cause.cause to retrieve the underlying cause
      }
    }
  }
}

व्यक्तिगत कॉलबैक बनाम onEvents

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

सुनें

coroutineScope.launch {
  player.listen { events ->
    // `Player` is a receiver scope for this trailing lambda
    if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
      // Access the player state directly from the receiver
      updateUi(playbackState)
    }

    if (events.contains(Player.EVENT_PLAYER_ERROR)) {
      // Access the error directly from the player
      handleError(playerError)
    }
  }
}

listenTo

coroutineScope.launch {
  player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events ->
    // `Player` is a receiver scope for this trailing lambda
    if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
      // Access the player state directly from the receiver
      updateUi(playbackState)
    }

    if (events.contains(Player.EVENT_PLAYER_ERROR)) {
      // Access the error directly from the player
      handleError(playerError)
    }
  }
}

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

coroutineScope.launch {
  player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events ->
    // Unclear which event got triggered without querying `events` parameter
    // The following function will fire whenever either one is caught
    updateUiAndHandleError(playbackState, playerError)
  }
}

ये फ़ंक्शन onEvents पर काम करते हैं. इसलिए, ये उन अस्थायी तर्कों का ऐक्सेस नहीं देते जिन्हें अलग-अलग कॉलबैक में पास किया जाता है. जैसे, onMediaItemTransition(..., int reason) में वजह या onPositionDiscontinuity(...) में oldPosition. अगर आपका लॉजिक इन खास तर्कों पर निर्भर करता है और ये Player पर स्टेट प्रॉपर्टी के तौर पर उपलब्ध नहीं हैं, तो आपको स्टैंडर्ड Player.Listener इंटरफ़ेस का इस्तेमाल करना चाहिए.

AnalyticsListener का इस्तेमाल करें

ExoPlayer का इस्तेमाल करते समय, addAnalyticsListener को कॉल करके, AnalyticsListener को प्लेयर के साथ रजिस्टर किया जा सकता है. AnalyticsListener लागू करने पर, ज़्यादा जानकारी वाले इवेंट को सुना जा सकता है. ये इवेंट, आंकड़ों और लॉगिंग के लिए काम के हो सकते हैं. ज़्यादा जानकारी के लिए, कृपया Analytics पेज देखें.

EventLogger का इस्तेमाल करें

EventLogger एक AnalyticsListener है, जिसे लॉगिंग के लिए सीधे तौर पर लाइब्रेरी उपलब्ध कराती है. एक लाइन में काम की अतिरिक्त लॉगिंग चालू करने के लिए, किसी ExoPlayer में EventLogger जोड़ें:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

ज़्यादा जानकारी के लिए, डीबग लॉगिंग पेज देखें.

वीडियो चलाने की तय की गई जगहों पर इवेंट ट्रिगर करना

कुछ मामलों में, वीडियो चलाने की तय की गई पोज़िशन पर इवेंट ट्रिगर करने की ज़रूरत होती है. PlayerMessage का इस्तेमाल करके, इस सुविधा का फ़ायदा लिया जा सकता है. ExoPlayer.createMessage का इस्तेमाल करके, PlayerMessage बनाया जा सकता है. PlayerMessage.setPosition का इस्तेमाल करके, वीडियो चलाने की उस पोज़िशन को सेट किया जा सकता है जिस पर इसे लागू किया जाना चाहिए. मैसेज डिफ़ॉल्ट रूप से, प्लेबैक थ्रेड पर लागू होते हैं. हालांकि, PlayerMessage.setLooper का इस्तेमाल करके, इन्हें अपनी पसंद के मुताबिक बनाया जा सकता है. PlayerMessage.setDeleteAfterDelivery का इस्तेमाल यह कंट्रोल करने के लिए किया जा सकता है कि वीडियो चलाने की तय की गई पोज़िशन पर पहुंचने पर, मैसेज हर बार दिखेगा या सिर्फ़ पहली बार. ऐसा हो सकता है कि वीडियो को आगे-पीछे करने और उसे बार-बार चलाने के मोड की वजह से, वीडियो चलाने की तय की गई पोज़िशन पर कई बार पहुंचा जाए. PlayerMessage को कॉन्फ़िगर करने के बाद, PlayerMessage.send का इस्तेमाल करके इसे शेड्यूल किया जा सकता है.

Kotlin

player
  .createMessage { messageType: Int, payload: Any? -> }
  .setLooper(Looper.getMainLooper())
  .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000)
  .setPayload(customPayloadData)
  .setDeleteAfterDelivery(false)
  .send()

Java

player
    .createMessage(
        (messageType, payload) -> {
          // Do something at the specified playback position.
        })
    .setLooper(Looper.getMainLooper())
    .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000)
    .setPayload(customPayloadData)
    .setDeleteAfterDelivery(false)
    .send();