eneim / toro

Video list auto playback made simple, specially built for RecyclerView
Apache License 2.0
1.42k stars 253 forks source link

java.lang.IllegalStateException: Player is playing while it is not in managed state: ViewHolder{3cb12e84 position=9 id=-1, oldPos=-1, pLpos:-1} #246

Open sharmadev419 opened 7 years ago

sharmadev419 commented 7 years ago

I am facing this issue while scrolling after using your updated library 3.0.0-B1.

logcat- Player is playing while it is not in managed state: ViewHolder{11638295 position=11 id=-1, oldPos=-1, pLpos:-1} java.lang.IllegalStateException: Player is playing while it is not in managed state: ViewHolder{11638295 position=11 id=-1, oldPos=-1, pLpos:-1} at im.ene.toro.widget.Container.onChildDetachedFromWindow(Container.java:185) at android.support.v7.widget.RecyclerView.dispatchChildDetached(RecyclerView.java:6819) at android.support.v7.widget.RecyclerView$5.removeViewAt(RecyclerView.java:727) at android.support.v7.widget.ChildHelper.removeViewAt(ChildHelper.java:168) at android.support.v7.widget.RecyclerView$LayoutManager.removeViewAt(RecyclerView.java:7915) at android.support.v7.widget.RecyclerView$LayoutManager.removeAndRecycleViewAt(RecyclerView.java:8187) at android.support.v7.widget.LinearLayoutManager.recycleChildren(LinearLayoutManager.java:1367) at android.support.v7.widget.LinearLayoutManager.recycleViewsFromEnd(LinearLayoutManager.java:1453) at android.support.v7.widget.LinearLayoutManager.recycleByLayoutState(LinearLayoutManager.java:1476) at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1502) at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1325) at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1061) at android.support.v7.widget.RecyclerView.scrollByInternal(RecyclerView.java:1695) at android.support.v7.widget.RecyclerView.onTouchEvent(RecyclerView.java:2883) at android.view.View.dispatchTouchEvent(View.java:8489) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2432) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2103) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2438) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2132) at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2386) at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1730) at android.app.Activity.dispatchTouchEvent(Activity.java:2757) at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:71) at com.android.internal.policy.impl.PhoneWindow$DecorView.disp

eneim commented 7 years ago

@sharmadev419 Can you give me more information on how can you reproduce this? And which version of Android you use? Thanks.

Shakir4u commented 6 years ago

I also facing the same issue. Its a random exception thrown somewhere. I am not even able to trace the problem when it comes. My log is :

05-11 11:40:10.843 11747-11747/com.hiddenug E/AndroidRuntime: FATAL EXCEPTION: main Process: com.hiddenug, PID: 11747 java.lang.IllegalStateException: Player is playing while it is not in managed state: ViewHolder{6ad59c3 position=8 id=-1, oldPos=-1, pLpos:-1} at im.ene.toro.widget.Container.onChildDetachedFromWindow(Container.java:205) at android.support.v7.widget.RecyclerView.dispatchChildDetached(RecyclerView.java:7145) at android.support.v7.widget.RecyclerView$5.removeViewAt(RecyclerView.java:794) at android.support.v7.widget.ChildHelper.removeViewAt(ChildHelper.java:168) at android.support.v7.widget.RecyclerView$LayoutManager.removeViewAt(RecyclerView.java:8260) at android.support.v7.widget.RecyclerView$LayoutManager.removeAndRecycleViewAt(RecyclerView.java:8532) at android.support.v7.widget.LinearLayoutManager.recycleChildren(LinearLayoutManager.java:1371) at android.support.v7.widget.LinearLayoutManager.recycleViewsFromStart(LinearLayoutManager.java:1417) at android.support.v7.widget.LinearLayoutManager.recycleByLayoutState(LinearLayoutManager.java:1486) at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1510) at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1333) at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1077) at android.support.v7.widget.RecyclerView.scrollByInternal(RecyclerView.java:1815) at android.support.v7.widget.RecyclerView.onTouchEvent(RecyclerView.java:3076) at android.view.View.dispatchTouchEvent(View.java:9297) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2549) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2240) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2254) at com.android.internal.policy.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2403) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1737) at android.app.Activity.dispatchTouchEvent(Activity.java:2769) at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:68) at co

sharmadev2506 commented 6 years ago

@Shakir4u I have updated the player and used ExoPlayerHelper instead of ToroPlayerHelper. I was using it with Exoplayer Please check if this is the same issue with your code.

Anilugale commented 6 years ago

it reproduces in android 7.1.1 samsung device. toro player implement in recycler view nested ways

Toro version 3.2.0 Exo player version 2.5.3

java.lang.IllegalStateException: Player is playing while it is not in managed state: ViewHolder{2340423 position=8 id=-1, oldPos=-1, pLpos:-1} at im.ene.toro.widget.Container.onChildDetachedFromWindow(Container.java:182) at android.support.v7.widget.RecyclerView.dispatchChildDetached(RecyclerView.java:7145) at android.support.v7.widget.RecyclerView$5.removeViewAt(RecyclerView.java:794) at android.support.v7.widget.ChildHelper.removeViewAt(ChildHelper.java:168) at android.support.v7.widget.RecyclerView$LayoutManager.removeViewAt(RecyclerView.java:8260) at android.support.v7.widget.RecyclerView$LayoutManager.removeAndRecycleViewAt(RecyclerView.java:8532) at android.support.v7.widget.LinearLayoutManager.recycleChildren(LinearLayoutManager.java:1371) at android.support.v7.widget.LinearLayoutManager.recycleViewsFromStart(LinearLayoutManager.java:1417) at android.support.v7.widget.LinearLayoutManager.recycleByLayoutState(LinearLayoutManager.java:1486) at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1510) at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1333) at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1077) at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:4960) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:930) at android.view.Choreographer.doCallbacks(Choreographer.java:705) at android.view.Choreographer.doFrame(Choreographer.java:637) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:916) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154)

Application is crash because of this. #bug

sharmadev2506 commented 6 years ago

@Shakir4u For me, it was also happening in some specific android device. I just updated the player at my end. After updating the player, not only this issue gets resolved but its performance has also increased. You can share your code with me or I will share mine. I think this is the only way to get it resolved.

eneim commented 6 years ago

@Anilugale can you share with me the code of your ViewHolder? Both the ViewHolder that is a nested RecyclerView, and the ViewHolder that is a Player.

There are some cases this issue were found but I could never reproduce them easily. If I can have some clue from your code it can be eliminated. Also, I guess 3.4.2 improve this a lot, though migration may be non-trivial, but please consider the latest version.

Shakir4u commented 6 years ago

Dear all , I have added these dependencies version implementation 'im.ene.toro3:toro:3.4.1' implementation 'im.ene.toro3:toro-ext-exoplayer:3.4.1'

My ViewHolder is -

 public class VideoViewHolder extends RecyclerView.ViewHolder implements  ToroPlayer {
        private final RowPopularVideoBinding binding;
        @Nullable
        View imageTobeSaved;
        @Nullable
        private Uri mediaUri;
        VideoViewHolder(View itemView) {
            super(itemView);
            binding = DataBindingUtil.bind(itemView);

            BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
            TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
            TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
            //  Create the player
            player = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
            binding.exoPlayer.setPlayer(player);
        }
        void bind(@NonNull RecyclerView.Adapter adapter, Uri item, List<Object> payloads) {
            if (item != null) {
                mediaUri = item;
            }
        }
        void bind(Uri media) {
            this.mediaUri = media;
        }
        @NonNull
        @Override
        public View getPlayerView() {
            return binding.exoPlayer;
        }
        @NonNull
        @Override
        public PlaybackInfo getCurrentPlaybackInfo() {
            return helper != null ? helper.getLatestPlaybackInfo() : new PlaybackInfo();
        }
        @Override
        public void initialize(@NonNull Container container, @Nullable PlaybackInfo playbackInfo) {
            if (helper == null) {
                assert mediaUri != null;
                helper = new SimpleExoPlayerViewHelper(container, this, mediaUri);
                muteListener.isMute(player, 0, helper);
            }
            helper.initialize(playbackInfo);
        }
        @Override
        public void release() {
            if (helper != null) {
                helper.release();
                helper = null;
            }
        }
        @Override
        public void play() {
            if (helper != null) try {
                helper.play();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        @Override
        public void pause() {
            if (helper != null) helper.pause();
        }

        @Override
        public boolean isPlaying() {
            boolean isPlaying = false;
            try {
                isPlaying = helper != null && helper.isPlaying();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return isPlaying;
        }
        @Override
        public boolean wantsToPlay() {
            isVideoShow = ToroUtil.visibleAreaOffset(this, itemView.getParent()) >= 0.50;
            if (isVideoShow) {
                if (HomeFragment.isMute) {
                    binding.exoMute.setImageResource(R.mipmap.mute_music);
                } else {
                    binding.exoMute.setImageResource(R.mipmap.unmute_music);
                }
            } else {
                if (binding.exoPlayer.getPlayer() != null) {
                    binding.exoPlayer.getPlayer().setPlayWhenReady(false);
                }
            }
            return ToroUtil.visibleAreaOffset(this, itemView.getParent()) >= 0.85;
        }

        @Override
        public int getPlayerOrder() {
            return getAdapterPosition();
        }

        @Override
        public void onSettled(Container container) {

        }
    }

I have used a toro player on a fragment inside viewPager. I actually throws exception randomly on every devices.

eneim commented 6 years ago

@Shakir4u You have setup both the helper and exoplayer ... what does it mean? And in term of user interaction, at what timing your app throws exception?

Shakir4u commented 6 years ago

sorry dear , Its a mistake of naming variable. exoPlayer is SimpleExoPlayerView which is a view from com.google.android.exoplayer2.ui.SimpleExoPlayerView and on this view I am setting a player as

player = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
            binding.exoPlayer.setPlayer(player);

Helper is object of SimpleExoPlayerViewHelper which is being used for managing state of player.

if I am not using right approch , please suggest the right one.

eneim commented 6 years ago

@Shakir4u Well, when you create a SimpleExoPlayerViewHelper, you give it the ToroPlayer and there it will request for the same SimpleExoPlayerView instance. So all the setup is handled by SimpleExoPlayerViewHelper already. Do don't need the part using ExoPlayerFactory.newSimpleInstance above.

eneim commented 6 years ago

@Shakir4u And I wonder which kind of user interaction (swipe the ViewPager? scroll the RecyclerView?) could reproduce the issue?

Shakir4u commented 6 years ago

Hello , It is random crash ,When i scroll the recycler view.

eneim commented 6 years ago

Let me take a look ...

iharshb commented 6 years ago

java.lang.IllegalStateException: Player is playing while it is not in managed state: ViewHolder{2b10cca position=11 id=-1, oldPos=-1, pLpos:-1 not recyclable(1)} faced same issue @sharmadev419 @Shakir4u

sharmadev2506 commented 6 years ago

@harshbhavsar011 Could you post your adapter class here.

iharshb commented 6 years ago

I think it occurs during my NestedPlayerViewholder ready to play then immediately changed to another viewholder that time this occurs. (Happends during onDetached viewholder)

`static class Adapter extends RecyclerView.Adapter<BaseViewHolder> {

    static final int TYPE_VIDEO = 10;
    static final int TYPE_TEXT = 20;
    static final int TYPE_IMAGE = 30;

    private LayoutInflater inflater;
    final PostList mediaList;
    Context mContext;
    private int position;

    Adapter(Context context, PostList mediaList) {
        this.mediaList = mediaList;
        this.mContext = context;
    }

    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (inflater == null || inflater.getContext() != parent.getContext()) {
            inflater = LayoutInflater.from(parent.getContext());
        }

        final View view;
        final BaseViewHolder viewHolder;

        switch (viewType) {

            case TYPE_VIDEO:
                view = inflater.inflate(NestedPlayerViewHolder.LAYOUT_RES, parent, false);
                viewHolder = new NestedPlayerViewHolder(view);
                viewHolder.setIsRecyclable(true);
                break;

            case TYPE_IMAGE:
                view = inflater.inflate(ImageViewHolder.LAYOUT_RES, parent, false);
                viewHolder = new ImageViewHolder(view);
                break;

            default:
                view = inflater.inflate(HorizontalTextViewHolder.LAYOUT_RES, parent, false);
                viewHolder = new HorizontalTextViewHolder(view);
                break;
        }

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull BaseViewHolder baseViewHolder, int position) {

        this.position = position;

        if (CommonUtils.isVideoFile(mediaList.getMedia_content().get(position).getMedia_url())) {

            int required_height = 1, required_width = 1;
            String videoWidth;
            String videoHeight = null;

            DisplayMetrics displayMetrics = new DisplayMetrics();
            ((Activity) baseViewHolder.itemView.getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
            int device_height = displayMetrics.heightPixels;
            int device_width = displayMetrics.widthPixels;

            if (mediaList.getMedia_height() == null || mediaList.getMedia_height() == null) {
                videoWidth = String.valueOf(device_width);
                videoHeight = "800";

            } else {
                videoWidth = mediaList.getMedia_width();
                videoHeight = mediaList.getMedia_height();
            }

            if (Integer.parseInt(videoWidth) == 0 && Integer.parseInt(videoHeight) == 0) {
                videoWidth = String.valueOf(device_width);
                videoHeight = "800";

            }

            required_width = device_width;
            required_height = ((Integer.parseInt(videoHeight) * device_width) / Integer.parseInt(videoWidth));
            ViewGroup.LayoutParams params = baseViewHolder.itemView.getLayoutParams();
            params.width = required_width;
            params.height = required_height;
        } else {

            // scaleImage(baseViewHolder.itemView);

        }

        baseViewHolder.bind(position, mediaList.getMedia_content().get(position));
    }

    @Override
    public int getItemViewType(int position) {

        if (CommonUtils.isVideoFile(mediaList.getMedia_content().get(position).getMedia_url())) {
            return TYPE_VIDEO;
        } else if (CommonUtils.isImageFile(mediaList.getMedia_content().get(position).getMedia_url())) {
            return TYPE_IMAGE;
        } else {
            return TYPE_TEXT;
        }
    }

    @Override
    public int getItemCount() {
        return mediaList.getMedia_content().size();
    }
}

static class StateManager implements CacheManager {

    final PostList mediaList;

    StateManager(PostList mediaList) {
        this.mediaList = mediaList;
    }

    @NonNull
    @Override
    public Object getKeyForOrder(int order) {
        if (this.mediaList.getMedia_content().size() > 0 && order <= this.mediaList.getMedia_content().size())
            return this.mediaList.getMedia_content().get(order);
        else
            return null;
    }

    @Nullable
    @Override
    public Integer getOrderForKey(@NonNull Object key) {
        return key instanceof Content.Media ? this.mediaList.getMedia_content().indexOf(key) : null;
    }
}`

` @SuppressWarnings("WeakerAccess") // public class NestedPlayerViewHolder extends BaseViewHolder implements ToroPlayer {

static final int LAYOUT_RES = R.layout.view_holder_exoplayer_nested;

ExoPlayerViewHelper helper;
Uri mediaUri;

@BindView(R.id.player)
PlayerView playerView;

@BindView(R.id.progress_bar)
ProgressBar progress_bar;

public NestedPlayerViewHolder(View itemView) {
    super(itemView);
    ButterKnife.bind(this, itemView);

}

@Override
void bind(int position, Object object) {

    try {
        PostList.MediaContentBean media = (PostList.MediaContentBean) object;
        this.mediaUri = (Uri.parse(String.valueOf(media.getMedia_url())));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@NonNull
@Override
public View getPlayerView() {
    return playerView;
}

@NonNull
@Override
public PlaybackInfo getCurrentPlaybackInfo() {
    return helper != null ? helper.getLatestPlaybackInfo() : new PlaybackInfo();
}

private ToroPlayer.EventListener toroPlayerEventListener = new EventListener() {
    @Override
    public void onBuffering() {
        // do something
        progress_bar.setVisibility(View.VISIBLE);
    }

    @Override
    public void onPlaying() {
        // do something
        progress_bar.setVisibility(View.GONE);
    }

    @Override
    public void onPaused() {
        // do something
    }

    @Override
    public void onCompleted() {
        // do something
        progress_bar.setVisibility(View.GONE);
    }
};

@Override
public void initialize(@NonNull Container container, @NonNull PlaybackInfo playbackInfo) {
    try {
        if (helper == null) {
            helper = new ExoPlayerViewHelper(this, mediaUri);
        }
        helper.initialize(container, playbackInfo);
        playerView.getPlayer().setRepeatMode(1);
        helper.addPlayerEventListener(toroPlayerEventListener);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public void play() {

    try {
        if (helper != null && !helper.isPlaying()) {
            helper.play();

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public void pause() {
    try {
        if (helper != null && helper.isPlaying()) {
            helper.pause();

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public boolean isPlaying() {
    try {
        return helper != null && helper.isPlaying();
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

@Override
public void release() {
    try {
        if (helper != null) {
            helper.addPlayerEventListener(null);
            helper.release();
            helper = null;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public boolean wantsToPlay() {
    try {
        return ToroUtil.visibleAreaOffset(this, itemView.getParent()) >= 0.50;
    } catch (Exception e) {
        e.printStackTrace();
        release();
        return false;
    }
}

@Override
public int getPlayerOrder() {
    return getAdapterPosition();
}

@Override
public String toString() {
    return "ExoPlayer{" + hashCode() + " " + getAdapterPosition() + "}";
}

}`

sharmadev2506 commented 6 years ago

@harshbhavsar011 Is your holder global to the adapter or its local to the viewholder?

iharshb commented 6 years ago

Are you talking about NestedPlayerView Holder and ImageViewHolder ? Right ? Its Local viewholder i Can not access to MediaViewHolder

sharmadev2506 commented 6 years ago

Pardon. I was asking about helper.

iharshb commented 6 years ago

I am using ExoPlayerViewHelper as per library . Should i change into that?

sharmadev2506 commented 6 years ago

class ViewHolderVideo extends RecyclerView.ViewHolder implements ToroPlayer, ExoPlayer.EventListener {

    private ExoPlayerHelper helper;
    private SimpleExoPlayerView dialogPlayerView;

    ViewHolderVideo(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);

    }

    @NonNull
    @Override
    public View getDialogPlayerView() {
        return videoView;
    }

    @NonNull
    @Override
    public PlaybackInfo getCurrentPlaybackInfo() {
        return helper != null ? helper.getPlaybackInfo() : new PlaybackInfo();
    }

    @Override
    public void initialize(@NonNull Container container, @Nullable PlaybackInfo playbackInfo) {
        if (helper == null) {
            if (getAdapterPosition() > -1) {
                if (helper == null) {
                    isPlayAgain = true;
                    helper = new ExoPlayerHelper(videoView, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON, false);
                    try {
                        MediaSourceBuilder sourceBuilder;
                        if (arrayListCommonOne.get(getAdapterPosition()).getType() == 2) {
                            sourceBuilder = new MediaSourceBuilder(contextUri.parse(arrayListCommonOne.get(getAdapterPosition()).getListVideos().get(0).getUserWorkoutVideo()));

                        } else {
                            sourceBuilder = new MediaSourceBuilder(contextUri.parse(arrayListCommonOne.get(getAdapterPosition()).getListVideos().get(0).getUserWorkoutVideo()));

                        }
                        helper.prepare(sourceBuilder);
                        helper.setVolume(1f);
                    } catch (ParserException e) {
                        e.printStackTrace();
                    }

                }
            }
            if (playbackInfo != null && helper != null) {
                helper.setPlaybackInfo(playbackInfo);
            }
            if (helper != null) {
                helper.addEventListener(this);
            }
        }

    }

    @Override
    public void play() {
        if (helper != null) {
           helper.play();    

        }
    }

    @Override
    public void pause() {
        if (helper != null) helper.pause();
    }

    @Override
    public boolean isPlaying() {
        return helper != null && helper.isPlaying();
    }

    @Override
    public void release() {

        if (helper != null) {
            ivThumbNail.setVisibility(View.VISIBLE);
            try {
                helper.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
            helper = null;
        }

    }

    @Override
    public boolean wantsToPlay() {
        return ToroUtil.visibleAreaOffset(this, itemView.getParent()) >= 0.50;
    }

    @Override
    public int getPlayerOrder() {
        return getAdapterPosition();
    }

    @Override
    public void onContainerScrollStateChange(Container container, int newState) {

    }

    @Override
    public void onTimelineChanged(Timeline timeline, Object manifest) {

    }

    @Override
    public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

    }

    @Override
    public void onLoadingChanged(boolean isLoading) {

    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int i) {

    }

    @Override
    public void onPlayerError(ExoPlaybackException error) {

    }

    @Override
    public void onPositionDiscontinuity() {

    }

    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {

    }
}

This is my code. See if there is anything you are missing.

iharshb commented 6 years ago

Please provide ExoplayerHelper Class.

sharmadev2506 commented 6 years ago

/*

package im.ene.toro.exoplayer;

import android.content.Context; import android.os.Handler; import android.os.Looper; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.widget.Toast; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.DefaultRenderersFactory.ExtensionRendererMode; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.SimpleExoPlayerView; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.util.Util; import im.ene.toro.R; import im.ene.toro.ToroUtil; import im.ene.toro.media.DrmMedia; import im.ene.toro.media.PlaybackInfo; import java.net.CookieManager; import java.net.CookiePolicy; import java.util.ArrayList; import java.util.UUID;

import static com.google.android.exoplayer2.drm.UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME;

/**

@SuppressWarnings({ "WeakerAccess", "unused" }) // public final class ExoPlayerHelper {

private static final String TAG = "ToroLib:ExoPlayer";

// instance is unchanged, but inner fields are changeable. @NonNull final PlaybackInfo playbackInfo = new PlaybackInfo();

final Context context; // Application context, will obtain from playerView context. @NonNull final SimpleExoPlayerView playerView; @ExtensionRendererMode final int extensionMode; final Handler mainHandler;

SimpleExoPlayer player; ComponentListener componentListener; DefaultTrackSelector trackSelector;

MediaSourceBuilder mediaSourceBuilder; BandwidthMeter bandwidthMeter;

boolean shouldAutoPlay; boolean needRetrySource;

ArrayList eventListeners;

public ExoPlayerHelper(@NonNull SimpleExoPlayerView playerView, @ExtensionRendererMode int extensionMode, boolean playWhenReady) { this.playerView = playerView; this.context = playerView.getContext().getApplicationContext(); this.extensionMode = extensionMode; this.shouldAutoPlay = playWhenReady; this.mainHandler = new Handler(Looper.myLooper()); }

public ExoPlayerHelper(@NonNull SimpleExoPlayerView playerView, @ExtensionRendererMode int extensionMode) { this(playerView, extensionMode, false); }

public ExoPlayerHelper(@NonNull SimpleExoPlayerView playerView) { this(playerView, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); }

public void setPlaybackInfo(@Nullable PlaybackInfo playbackInfo) { if (playbackInfo != null) { this.playbackInfo.setResumeWindow(playbackInfo.getResumeWindow()); this.playbackInfo.setResumePosition(playbackInfo.getResumePosition()); }

if (player != null) {
  boolean haveResumePosition = this.playbackInfo.getResumeWindow() != C.INDEX_UNSET;
  if (haveResumePosition) {
    player.seekTo(this.playbackInfo.getResumeWindow(), this.playbackInfo.getResumePosition());
  }
}

}

public void prepare(@NonNull MediaSourceBuilder mediaSourceBuilder) throws ParserException { prepare(mediaSourceBuilder, new DefaultBandwidthMeter(mainHandler, null)); }

public void prepare(@NonNull MediaSourceBuilder mediaSourceBuilder, @Nullable BandwidthMeter bandwidthMeter) throws ParserException { //noinspection ConstantConditions if (mediaSourceBuilder == null) { throw new IllegalArgumentException("MediaSourceBuilder must not be null."); } this.mediaSourceBuilder = mediaSourceBuilder; DrmSessionManager drmSessionManager = null; if (mediaSourceBuilder instanceof DrmMediaProvider) { DrmMedia drmMedia = ((DrmMediaProvider) mediaSourceBuilder).getDrmMedia(); //noinspection ConstantConditions if (drmMedia == null) { throw new IllegalArgumentException("DrmMediaProvider must provide a non-null DrmMedia."); }

  UUID drmSchemeUuid = getDrmUuid(drmMedia.getType());
  if (drmSchemeUuid != null) {
    String drmLicenseUrl = drmMedia.getLicenseUrl();
    String[] keyRequestPropertiesArray = drmMedia.getKeyRequestPropertiesArray();
    try {
      drmSessionManager = buildDrmSessionManager(drmSchemeUuid, drmLicenseUrl,  //
          keyRequestPropertiesArray, mainHandler);
    } catch (UnsupportedDrmException e) {
      int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
          : (e.reason == REASON_UNSUPPORTED_SCHEME ?  //
              R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
      Toast.makeText(context, errorStringId, Toast.LENGTH_SHORT).show();
      return;
    }
  }
}

prepare(mediaSourceBuilder.build(bandwidthMeter), bandwidthMeter, drmSessionManager);

}

@SuppressWarnings("ConstantConditions") // void prepare(@NonNull MediaSource mediaSource, BandwidthMeter bandwidthMeter, DrmSessionManager drmSessionManager) throws ParserException { if (mediaSource == null) { throw new IllegalStateException("MediaSource must not be null."); }

this.bandwidthMeter = bandwidthMeter;
if (componentListener == null) {
  componentListener = new ComponentListener();
}

this.player = playerView.getPlayer();
boolean needNewPlayer = player == null;
if (needNewPlayer) {
  TrackSelection.Factory adaptiveTrackSelectionFactory =
      new AdaptiveTrackSelection.Factory(bandwidthMeter);
  trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);

  RenderersFactory renderersFactory =
      new DefaultRenderersFactory(context, drmSessionManager, extensionMode);

  player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
  player.addListener(componentListener);
  player.setPlayWhenReady(shouldAutoPlay);
  needRetrySource = true;
}

if (needNewPlayer || needRetrySource) {
  playerView.setPlayer(player);
  boolean haveResumePosition = playbackInfo.getResumeWindow() != C.INDEX_UNSET;
  if (haveResumePosition) {
    player.seekTo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition());
  }
  player.prepare(mediaSource, !haveResumePosition, false);
  needRetrySource = false;
}

}

public void release() { mainHandler.removeCallbacksAndMessages(null);

if (player != null) {
  shouldAutoPlay = player.getPlayWhenReady();
  updateResumePosition();
  player.removeListener(componentListener);
  player.release();
  playerView.setPlayer(null);
  player = null;
}

trackSelector = null;
mediaSourceBuilder = null;
bandwidthMeter = null;
componentListener = null;

}

@NonNull public PlaybackInfo getPlaybackInfo() { updateResumePosition(); // return a copy only. return new PlaybackInfo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition()); }

public void addEventListener(@NonNull ExoPlayer.EventListener eventListener) { if (this.eventListeners == null) { this.eventListeners = new ArrayList<>(); }

//noinspection ConstantConditions
if (eventListener != null) this.eventListeners.add(eventListener);

}

public void removeEventListener(ExoPlayer.EventListener eventListener) { if (this.eventListeners != null && eventListener != null) { this.eventListeners.remove(eventListener); } }

public void play() { if (player != null) player.setPlayWhenReady(true);

}

public void pause() { if (player != null) player.setPlayWhenReady(false); }

public boolean isPlaying() { return this.player != null && this.player.getPlayWhenReady(); }

public void setVolume(@FloatRange(from = 0.0, to = 1.0) float volume) { if (player != null) player.setVolume(volume); }

public float getVolume() { return player != null ? player.getVolume() : 1 / unity gain, default value /; }

void updateResumePosition() { if (player == null || player.getPlaybackState() == 1) return; playbackInfo.setResumeWindow(player.getCurrentWindowIndex()); playbackInfo.setResumePosition( player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition()) : C.TIME_UNSET); }

void clearResumePosition() { playbackInfo.reset(); }

private class ComponentListener implements ExoPlayer.EventListener {

ComponentListener() {
}

@Override public void onTimelineChanged(Timeline timeline, Object manifest) {
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onTimelineChanged(timeline, manifest);
    }
  }
}

@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
  MappedTrackInfo mappedTrackInfo =
      trackSelector != null ? trackSelector.getCurrentMappedTrackInfo() : null;
  if (mappedTrackInfo != null) {
    if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_VIDEO)
        == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
      Toast.makeText(context, R.string.error_unsupported_video, Toast.LENGTH_SHORT).show();
    }
    if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_AUDIO)
        == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
      Toast.makeText(context, R.string.error_unsupported_audio, Toast.LENGTH_SHORT).show();
    }
  }
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onTracksChanged(trackGroups, trackSelections);
    }
  }
}

@Override public void onLoadingChanged(boolean isLoading) {
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onLoadingChanged(isLoading);
    }
  }
}

@Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
  boolean screenOn = isPlaying() && (playbackState >= 2 || playbackState <= 3);
  playerView.setKeepScreenOn(screenOn);
  if (playbackState == ExoPlayer.STATE_ENDED) {
    if (player != null) {
      player.setPlayWhenReady(false);
      player.seekTo(0); // reset playback position for current window
    }
  }

  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPlayerStateChanged(playWhenReady, playbackState);
    }
  }
}

@Override public void onPlayerError(ExoPlaybackException e) {
  String errorString = null;
  if (e.type == ExoPlaybackException.TYPE_RENDERER) {
    Exception cause = e.getRendererException();
    if (cause instanceof MediaCodecRenderer.DecoderInitializationException) {
      // Special case for decoder initialization failures.
      MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
          (MediaCodecRenderer.DecoderInitializationException) cause;
      if (decoderInitializationException.decoderName == null) {
        if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
          errorString = context.getString(R.string.error_querying_decoders);
        } else if (decoderInitializationException.secureDecoderRequired) {
          errorString = context.getString(R.string.error_no_secure_decoder,
              decoderInitializationException.mimeType);
        } else {
          errorString = context.getString(R.string.error_no_decoder,
              decoderInitializationException.mimeType);
        }
      } else {
        errorString = context.getString(R.string.error_instantiating_decoder,
            decoderInitializationException.decoderName);
      }
    }
  }
  if (errorString != null) {
    Toast.makeText(context, errorString, Toast.LENGTH_SHORT).show();
  }

  needRetrySource = true;
  if (isBehindLiveWindow(e)) {
    clearResumePosition();
    try {
      prepare(ExoPlayerHelper.this.mediaSourceBuilder, ExoPlayerHelper.this.bandwidthMeter);
    } catch (ParserException e1) {
      e1.printStackTrace();
    }
  } else {
    updateResumePosition();
  }
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPlayerError(e);
    }
  }
}

@Override public void onPositionDiscontinuity() {
  if (needRetrySource) {
    // This will only occur if the user has performed a seek whilst in the error playerState. Update the
    // resume position so that if the user then retries, playback will resume from the position to
    // which they seek.
    updateResumePosition();
  }
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPositionDiscontinuity();
    }
  }
}

@Override public void onPlaybackParametersChanged(PlaybackParameters parameters) {
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPlaybackParametersChanged(parameters);
    }
  }
}

}

//// static methods

private static final CookieManager DEFAULT_COOKIE_MANAGER;

static { DEFAULT_COOKIE_MANAGER = new CookieManager(); DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); }

static boolean isBehindLiveWindow(ExoPlaybackException e) { if (e.type != ExoPlaybackException.TYPE_SOURCE) { return false; } Throwable cause = e.getSourceException(); while (cause != null) { if (cause instanceof BehindLiveWindowException) { return true; } cause = cause.getCause(); } return false; }

@Nullable DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, Handler mainHandler) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; } HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, new DefaultHttpDataSourceFactory(Util.getUserAgent(context, ToroUtil.LIB_NAME), null)); if (keyRequestPropertiesArray != null) { for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) { drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]); } } return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback, null, mainHandler, null); }

private static UUID getDrmUuid(String typeString) throws ParserException { switch (typeString.toLowerCase()) { case "widevine": return C.WIDEVINE_UUID; case "playready": return C.PLAYREADY_UUID; default: try { return UUID.fromString(typeString); } catch (RuntimeException e) { throw new ParserException("Unsupported drm type: " + typeString); } } }

// Adapter for original EventListener public static class EventListener implements ExoPlayer.EventListener {

private ExoPlayer.EventListener delegate;

public EventListener(ExoPlayer.EventListener delegate) {
  this.delegate = delegate;
}

public EventListener() {
  this.delegate = null;
}

public void setDelegate(ExoPlayer.EventListener delegate) {
  this.delegate = delegate;
}

@Override public void onTimelineChanged(Timeline timeline, Object manifest) {
  if (this.delegate != null) this.delegate.onTimelineChanged(timeline, manifest);
}

@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
  if (this.delegate != null) this.delegate.onTracksChanged(trackGroups, trackSelections);
}

@Override public void onLoadingChanged(boolean isLoading) {
  if (this.delegate != null) this.delegate.onLoadingChanged(isLoading);
}

@Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
  if (this.delegate != null) this.delegate.onPlayerStateChanged(playWhenReady, playbackState);
}

@Override public void onPlayerError(ExoPlaybackException error) {
  if (this.delegate != null) this.delegate.onPlayerError(error);
}

@Override public void onPositionDiscontinuity() {
  if (this.delegate != null) this.delegate.onPositionDiscontinuity();
}

@Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
  if (this.delegate != null) this.delegate.onPlaybackParametersChanged(playbackParameters);
}

} }

tranquoctrungcntt commented 5 years ago

i have the same issue , any solutions ?

sharmadev2506 commented 5 years ago

above comments didn't work for you?

Dhwanidesai173 commented 5 years ago

/*

  • Copyright (c) 2017 Nam Nguyen, nam@ene.im
  • Licensed under the Apache License, Version 2.0 (the "License");
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  http://www.apache.org/licenses/LICENSE-2.0
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an "AS IS" BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License. */

package im.ene.toro.exoplayer;

import android.content.Context; import android.os.Handler; import android.os.Looper; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.widget.Toast; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.DefaultRenderersFactory.ExtensionRendererMode; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.SimpleExoPlayerView; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.util.Util; import im.ene.toro.R; import im.ene.toro.ToroUtil; import im.ene.toro.media.DrmMedia; import im.ene.toro.media.PlaybackInfo; import java.net.CookieManager; import java.net.CookiePolicy; import java.util.ArrayList; import java.util.UUID;

import static com.google.android.exoplayer2.drm.UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME;

/**

  • @author eneim | 6/5/17.
  •   A helper class, dedicated to {@link SimpleExoPlayerView}.

*/

@SuppressWarnings({ "WeakerAccess", "unused" }) // public final class ExoPlayerHelper {

private static final String TAG = "ToroLib:ExoPlayer";

// instance is unchanged, but inner fields are changeable. @nonnull final PlaybackInfo playbackInfo = new PlaybackInfo();

final Context context; // Application context, will obtain from playerView context. @nonnull final SimpleExoPlayerView playerView; @ExtensionRendererMode final int extensionMode; final Handler mainHandler;

SimpleExoPlayer player; ComponentListener componentListener; DefaultTrackSelector trackSelector;

MediaSourceBuilder mediaSourceBuilder; BandwidthMeter bandwidthMeter;

boolean shouldAutoPlay; boolean needRetrySource;

ArrayList eventListeners;

public ExoPlayerHelper(@nonnull SimpleExoPlayerView playerView, @ExtensionRendererMode int extensionMode, boolean playWhenReady) { this.playerView = playerView; this.context = playerView.getContext().getApplicationContext(); this.extensionMode = extensionMode; this.shouldAutoPlay = playWhenReady; this.mainHandler = new Handler(Looper.myLooper()); }

public ExoPlayerHelper(@nonnull SimpleExoPlayerView playerView, @ExtensionRendererMode int extensionMode) { this(playerView, extensionMode, false); }

public ExoPlayerHelper(@nonnull SimpleExoPlayerView playerView) { this(playerView, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); }

public void setPlaybackInfo(@nullable PlaybackInfo playbackInfo) { if (playbackInfo != null) { this.playbackInfo.setResumeWindow(playbackInfo.getResumeWindow()); this.playbackInfo.setResumePosition(playbackInfo.getResumePosition()); }

if (player != null) {
  boolean haveResumePosition = this.playbackInfo.getResumeWindow() != C.INDEX_UNSET;
  if (haveResumePosition) {
    player.seekTo(this.playbackInfo.getResumeWindow(), this.playbackInfo.getResumePosition());
  }
}

}

public void prepare(@nonnull MediaSourceBuilder mediaSourceBuilder) throws ParserException { prepare(mediaSourceBuilder, new DefaultBandwidthMeter(mainHandler, null)); }

public void prepare(@nonnull MediaSourceBuilder mediaSourceBuilder, @nullable BandwidthMeter bandwidthMeter) throws ParserException { //noinspection ConstantConditions if (mediaSourceBuilder == null) { throw new IllegalArgumentException("MediaSourceBuilder must not be null."); } this.mediaSourceBuilder = mediaSourceBuilder; DrmSessionManager drmSessionManager = null; if (mediaSourceBuilder instanceof DrmMediaProvider) { DrmMedia drmMedia = ((DrmMediaProvider) mediaSourceBuilder).getDrmMedia(); //noinspection ConstantConditions if (drmMedia == null) { throw new IllegalArgumentException("DrmMediaProvider must provide a non-null DrmMedia."); }

  UUID drmSchemeUuid = getDrmUuid(drmMedia.getType());
  if (drmSchemeUuid != null) {
    String drmLicenseUrl = drmMedia.getLicenseUrl();
    String[] keyRequestPropertiesArray = drmMedia.getKeyRequestPropertiesArray();
    try {
      drmSessionManager = buildDrmSessionManager(drmSchemeUuid, drmLicenseUrl,  //
          keyRequestPropertiesArray, mainHandler);
    } catch (UnsupportedDrmException e) {
      int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
          : (e.reason == REASON_UNSUPPORTED_SCHEME ?  //
              R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
      Toast.makeText(context, errorStringId, Toast.LENGTH_SHORT).show();
      return;
    }
  }
}

prepare(mediaSourceBuilder.build(bandwidthMeter), bandwidthMeter, drmSessionManager);

}

@SuppressWarnings("ConstantConditions") // void prepare(@nonnull MediaSource mediaSource, BandwidthMeter bandwidthMeter, DrmSessionManager drmSessionManager) throws ParserException { if (mediaSource == null) { throw new IllegalStateException("MediaSource must not be null."); }

this.bandwidthMeter = bandwidthMeter;
if (componentListener == null) {
  componentListener = new ComponentListener();
}

this.player = playerView.getPlayer();
boolean needNewPlayer = player == null;
if (needNewPlayer) {
  TrackSelection.Factory adaptiveTrackSelectionFactory =
      new AdaptiveTrackSelection.Factory(bandwidthMeter);
  trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);

  RenderersFactory renderersFactory =
      new DefaultRenderersFactory(context, drmSessionManager, extensionMode);

  player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
  player.addListener(componentListener);
  player.setPlayWhenReady(shouldAutoPlay);
  needRetrySource = true;
}

if (needNewPlayer || needRetrySource) {
  playerView.setPlayer(player);
  boolean haveResumePosition = playbackInfo.getResumeWindow() != C.INDEX_UNSET;
  if (haveResumePosition) {
    player.seekTo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition());
  }
  player.prepare(mediaSource, !haveResumePosition, false);
  needRetrySource = false;
}

}

public void release() { mainHandler.removeCallbacksAndMessages(null);

if (player != null) {
  shouldAutoPlay = player.getPlayWhenReady();
  updateResumePosition();
  player.removeListener(componentListener);
  player.release();
  playerView.setPlayer(null);
  player = null;
}

trackSelector = null;
mediaSourceBuilder = null;
bandwidthMeter = null;
componentListener = null;

}

@nonnull public PlaybackInfo getPlaybackInfo() { updateResumePosition(); // return a copy only. return new PlaybackInfo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition()); }

public void addEventListener(@nonnull ExoPlayer.EventListener eventListener) { if (this.eventListeners == null) { this.eventListeners = new ArrayList<>(); }

//noinspection ConstantConditions
if (eventListener != null) this.eventListeners.add(eventListener);

}

public void removeEventListener(ExoPlayer.EventListener eventListener) { if (this.eventListeners != null && eventListener != null) { this.eventListeners.remove(eventListener); } }

public void play() { if (player != null) player.setPlayWhenReady(true);

}

public void pause() { if (player != null) player.setPlayWhenReady(false); }

public boolean isPlaying() { return this.player != null && this.player.getPlayWhenReady(); }

public void setVolume(@FloatRange(from = 0.0, to = 1.0) float volume) { if (player != null) player.setVolume(volume); }

public float getVolume() { return player != null ? player.getVolume() : 1 / unity gain, default value /; }

void updateResumePosition() { if (player == null || player.getPlaybackState() == 1) return; playbackInfo.setResumeWindow(player.getCurrentWindowIndex()); playbackInfo.setResumePosition( player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition()) : C.TIME_UNSET); }

void clearResumePosition() { playbackInfo.reset(); }

private class ComponentListener implements ExoPlayer.EventListener {

ComponentListener() {
}

@Override public void onTimelineChanged(Timeline timeline, Object manifest) {
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onTimelineChanged(timeline, manifest);
    }
  }
}

@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
  MappedTrackInfo mappedTrackInfo =
      trackSelector != null ? trackSelector.getCurrentMappedTrackInfo() : null;
  if (mappedTrackInfo != null) {
    if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_VIDEO)
        == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
      Toast.makeText(context, R.string.error_unsupported_video, Toast.LENGTH_SHORT).show();
    }
    if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_AUDIO)
        == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
      Toast.makeText(context, R.string.error_unsupported_audio, Toast.LENGTH_SHORT).show();
    }
  }
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onTracksChanged(trackGroups, trackSelections);
    }
  }
}

@Override public void onLoadingChanged(boolean isLoading) {
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onLoadingChanged(isLoading);
    }
  }
}

@Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
  boolean screenOn = isPlaying() && (playbackState >= 2 || playbackState <= 3);
  playerView.setKeepScreenOn(screenOn);
  if (playbackState == ExoPlayer.STATE_ENDED) {
    if (player != null) {
      player.setPlayWhenReady(false);
      player.seekTo(0); // reset playback position for current window
    }
  }

  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPlayerStateChanged(playWhenReady, playbackState);
    }
  }
}

@Override public void onPlayerError(ExoPlaybackException e) {
  String errorString = null;
  if (e.type == ExoPlaybackException.TYPE_RENDERER) {
    Exception cause = e.getRendererException();
    if (cause instanceof MediaCodecRenderer.DecoderInitializationException) {
      // Special case for decoder initialization failures.
      MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
          (MediaCodecRenderer.DecoderInitializationException) cause;
      if (decoderInitializationException.decoderName == null) {
        if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
          errorString = context.getString(R.string.error_querying_decoders);
        } else if (decoderInitializationException.secureDecoderRequired) {
          errorString = context.getString(R.string.error_no_secure_decoder,
              decoderInitializationException.mimeType);
        } else {
          errorString = context.getString(R.string.error_no_decoder,
              decoderInitializationException.mimeType);
        }
      } else {
        errorString = context.getString(R.string.error_instantiating_decoder,
            decoderInitializationException.decoderName);
      }
    }
  }
  if (errorString != null) {
    Toast.makeText(context, errorString, Toast.LENGTH_SHORT).show();
  }

  needRetrySource = true;
  if (isBehindLiveWindow(e)) {
    clearResumePosition();
    try {
      prepare(ExoPlayerHelper.this.mediaSourceBuilder, ExoPlayerHelper.this.bandwidthMeter);
    } catch (ParserException e1) {
      e1.printStackTrace();
    }
  } else {
    updateResumePosition();
  }
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPlayerError(e);
    }
  }
}

@Override public void onPositionDiscontinuity() {
  if (needRetrySource) {
    // This will only occur if the user has performed a seek whilst in the error playerState. Update the
    // resume position so that if the user then retries, playback will resume from the position to
    // which they seek.
    updateResumePosition();
  }
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPositionDiscontinuity();
    }
  }
}

@Override public void onPlaybackParametersChanged(PlaybackParameters parameters) {
  int count;
  if (eventListeners != null && (count = eventListeners.size()) > 0) {
    for (int i = count - 1; i >= 0; i--) {
      eventListeners.get(i).onPlaybackParametersChanged(parameters);
    }
  }
}

}

//// static methods

private static final CookieManager DEFAULT_COOKIE_MANAGER;

static { DEFAULT_COOKIE_MANAGER = new CookieManager(); DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); }

static boolean isBehindLiveWindow(ExoPlaybackException e) { if (e.type != ExoPlaybackException.TYPE_SOURCE) { return false; } Throwable cause = e.getSourceException(); while (cause != null) { if (cause instanceof BehindLiveWindowException) { return true; } cause = cause.getCause(); } return false; }

@nullable DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, Handler mainHandler) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; } HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, new DefaultHttpDataSourceFactory(Util.getUserAgent(context, ToroUtil.LIB_NAME), null)); if (keyRequestPropertiesArray != null) { for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) { drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]); } } return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback, null, mainHandler, null); }

private static UUID getDrmUuid(String typeString) throws ParserException { switch (typeString.toLowerCase()) { case "widevine": return C.WIDEVINE_UUID; case "playready": return C.PLAYREADY_UUID; default: try { return UUID.fromString(typeString); } catch (RuntimeException e) { throw new ParserException("Unsupported drm type: " + typeString); } } }

// Adapter for original EventListener public static class EventListener implements ExoPlayer.EventListener {

private ExoPlayer.EventListener delegate;

public EventListener(ExoPlayer.EventListener delegate) {
  this.delegate = delegate;
}

public EventListener() {
  this.delegate = null;
}

public void setDelegate(ExoPlayer.EventListener delegate) {
  this.delegate = delegate;
}

@Override public void onTimelineChanged(Timeline timeline, Object manifest) {
  if (this.delegate != null) this.delegate.onTimelineChanged(timeline, manifest);
}

@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
  if (this.delegate != null) this.delegate.onTracksChanged(trackGroups, trackSelections);
}

@Override public void onLoadingChanged(boolean isLoading) {
  if (this.delegate != null) this.delegate.onLoadingChanged(isLoading);
}

@Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
  if (this.delegate != null) this.delegate.onPlayerStateChanged(playWhenReady, playbackState);
}

@Override public void onPlayerError(ExoPlaybackException error) {
  if (this.delegate != null) this.delegate.onPlayerError(error);
}

@Override public void onPositionDiscontinuity() {
  if (this.delegate != null) this.delegate.onPositionDiscontinuity();
}

@Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
  if (this.delegate != null) this.delegate.onPlaybackParametersChanged(playbackParameters);
}

} }

where i can use "player.seekTo()" because i can not find seekTo() in my toroplayer