Android VideoView 缓冲

Posted

技术标签:

【中文标题】Android VideoView 缓冲【英文标题】:Android VideoView buffering 【发布时间】:2017-08-26 16:16:55 【问题描述】:

我正在开发android TV 应用程序,该应用程序使用VideoView 从网址播放视频。每次视频暂停然后恢复时 - 首先需要一些时间来预下载(在几秒钟内会出现进度条)。目标是在播放期间缓冲视频,以便当我暂停它然后恢复时 - 它立即从预缓冲状态恢复,没有延迟。我也尝试过videoView.resume() 方法,但也无济于事。有没有办法做到这一点,还是我做错了什么?

这是播放/恢复视频的代码:

public void onFragmentPlayPause(final Video video,
                                    final VideoQuality quality,
                                    final int position,
                                    final Boolean play) 
        stopSeeking();

        videoView.setVideoPath(video.videoUrl(quality));

        if (position == 0 || playbackState == LeanbackPlaybackState.IDLE) 
            setupCallbacks();
            playbackState = LeanbackPlaybackState.IDLE;
        

        if (play && playbackState != LeanbackPlaybackState.PLAYING) 
            progressBar.setVisibility(View.VISIBLE);
            playbackState = LeanbackPlaybackState.PLAYING;
            if (position > 0) 
                videoView.seekTo(position);
                videoView.start();
            
            playbackFragment.startProgressObservation(progressFlowable());
         else 
            playbackState = LeanbackPlaybackState.PAUSED;
            videoView.pause();
            playbackFragment.stopProgressObservation();
        

        updatePlaybackState(position);
        updateMetadata(video);
    

【问题讨论】:

试过ExoPlayer?更多信息:google.github.io/ExoPlayer/guide.html#pros-and-cons @pskink 不,我没有。如果我使用它,有很多代码需要更改,所以我想通过VideoView 来实现这一点。目前,这是唯一的问题,其他一切正常。 如果无法使用VideoView 平滑流,那将是一个坏消息。 请参阅“优缺点”部分:“支持基于 HTTP 的动态自适应流式传输 (DASH) 和 SmoothStreaming,MediaPlayer 都不支持这两种格式。还支持许多其他格式。请参阅支持的格式详情页。”,MediaPlayer 真的是穷人的播放器...只要看看一个示例应用程序,你就会发现它是多么容易完成 在这里找到了一个不错的库,可以轻松迁移到ExoPlayer:github.com/brianwernick/ExoMedia。 【参考方案1】:

不幸的是,我没有找到使用VideoView 实现此目的的方法,仅使用ExoPlayer,感谢@pskink 的正确方向。 API 正在返回 mp4 视频,所以我使用了 ExtractorMediaSource。支持多种格式的完整演示示例可以在ExoPlayer GitHub page找到。

这是我使用ExoPlayer 最终得到的代码:

private void playPause(final Video video, final int position, final Boolean play) 
        if (position == 0 || playbackState == LeanbackPlaybackState.IDLE) 
            setupCallbacks();
            playbackState = LeanbackPlaybackState.IDLE;
        

        if (play && playbackState != LeanbackPlaybackState.PLAYING) 
            progressBar.setVisibility(View.VISIBLE);
            playbackState = LeanbackPlaybackState.PLAYING;
            player.start();
            playbackFragment.startProgressObservation(progressFlowable());
         else 
            playbackState = LeanbackPlaybackState.PAUSED;
            player.pause();
            playbackFragment.stopProgressObservation();
        

        updatePlaybackState(position);
        updateMetadata(video);
    

Player 实现:

public class Player implements MediaController.MediaPlayerControl, ExoPlayer.EventListener 

    private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();

    private final SimpleExoPlayer exoPlayer;
    private final Context context;

    private OnErrorListener onErrorListener;
    private OnPreparedListener onPreparedListener;
    private OnCompletionListener onCompletionListener;

    private String url;

    public interface OnErrorListener 
        void onError(final Exception e);
    

    public interface OnPreparedListener 
        void onPrepared();
    

    public interface OnCompletionListener 
        void onComplete();
    

    public Player(final Context context) 
        this.context = context;

        final @SimpleExoPlayer.ExtensionRendererMode int extensionRendererMode =
                SimpleExoPlayer.EXTENSION_RENDERER_MODE_OFF;
        final TrackSelection.Factory videoTrackSelectionFactory =
                new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
        this.exoPlayer = ExoPlayerFactory.newSimpleInstance(
                context,
                new DefaultTrackSelector(videoTrackSelectionFactory),
                new DefaultLoadControl(),
                null,
                extensionRendererMode
        );
        this.exoPlayer.addListener(this);
    

    @Override
    public boolean canPause() 
        return true;
    

    @Override
    public boolean canSeekBackward() 
        return true;
    

    @Override
    public boolean canSeekForward() 
        return true;
    

    @Override
    public int getAudiosessionId() 
        throw new UnsupportedOperationException();
    

    @Override
    public int getBufferPercentage() 
        return exoPlayer.getBufferedPercentage();
    

    @Override
    public int getCurrentPosition() 
        return exoPlayer.getDuration() == com.google.android.exoplayer2.C.TIME_UNSET ? 0
                : (int) exoPlayer.getCurrentPosition();
    

    @Override
    public int getDuration() 
        return exoPlayer.getDuration() == com.google.android.exoplayer2.C.TIME_UNSET ? 0
                : (int) exoPlayer.getDuration();
    

    @Override
    public boolean isPlaying() 
        return exoPlayer.getPlayWhenReady();
    

    @Override
    public void start() 
        exoPlayer.setPlayWhenReady(true);
    

    @Override
    public void pause() 
        exoPlayer.setPlayWhenReady(false);
    

    public void stop() 
        exoPlayer.seekTo(0);
        pause();
    

    public void setOnErrorListener(final OnErrorListener onErrorListener) 
        this.onErrorListener = onErrorListener;
    

    public void setOnPreparedListener(final OnPreparedListener onPreparedListener) 
        this.onPreparedListener = onPreparedListener;
    

    public void setOnCompletionListener(final OnCompletionListener onCompletionListener) 
        this.onCompletionListener = onCompletionListener;
    

    public void setVolume(final float volume) 
        exoPlayer.setVolume(volume);
    

    public void release() 
        exoPlayer.release();
    

    public void updateUrl(final String url) 
        this.url = url;
        exoPlayer.prepare(buildMediaSource(Uri.parse(url)));
    

    public SimpleExoPlayer exoPlayer() 
        return exoPlayer;
    

    @Override
    public void seekTo(final int timeMillis) 
        exoPlayer.seekTo(timeMillis);
    

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

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

    @Override
    public void onLoadingChanged(final boolean isLoading) 
    

    @Override
    public void onPlayerStateChanged(final boolean playWhenReady, final int playbackState) 
        if (playbackState == ExoPlayer.STATE_READY) 
            onPreparedListener.onPrepared();
        
        if (playbackState == ExoPlayer.STATE_ENDED) 
            onCompletionListener.onComplete();
        
    

    @Override
    public void onPlayerError(final ExoPlaybackException error) 
        onErrorListener.onError(error);
    

    @Override
    public void onPositionDiscontinuity() 
    

    public String url() 
        return url;
    

    private MediaSource buildMediaSource(final Uri uri) 
        return new ExtractorMediaSource(uri, buildDataSourceFactory(true), new DefaultExtractorsFactory(),
                null, null);
    

    private DataSource.Factory buildDataSourceFactory(final DefaultBandwidthMeter bandwidthMeter) 
        return new DefaultDataSourceFactory(context, bandwidthMeter,
                buildHttpDataSourceFactory(bandwidthMeter));
    

    private HttpDataSource.Factory buildHttpDataSourceFactory(final DefaultBandwidthMeter bandwidthMeter) 
        return new DefaultHttpDataSourceFactory(Util.getUserAgent(context, Application.TAG), bandwidthMeter);
    

    private DataSource.Factory buildDataSourceFactory(final boolean useBandwidthMeter) 
        return buildDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null);
    

【讨论】:

以上是关于Android VideoView 缓冲的主要内容,如果未能解决你的问题,请参考以下文章

Android - 使用 VideoView 缓冲滞后

Android videoview can't play this video on android 4.3 错误

如何在 Android Studio 中将一个 VideoView 放在另一个 VideoView 下方?

Android VideoView未解决,动态读取权限BottomNavigationView的用法

Android VideoView未解决,动态读取权限BottomNavigationView的用法

Android Videoview - 去除两侧的黑色空间