Video flashes black just before playing (ExoPlayer)
Asked Answered
O

1

8

I'm using ExoPlayer in a RecyclerView.

The way I have it working now is:

  • Using only one ExoPlayer instance
  • When the RecyclerView item comes into view, it prepares the player and attaches the PlayerView to the layout
  • When the RecyclerView item goes out of view, it calls setPlayer(null) and removes the PlayerView from the layout

However, when scrolling through my RecyclerView, each video flashes black very briefly (milliseconds) just before playing, as you can see in the video below:

https://user-images.githubusercontent.com/30815862/128436800-d2153174-e768-4181-9fc0-19d9dec52efb.mp4

What am I doing wrong? What can I change to prevent this black screen from flashing before the video plays?

Here are the relevant parts of my RecyclerView adapter:

public class PostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context context;
    private SimpleExoPlayer exoPlayer;
    private HlsMediaSource.Factory hlsMediaSourceFactory;

    public PostAdapter(Context context) {
        this.context = context;
        this.exoPlayer = new SimpleExoPlayer.Builder(context)
                .build();
        this.exoPlayer.setVideoTextureView(new TextureView(context));
        this.hlsMediaSourceFactory = new HlsMediaSource.Factory(CustomMediaSourceFactory.buildMediaSourceFactory())
                .setAllowChunklessPreparation(true); // TODO: Look into this
    }

    public class PostViewHolder extends RecyclerView.ViewHolder {
        public ConstraintLayout videoContainer;
        public ImageView videoThumbnail;
        public HlsMediaSource hlsMediaSource;
        public FrameLayout playerViewContainer;
        public PlayerView playerView;
        public Player.Listener playerListener;

        public PostViewHolder(View v) {
            super(v);

            videoContainer = v.findViewById(R.id.video_container);
            videoThumbnail = v.findViewById(R.id.video_thumbnail);
            playerViewContainer = v.findViewById(R.id.player_view_container);

            playerView = new PlayerView(context);
            playerView.setUseController(false);
            playerView.setKeepContentOnPlayerReset(true);
        }

        public void prepareVideo() {
            if (exoPlayer == null) {
                return;
            }

            if (exoPlayer.getCurrentMediaItem() != hlsMediaSource.getMediaItem()) {
                playerListener = new Player.Listener() {
                    @Override
                    public void onPlaybackStateChanged(int state) {
                        if (state == Player.STATE_READY) {
                            int index = playerViewContainer.indexOfChild(playerView);
                            if (index == -1) {
                                // Add the player view to the layout
                                playerViewContainer.addView(playerView);
                                playerView.setVisibility(View.VISIBLE);
                            }
                        }
                    }
                };

                exoPlayer.addListener(playerListener);

                exoPlayer.setVolume(0);
                exoPlayer.setRepeatMode(Player.REPEAT_MODE_ALL);

                // Set the media item to be played.
                exoPlayer.setMediaSource(hlsMediaSource);

                // Prepare the player
                exoPlayer.prepare();

                // Attach the player to the view
                playerView.setPlayer(exoPlayer);
            }

            // Play
            playVideo();
        }

        public void playVideo() {
            if (exoPlayer != null) {
                exoPlayer.play();
            }
        }

        // This is called when the RecyclerView item comes into view
        public void attachPlayer() {
            // Prepare the video
            prepareVideo();
        }

        // This is called when the RecyclerView item goes out of view
        public void detachPlayer() {
            playerView.setPlayer(null);

            if (playerListener != null) {
                exoPlayer.removeListener(playerListener);
            }

            // Remove the player view from the layout
            if (playerView.getParent() != null) {
                playerView.setVisibility(View.GONE);
                ((ViewGroup) playerView.getParent()).removeView(playerView);
            }
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        final Post post = (Post) data.get(position);
        Video video = post.getVideo();

        if (video != null) {
            Glide.with(context)
                    .load(video.getThumbnailUrl())
                    .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
                    .into(holder.videoThumbnail);

            if (video.getUrls() != null && video.getUrls().getHls() != null) {
                holder.hlsMediaSource = hlsMediaSourceFactory.createMediaSource(MediaItem.fromUri(video.getUrls().getHls()));
            }
        }

        holder.videoContainer.setVisibility(View.VISIBLE);
    }
}

And here are the relevant parts of my layout:

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/video_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:visibility="gone">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="H,16:9"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <com.makeramen.roundedimageview.RoundedImageView
            android:id="@+id/video_thumbnail"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <FrameLayout
            android:id="@+id/player_view_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

Any advice is appreciated!

Offensive answered 6/8, 2021 at 0:18 Comment(1)
Have you solved it ? @OffensiveCreatinine
A
8

I hope that it's not too late. Check your Player view XML. make sure that its background is set to transparent. setting app:shutter_background_color to transparent may solve it too.

Argil answered 22/9, 2021 at 7:40 Comment(3)
yeah ,this solution solved my problem , thanksZirconia
this didn't solve issue with black screen flicking while playing video, changing sureface_type to be texture_view solved it.Debouch
Solved with setting shutter_background_color attribute to transparent color. Saved my day! Thanks!Conventioner

© 2022 - 2024 — McMap. All rights reserved.