如何在ExoPlayer Android中跳过视频的一小部分

时间:2019-02-14 11:49:08

标签: android exoplayer

我想实现诸如“跳过简介”之类的功能,并在我的视频播放器中跳过功劳。我正在使用Exoplayer,假设我有一个长度为00:15:21(hh:mm:ss)的视频,我知道该视频的实际内容始于00:00:18且内容始于00:14 :12。我想像在NetFlix中一样显示“跳过简介”和“下一集”按钮。我该如何实现?

Exoplayer页面Github上的问题: https://github.com/google/ExoPlayer/issues/5515

3 个答案:

答案 0 :(得分:1)

Developer Guide中,您可以使用ClippingMediaSource API来完成任务。

  

ClippingMediaSource可用于剪辑MediaSource,以便仅播放其中的一部分

要从00:00:18开始播放视频,直到结束(跳过简介)。

MediaSource videoSource =
    new ExtractorMediaSource.Factory(...).createMediaSource(videoUri);
// Clip to start at from 00:00:18 to the end.
ClippingMediaSource clippingSource =
    new ClippingMediaSource(
        videoSource,
        /* startPositionUs= */ 18_000_000,
        /* endPositionUs= */ C.TIME_END_OF_SOURCE);

要从00:14:12到结尾播放视频(下一集

MediaSource videoSource =
    new ExtractorMediaSource.Factory(...).createMediaSource(videoUri);
// Clip to start at from 00:14:12 to the end.
ClippingMediaSource clippingSource =
    new ClippingMediaSource(
        videoSource,
        /* startPositionUs= */ 852_000_000,
        /* endPositionUs= */ C.TIME_END_OF_SOURCE);

或者从00:00:18到00:14:12播放视频

MediaSource videoSource =
    new ExtractorMediaSource.Factory(...).createMediaSource(videoUri);
// Clip to start at from 00:00:18 to 00:14:12.
ClippingMediaSource clippingSource =
    new ClippingMediaSource(
        videoSource,
        /* startPositionUs= */ 18_000_000,
        /* endPositionUs= */ 852_000_000);

您可以找到有关API here的更多信息。

答案 1 :(得分:0)

好的,在ExoPlayer开发人员自己的帮助下,我找到了解决方案。详细信息在我在问题中添加的参考链接中。这是摘要:

只要我们知道跳过的持续时间,我们就可以创建消息并将其发送到Exoplayer,并在回调中,我们可以实现我们的业务逻辑,例如使按钮可见,按钮的onClickListners等。

private class PlayerEventListener extends Player.DefaultEventListener {

        @Override
        public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) {

                prepareSkipToNextEpisode();
                if(reason == DISCONTINUITY_REASON_PERIOD_TRANSITION){          //when a video naturally ends it course and starts playing next video.
                    prepareSkipIntro();
                }
            }

        }

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

        }

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

            String stateString;

            switch (playbackState) {
                case Player.STATE_IDLE: // The player does not have any media to play.
                    stateString = "Player.STATE_IDLE";
                    mProgressBar.setVisibility(View.VISIBLE);
                    playerView.setKeepScreenOn(false);
                    //mPlayerView.hideController();
                    mediaControlsLayout.setVisibility(View.GONE);
                    break;
                case Player.STATE_BUFFERING: // The player needs to load media before playing.
                    stateString = "Player.STATE_BUFFERING";
                    mProgressBar.setVisibility(View.VISIBLE);
                    mediaControlsLayout.setVisibility(View.GONE);
                    playerView.setKeepScreenOn(true);
                    break;
                case Player.STATE_READY: // The player is able to immediately play from its current position.
                    stateString = "Player.STATE_READY";
                    mProgressBar.setVisibility(View.GONE);
                    mediaControlsLayout.setVisibility(View.VISIBLE);
                    playerView.setKeepScreenOn(true);
                    prepareSkipToNextEpisode();
                    if((player.getContentPosition() < 5000)) {
                            prepareSkipIntro();
                        }
                        //prepareSkipToNextEpisode();


                    break;
                case Player.STATE_ENDED: // The player has finished playing the media.
                    stateString = "Player.STATE_ENDED";
                    playerView.setKeepScreenOn(false);
                    break;
                default:
                    stateString = "UNKNOWN_STATE";
                    break;
            }

        }

    }


private void prepareSkipIntro(){

        if(episodeStartTimes[player.getCurrentWindowIndex()] > 0) {
            inttoBeginsHandler = new Handler();
            long introDurationMs = 3 * 1000;
            player.createMessage((messageType, payload) -> hideSkipIntro())
                    .setPosition(introDurationMs).setHandler(inttoBeginsHandler).send();
        }

    }


    private void hideSkipIntro(){

        if(player.getCurrentPosition()>2500) {
            skipIntroBtn.setVisibility(View.VISIBLE);
            skipIntroBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    if (player != null) {

                        player.seekTo(episodeStartTimes[player.getCurrentWindowIndex()]);
                        prepareSkipToNextEpisode();
                        skipIntroBtn.setVisibility(View.GONE);
                    }

                }
            });

            final Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    skipIntroBtn.setVisibility(View.GONE);

                }
            }, 5000);

        }
    }


    private void prepareSkipToNextEpisode(){


        if(episodeEndTimes[player.getCurrentWindowIndex()] > 0) {
            if (player.getCurrentPosition() < 1000) {

                remainingTime = episodeDummyEndtimes[player.getCurrentWindowIndex()];

            } else {

                remainingTime = episodeDummyEndtimes[player.getCurrentWindowIndex()] - player.getCurrentPosition();


            }

            creditsBeginssHandler = null;
            creditsBeginssHandler = new Handler();
            player.createMessage((messageType, payload) -> endEpisode())
                    .setPosition(remainingTime).setHandler(creditsBeginssHandler).send();
        }

    }

    private void endEpisode(){


        if(player.getCurrentPosition()>= episodeDummyEndtimes[player.getCurrentWindowIndex()]) {



            if (player.getCurrentWindowIndex() < (episodeEndTimes.length-1)) {  // no need of skip credits for the final episode
                skipCreditsBtn.setVisibility(View.VISIBLE);
                skipCreditsBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {


                        player.seekTo(player.getNextWindowIndex(), 0);
                        skipCreditsBtn.setVisibility(View.GONE);
                    }
                });

                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        skipCreditsBtn.setVisibility(View.GONE);

                    }
                }, 5000);
            }else{
                skipCreditsBtn.setVisibility(View.GONE);
            }
        }
    }

在这里,我通常以00:15:00作为视频内容的结尾,请注意,如果用户使用搜索栏手动移动了该持续时间,则不会触发该消息。

答案 2 :(得分:0)

这里简短而甜美的方法就在这里。 下面的代码会在最多13秒的开始时间剪辑视频,以跳过介绍。 视频源为HlsMediaSource,但您可以对VideoSource进行类似的操作。

DefaultHttpDataSourceFactory dataSourceFactory = getSettedHeadersDataFactory();
            HlsMediaSource hlsMediaSource =
                    new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(qualities.get(currentQuality).getQualityUrl()));
            ClippingMediaSource clippingSource =
                    new ClippingMediaSource(
                            hlsMediaSource,
                            /* startPositionUs= */ 13_000_000,
                            /* endPositionUs= */ C.TIME_END_OF_SOURCE);
            player.prepare(clippingSource);