Skip to content

Commit

Permalink
ExoPlayer实现缓冲进度监听
Browse files Browse the repository at this point in the history
  • Loading branch information
Doikki committed May 3, 2018
1 parent 844e6e8 commit 0895182
Show file tree
Hide file tree
Showing 16 changed files with 275 additions and 88 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ A video player based on [IjkPlayer](https://github.com/Bilibili/ijkplayer).
* **支持封面。**
* **支持锁定/解锁全屏。**
* **支持调整显示比例:默认、原始大小、16:9、4:3、铺满屏幕、居中裁剪。**
* **支持IJKPlayer和MediaPlayer切换**
* **支持IjkPlayer、MediaPlayer和ExoPlayer切换**
* **支持Https,rtsp,concat协议。**
* **支持连续播放一个列表的视频。**
* **支持广告播放。**
Expand Down Expand Up @@ -75,7 +75,6 @@ PlayerConfig playerConfig = new PlayerConfig.Builder()
.enableCache() //启用边播边缓存功能
.autoRotate() //启用重力感应自动进入/退出全屏功能
.enableMediaCodec()//启动硬解码,启用后可能导致视频黑屏,音画不同步
.usingAndroidMediaPlayer()//启动AndroidMediaPlayer,不调用此方法默认使用IjkPlayer
.usingSurfaceView() //启用SurfaceView显示视频,不调用默认使用TextureView
.savingProgress() //保存播放进度
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
.autoRotate()//自动旋转屏幕
// .enableCache()//启用边播边存
// .enableMediaCodec()//启动硬解码
// .usingAndroidMediaPlayer()//使用AndroidMediaPlayer
// .usingSurfaceView()//使用SurfaceView
.setCustomMediaPlayer(new IjkPlayer() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
DefinitionController controller = new DefinitionController(this);
ijkVideoView.setPlayerConfig(new PlayerConfig.Builder()
.autoRotate()//自动旋转屏幕
// .usingAndroidMediaPlayer()
.build());

LinkedHashMap<String, String> videos = new LinkedHashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
.autoRotate()//自动旋转屏幕
// .enableCache()//启用边播边存
// .enableMediaCodec()//启动硬解码
// .usingAndroidMediaPlayer()//使用AndroidMediaPlayer
// .usingSurfaceView()//使用SurfaceView
.build());
ijkVideoView.setUrl(intent.getStringExtra("url"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
.autoRotate()//自动旋转屏幕
// .enableCache()//启用边播边存
// .enableMediaCodec()//启动硬解码
// .usingAndroidMediaPlayer()//使用AndroidMediaPlayer
// .usingSurfaceView()//使用SurfaceView
.build());
ijkVideoView.setUrl("http://mov.bn.netease.com/open-movie/nos/flv/2017/01/03/SC8U8K7BC_hd.flv");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class SwitchPlayerActivity extends AppCompatActivity implements View.OnCl

private IjkVideoView ijkVideoView;
private StandardVideoController mController;
public static final String URL = "http://gslb.miaopai.com/stream/FQXM04zrW1dcXGiPdJ6Q3KAq2Fpv4TLV.mp4";

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
Expand All @@ -45,7 +46,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
.autoRotate()//自动旋转屏幕
// .usingSurfaceView()//使用SurfaceView
.build());
ijkVideoView.setUrl("http://flv2.bn.netease.com/videolib3/1611/28/GbgsL3639/HD/movie_index.m3u8");
ijkVideoView.setUrl(URL);
ijkVideoView.setVideoController(mController);
ijkVideoView.start();
}
Expand All @@ -68,7 +69,7 @@ public void onClick(View v) {
}

ijkVideoView.release();
ijkVideoView.setUrl("http://flv2.bn.netease.com/videolib3/1611/28/GbgsL3639/HD/movie_index.m3u8");
ijkVideoView.setUrl(URL);
ijkVideoView.setVideoController(mController);

ijkVideoView.setPlayerConfig(new PlayerConfig.Builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public class VideoHolder extends RecyclerView.ViewHolder {
// .enableCache()
// .autoRotate()//不能开启自动旋转
.addToPlayerManager()//required
// .usingAndroidMediaPlayer()
// .savingProgress()
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ private class ViewHolder {
mPlayerConfig = new PlayerConfig.Builder()
// .enableCache()
// .autoRotate()
// .usingAndroidMediaPlayer()
.addToPlayerManager()
.savingProgress()
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ public class VideoHolder extends RecyclerView.ViewHolder {
.enableCache()
.autoRotate()
.addToPlayerManager()//required
// .usingAndroidMediaPlayer()
// .savingProgress()
.build();
}
Expand Down
36 changes: 34 additions & 2 deletions app/src/main/java/com/dueeeke/dkplayer/player/ExoMediaPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.view.Surface;
import android.view.SurfaceHolder;

import com.dueeeke.videoplayer.player.AbstractPlayer;
import com.dueeeke.videoplayer.util.L;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
Expand Down Expand Up @@ -46,6 +48,7 @@ public class ExoMediaPlayer extends AbstractPlayer implements Player.EventListen
VideoRendererEventListener {

private static final String TAG = "ExoMediaPlayer";
private static final int BUFFER_REPEAT_DELAY = 1_000;

private Context mAppContext;
private SimpleExoPlayer2 mInternalPlayer;
Expand All @@ -61,6 +64,8 @@ public class ExoMediaPlayer extends AbstractPlayer implements Player.EventListen
private boolean lastReportedPlayWhenReady;
private boolean mIsPrepareing = true;
private boolean mIsBuffering = false;
@NonNull
private Repeater bufferRepeater = new Repeater();

// private int audioSessionId = C.AUDIO_SESSION_ID_UNSET;
public static final int TYPE_RTMP = 4;
Expand All @@ -70,6 +75,8 @@ public ExoMediaPlayer(Context context) {
Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper();
mainHandler = new Handler(eventLooper);
lastReportedPlaybackState = Player.STATE_IDLE;
bufferRepeater.setRepeaterDelay(BUFFER_REPEAT_DELAY);
bufferRepeater.setRepeatListener(new BufferRepeatListener());
}

@Override
Expand Down Expand Up @@ -210,12 +217,15 @@ public void prepareAsync() {
public void reset() {
if (mInternalPlayer != null) {
mInternalPlayer.release();
// mInternalPlayer.removeListener(this);
mInternalPlayer.removeListener(this);
mInternalPlayer = null;
}

mSurface = null;
mDataSource = null;
mIsPrepareing = true;
mIsBuffering = false;
setBufferRepeaterStarted(false);
}

@Override
Expand Down Expand Up @@ -321,6 +331,27 @@ public long getTcpSpeed() {
return 0;
}

private void setBufferRepeaterStarted(boolean start) {
if (start && mPlayerEventListener != null) {
bufferRepeater.start();
} else {
bufferRepeater.stop();
}
}

private class BufferRepeatListener implements Repeater.RepeatListener {
@Override
public void onRepeat() {
if (mPlayerEventListener != null) {
mPlayerEventListener.onBufferingUpdate(getBufferedPercentage());
}
}
}

private int getBufferedPercentage() {
return mInternalPlayer == null ? 0 : mInternalPlayer.getBufferedPercentage();
}

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

Expand All @@ -341,7 +372,7 @@ public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

//重新播放状态顺序为:STATE_IDLE -》STATE_BUFFERING -》STATE_READY
//缓冲时顺序为:STATE_BUFFERING -》STATE_READY
//Log.e(TAG, "onPlayerStateChanged: playWhenReady = " + playWhenReady + ", playbackState = " + playbackState);
L.e("onPlayerStateChanged: playWhenReady = " + playWhenReady + ", playbackState = " + playbackState);
if (lastReportedPlayWhenReady != playWhenReady || lastReportedPlaybackState != playbackState) {
if (mIsBuffering) {
switch (playbackState) {
Expand All @@ -362,6 +393,7 @@ public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
mPlayerEventListener.onPrepared();
mPlayerEventListener.onInfo(IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START, 0);
}
setBufferRepeaterStarted(true);
mIsPrepareing = false;
break;
}
Expand Down
148 changes: 148 additions & 0 deletions app/src/main/java/com/dueeeke/dkplayer/player/Repeater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (C) 2016 Brian Wernick
*
* 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 com.dueeeke.dkplayer.player;

import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.Nullable;

/**
* A method repeater to easily perform update functions on a timed basis.
* <b>NOTE:</b> the duration between repeats may not be exact.
*/
public class Repeater {
protected static final String HANDLER_THREAD_NAME = "ExoMedia_Repeater_HandlerThread";
protected static final int DEFAULT_REPEAT_DELAY = 33; // ~30 fps

public interface RepeatListener {
void onRepeat();
}

protected volatile boolean repeaterRunning = false;
protected int repeatDelay = DEFAULT_REPEAT_DELAY;

protected Handler delayedHandler;
protected HandlerThread handlerThread;
protected boolean useHandlerThread = false;

protected RepeatListener listener;
protected PollRunnable pollRunnable = new PollRunnable();

public Repeater() {
this(true);
}

/**
* @param processOnStartingThread True if the repeating process should be handled on the same thread that created the Repeater
*/
public Repeater(boolean processOnStartingThread) {
if (processOnStartingThread) {
delayedHandler = new Handler();
return;
}

useHandlerThread = true;
}

/**
* @param handler The Handler to use for the repeating process
*/
public Repeater(Handler handler) {
delayedHandler = handler;
}

/**
* Sets the amount of time between method invocation.
*
* @param milliSeconds The time between method calls [default: {@value #DEFAULT_REPEAT_DELAY}]
*/
public void setRepeaterDelay(int milliSeconds) {
repeatDelay = milliSeconds;
}

/**
* Retrieves the amount of time between method invocation.
*
* @return The millisecond time between method calls
*/
public int getRepeaterDelay() {
return repeatDelay;
}

/**
* Starts the repeater
*/
public void start() {
if (!repeaterRunning) {
repeaterRunning = true;

if (useHandlerThread) {
handlerThread = new HandlerThread(HANDLER_THREAD_NAME);
handlerThread.start();
delayedHandler = new Handler(handlerThread.getLooper());
}

pollRunnable.performPoll();
}
}

/**
* Stops the repeater
*/
public void stop() {
if (handlerThread != null) {
handlerThread.quit();
}

repeaterRunning = false;
}

/**
* Determines if the Repeater is currently running
*
* @return True if the repeater is currently running
*/
public boolean isRunning() {
return repeaterRunning;
}

/**
* Sets the listener to be notified for each repeat
*
* @param listener The listener or null
*/
public void setRepeatListener(@Nullable RepeatListener listener) {
this.listener = listener;
}

protected class PollRunnable implements Runnable {
@Override
public void run() {
if (listener != null) {
listener.onRepeat();
}

if (repeaterRunning) {
performPoll();
}
}

public void performPoll() {
delayedHandler.postDelayed(pollRunnable, repeatDelay);
}
}
}
Loading

0 comments on commit 0895182

Please sign in to comment.