The two codes are very similar in their structure and logic, but there are several notable differences:

1. Difference in the RenderersFactory

  • First code:

    renderFactory = new DefaultRenderersFactory(context);
    
    • It directly uses DefaultRenderersFactory from ExoPlayer.
  • Second code:

    renderFactory = prefs.getBoolean(
            context.getString(
                    R.string.always_use_exoplayer_set_output_surface_workaround_key), false)
            ? new CustomRenderersFactory(context) : new DefaultRenderersFactory(context);
    
    • It introduces conditional logic: if a user preference (always_use_exoplayer_set_output_surface_workaround_key) is enabled, it uses CustomRenderersFactory instead of DefaultRenderersFactory.
    • It also enables an additional option:
      renderFactory.setEnableDecoderFallback(
              prefs.getBoolean(
                      context.getString(
                              R.string.use_exoplayer_decoder_fallback_key), false));
      
      This option allows ExoPlayer to use a different decoder if the one initially planned fails.

2. Handling ExoPlayer Events

  • Present only in the second code:
    @Override
    public void onEvents(@NonNull final com.google.android.exoplayer2.Player player,
                         @NonNull final com.google.android.exoplayer2.Player.Events events) {
        Listener.super.onEvents(player, events);
        MediaItemTag.from(player.getCurrentMediaItem()).ifPresent(tag -> {
            if (tag == currentMetadata) {
                return;
            }
            final StreamInfo previousInfo = Optional.ofNullable(currentMetadata)
                    .flatMap(MediaItemTag::getMaybeStreamInfo).orElse(null);
            final MediaItemTag.AudioTrack previousAudioTrack =
                    Optional.ofNullable(currentMetadata)
                            .flatMap(MediaItemTag::getMaybeAudioTrack).orElse(null);
            currentMetadata = tag;
    
            if (!currentMetadata.getErrors().isEmpty()) {
                final ErrorInfo errorInfo = new ErrorInfo(
                        currentMetadata.getErrors(),
                        UserAction.PLAY_STREAM,
                        "Loading failed for [" + currentMetadata.getTitle()
                                + "]: " + currentMetadata.getStreamUrl(),
                        currentMetadata.getServiceId());
                ErrorUtil.createNotification(context, errorInfo);
            }
    
            currentMetadata.getMaybeStreamInfo().ifPresent(info -> {
                if (DEBUG) {
                    Log.d(TAG, "ExoPlayer - onEvents() update stream info: " + info.getName());
                }
                if (previousInfo == null || !previousInfo.getUrl().equals(info.getUrl())) {
                    updateMetadataWith(info);
                } else if (previousAudioTrack == null
                        || tag.getMaybeAudioTrack()
                        .map(t -> t.getSelectedAudioStreamIndex()
                                != previousAudioTrack.getSelectedAudioStreamIndex())
                        .orElse(false)) {
                    notifyAudioTrackUpdateToListeners();
                }
            });
        });
    }
    
    • It implements onEvents() to listen for ExoPlayer events.
    • It updates currentMetadata with each event and triggers updates if necessary.
    • It handles errors and sends a notification if a playback issue occurs.

3. Handling Audio Tracks

  • Present only in the second code:
    public Optional<AudioStream> getSelectedAudioStream() {
        return Optional.ofNullable(currentMetadata)
                .flatMap(MediaItemTag::getMaybeAudioTrack)
                .map(MediaItemTag.AudioTrack::getSelectedAudioStream);
    }
    
    private void notifyAudioTrackUpdateToListeners() {
        if (fragmentListener != null) {
            fragmentListener.onAudioTrackUpdate();
        }
        if (activityListener != null) {
            activityListener.onAudioTrackUpdate();
        }
    }
    
    • This section allows retrieving the selected audio track and sends updates to the listeners (fragmentListener and activityListener).
    • It is absent in the first code.

4. Handling Video Quality and Audio Tracks

  • In the first code:

    if (intent.hasExtra(PLAYBACK_QUALITY)) {
        setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY));
    }
    
  • In the second code:

    if (intent.hasExtra(PLAYBACK_QUALITY)) {
        videoResolver.setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY));
    }
    
    • The second code uses videoResolver.setPlaybackQuality(), suggesting that it employs a more centralized method to manage playback quality.
  • Added in the second code:

    public void setAudioTrack(@Nullable final String audioTrackId) {
        saveStreamProgressState();
        setRecovery();
        videoResolver.setAudioTrack(audioTrackId);
        audioResolver.setAudioTrack(audioTrackId);
        reloadPlayQueueManager();
    }
    
    • It allows setting the audio track, a feature absent in the first code.

Summary of Key Differences:

  1. Advanced handling of RenderersFactory in the second code

    • It introduces CustomRenderersFactory based on a user preference and enables setEnableDecoderFallback.
  2. Handling ExoPlayer events in the second code

    • It implements onEvents() to detect stream changes and handle errors.
  3. Addition of audio track management in the second code

    • It allows retrieving and changing the currently playing audio track.
  4. Improved video quality and audio track management in the second code

    • It uses videoResolver to modify playback quality and allows changing the audio track.

Les deux codes sont très similaires dans leur structure et leur logique, mais il y a plusieurs différences notables :

1. Différence au niveau de la RenderersFactory

  • Premier code :

    renderFactory = new DefaultRenderersFactory(context);
    
    • Il utilise directement DefaultRenderersFactory de ExoPlayer.
  • Deuxième code :

    renderFactory = prefs.getBoolean(
            context.getString(
                    R.string.always_use_exoplayer_set_output_surface_workaround_key), false)
            ? new CustomRenderersFactory(context) : new DefaultRenderersFactory(context);
    
    • Il introduit une logique conditionnelle : si une préférence utilisateur (always_use_exoplayer_set_output_surface_workaround_key) est activée, il utilise CustomRenderersFactory au lieu de DefaultRenderersFactory.
    • Il active également une option supplémentaire :
      renderFactory.setEnableDecoderFallback(
              prefs.getBoolean(
                      context.getString(
                              R.string.use_exoplayer_decoder_fallback_key), false));
      
      Cette option permet à ExoPlayer d’utiliser un autre décodeur si celui prévu initialement échoue.

2. Gestion des événements ExoPlayer

  • Présent uniquement dans le deuxième code :
    @Override
    public void onEvents(@NonNull final com.google.android.exoplayer2.Player player,
                         @NonNull final com.google.android.exoplayer2.Player.Events events) {
        Listener.super.onEvents(player, events);
        MediaItemTag.from(player.getCurrentMediaItem()).ifPresent(tag -> {
            if (tag == currentMetadata) {
                return;
            }
            final StreamInfo previousInfo = Optional.ofNullable(currentMetadata)
                    .flatMap(MediaItemTag::getMaybeStreamInfo).orElse(null);
            final MediaItemTag.AudioTrack previousAudioTrack =
                    Optional.ofNullable(currentMetadata)
                            .flatMap(MediaItemTag::getMaybeAudioTrack).orElse(null);
            currentMetadata = tag;
    
            if (!currentMetadata.getErrors().isEmpty()) {
                final ErrorInfo errorInfo = new ErrorInfo(
                        currentMetadata.getErrors(),
                        UserAction.PLAY_STREAM,
                        "Loading failed for [" + currentMetadata.getTitle()
                                + "]: " + currentMetadata.getStreamUrl(),
                        currentMetadata.getServiceId());
                ErrorUtil.createNotification(context, errorInfo);
            }
    
            currentMetadata.getMaybeStreamInfo().ifPresent(info -> {
                if (DEBUG) {
                    Log.d(TAG, "ExoPlayer - onEvents() update stream info: " + info.getName());
                }
                if (previousInfo == null || !previousInfo.getUrl().equals(info.getUrl())) {
                    updateMetadataWith(info);
                } else if (previousAudioTrack == null
                        || tag.getMaybeAudioTrack()
                        .map(t -> t.getSelectedAudioStreamIndex()
                                != previousAudioTrack.getSelectedAudioStreamIndex())
                        .orElse(false)) {
                    notifyAudioTrackUpdateToListeners();
                }
            });
        });
    }
    
    • Il implémente onEvents() pour écouter les événements d’ExoPlayer.
    • Il met à jour currentMetadata à chaque événement et déclenche des mises à jour si nécessaire.
    • Il gère les erreurs et envoie une notification si un problème de lecture survient.

3. Gestion des pistes audio

  • Présent uniquement dans le deuxième code :
    public Optional<AudioStream> getSelectedAudioStream() {
        return Optional.ofNullable(currentMetadata)
                .flatMap(MediaItemTag::getMaybeAudioTrack)
                .map(MediaItemTag.AudioTrack::getSelectedAudioStream);
    }
    
    private void notifyAudioTrackUpdateToListeners() {
        if (fragmentListener != null) {
            fragmentListener.onAudioTrackUpdate();
        }
        if (activityListener != null) {
            activityListener.onAudioTrackUpdate();
        }
    }
    
    • Cette partie permet de récupérer la piste audio sélectionnée et d’envoyer des mises à jour aux listeners (fragmentListener et activityListener).
    • Elle est absente dans le premier code.

4. Gestion de la qualité vidéo et des pistes audio

  • Dans le premier code :

    if (intent.hasExtra(PLAYBACK_QUALITY)) {
        setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY));
    }
    
  • Dans le deuxième code :

    if (intent.hasExtra(PLAYBACK_QUALITY)) {
        videoResolver.setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY));
    }
    
    • Le deuxième code passe par videoResolver.setPlaybackQuality(), ce qui suggère qu’il utilise une méthode plus centralisée pour gérer la qualité de lecture.
  • Ajout dans le deuxième code :

    public void setAudioTrack(@Nullable final String audioTrackId) {
        saveStreamProgressState();
        setRecovery();
        videoResolver.setAudioTrack(audioTrackId);
        audioResolver.setAudioTrack(audioTrackId);
        reloadPlayQueueManager();
    }
    
    • Il permet de définir la piste audio, fonctionnalité absente du premier code.

Résumé des différences principales :

  1. Gestion avancée de RenderersFactory dans le deuxième code

    • Il introduit CustomRenderersFactory en fonction d'une préférence utilisateur et active setEnableDecoderFallback.
  2. Gestion des événements ExoPlayer dans le deuxième code

    • Il implémente onEvents() pour détecter les changements de flux et gérer les erreurs.
  3. Ajout de la gestion des pistes audio dans le deuxième code

    • Il permet de récupérer et changer la piste audio en cours de lecture.
  4. Gestion améliorée de la qualité vidéo et des pistes audio dans le deuxième code

    • Il passe par videoResolver pour modifier la qualité de lecture et permet de changer la piste audio.