前言
在这篇博文中,我们将深入探讨使用 LibVLC 库的自定义 Android 视频播放器的实现细节。本分步指南将涵盖设置播放器、处理各种事件以及在您的 Android 应用程序中提供无缝视频播放体验的基本方面。
一、LibVLC 概述:
视频播放是许多 Android 应用程序中的常见功能,从流媒体服务到多媒体播放器。LibVLC 是一种流行的多媒体框架,为开发人员构建功能丰富的视频播放器提供了坚实的基础。我们将利用 LibVLC 的功能创建自定义视频播放器。
二、引入依赖
在深入研究实现之前,请确保在 Android 项目中设置了必要的依赖项。包括 LibVLC 库及其关联的依赖项。您可以在 LibVLC AAR下载地址上找到相应的AAR包并导入。
三、LibVLC 使用
1.初始化 LibVLC 实例
public class PlayerView extends FrameLayout {
private LibVLC mLibVLC;
private MediaPlayer mediaPlayer;
// Constructor and other methods...
private void init() {
// Initialize LibVLC and MediaPlayer
mLibVLC = LibVLCUtil.getLibVLC(CloudVlcOptions.getInstance().getOptions());
mediaPlayer = new MediaPlayer(mLibVLC);
// Additional setup...
}
}
2.设置 SurfaceView
public class PlayerView extends FrameLayout {
private SurfaceView mSurface;
private SurfaceHolder mSurfaceHolder;
// Constructor and other methods...
private void init() {
// Additional setup...
// Set up SurfaceView
mSurface = findViewById(R.id.player_surface);
mSurfaceHolder = mSurface.getHolder();
mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
mSurfaceHolder.addCallback(surfaceViewCallback);
// Additional surface-related configuration...
}
private SurfaceHolder.Callback surfaceViewCallback = new SurfaceHolder.Callback() {
// Implement surface-related callbacks...
};
}
3.实现事件处理
public class PlayerView extends FrameLayout {
private MediaPlayer.EventListener eventListener;
// Constructor and other methods...
private void init() {
// Additional setup...
// Event listener
eventListener = new MediaPlayer.EventListener() {
@Override
public void onEvent(MediaPlayer.Event event) {
// Handle MediaPlayer events
}
};
}
}
4.媒体播放控制
public class PlayerView extends FrameLayout {
// Constructor and other methods...
public void start() {
// Start playback
mediaPlayer.play();
}
public void pausePlay(boolean hasToDetach) {
// Pause playback
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
// Additional handling...
}
public void resumePlay() {
try {
//设置视频界面
vlcVout.detachViews();
vlcVout.setVideoSurface(mSurface.getHolder().getSurface(), mSurface.getHolder());
//设置播放窗口的尺寸
vlcVout.setWindowSize(mSurface.getWidth(), mSurface.getHeight());
vlcVout.addCallback(callback);
vlcVout.setVideoView(mSurface);
vlcVout.attachViews(this.layoutListener);
isAttachViewsReady = true;
mediaPlayer.setEventListener(eventListener);
} catch (Exception e) {
e.printStackTrace();
}
}
public void releasePlayer() {
// Release player resources
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
// Additional cleanup...
}
// Additional playback control methods...
}
5.布局和显示处理
public class PlayerView extends FrameLayout {
// Constructor and other methods...
private void updateVideoSurfaces() {
// Update video surfaces based on layout and display size
}
private void changeMediaPlayerLayout(int displayW, int displayH) {
// Change video placement using MediaPlayer API
}
// Additional layout and display handling methods...
}
结论
通过执行这些步骤,您可以使用 LibVLC 将自定义 Android 视频播放器集成到您的应用程序中。以下为LibVLC使用源码↓
1.创建LibVLC自定义View继承FrameLayout
public class PlayerView extends FrameLayout {
private static final String TAG = "PlayerView";
public interface OnChangeListener {
void onBuffer(float buffer);
void onEnd();
void onTimeChange(MediaPlayer.Event event);
void onNewLayout();
void onPlaying();
void onPause();
void onError();
}
private Context mContext;
private LibVLC mLibVLC;
private MediaPlayer mediaPlayer;
private IVLCVout vlcVout;
private int videoWidth;
private int videoHeight;
private boolean isLocal;
private long totalTime = 0;
private SurfaceView mSurface;
//private SurfaceView mSubtitlesSurface;
private SurfaceHolder mSurfaceHolder;
//private SurfaceHolder mSubtitlesSurfaceHolder;
private FrameLayout mSurfaceFrame;
private boolean isAttachViewsReady = false;
private OnChangeListener mOnChangeListener;
private boolean mCanSeek = false;
private String url;
//------------------------------
private final Handler mHandler = new Handler();
private View.OnLayoutChangeListener mOnLayoutChangeListener = null;
private int mVideoHeight = 0;
private int mVideoWidth = 0;
private int mVideoVisibleHeight = 0;
private int mVideoVisibleWidth = 0;
private int mVideoSarNum = 0;
private int mVideoSarDen = 0;
private static final int SURFACE_BEST_FIT = 0;
private static final int SURFACE_FIT_SCREEN = 1;
private static final int SURFACE_FILL = 2;
private static final int SURFACE_16_9 = 3;
private static final int SURFACE_4_3 = 4;
private static final int SURFACE_ORIGINAL = 5;
private static int CURRENT_SIZE = SURFACE_BEST_FIT;
private int screenWid, screenHei;
public PlayerView(Context context) {
super(context);
mContext = context;
init();
}
public PlayerView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
public PlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
public void initPlayer(String url) {
this.url = url;
isLocal = false;
initializeMedia();
updateVideoSurfaces();
}
public void initPlayer(File file) {
this.url = file.getAbsolutePath();
isLocal = true;
initializeMedia();
updateVideoSurfaces();
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.hz_view_player, this);
mSurface = (SurfaceView) findViewById(R.id.player_surface);
mSurfaceFrame = (FrameLayout) findViewById(R.id.player_surface_frame);
mLibVLC = LibVLCUtil.getLibVLC(CloudVlcOptions.getInstance().getOptions());
mediaPlayer = new MediaPlayer(mLibVLC);
mSurfaceHolder = mSurface.getHolder();
mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
mSurfaceHolder.setKeepScreenOn(true);
mSurfaceHolder.addCallback(surfaceViewCallback);
//设置视频界面
vlcVout = mediaPlayer.getVLCVout();
vlcVout.setVideoSurface(mSurface.getHolder().getSurface(), mSurface.getHolder());
vlcVout.setWindowSize(mSurface.getWidth(), mSurface.getHeight());
vlcVout.addCallback(callback);
vlcVout.setVideoView(mSurface);
vlcVout.attachViews(this.layoutListener);
//设置播放窗口的尺寸
PixelFormat info = new PixelFormat();
PixelFormat.getPixelFormatInfo(PixelFormat.RGBX_8888, info);
isAttachViewsReady = true;
mediaPlayer.setEventListener(eventListener);
if (mOnLayoutChangeListener == null) {
mOnLayoutChangeListener = new View.OnLayoutChangeListener() {
private final Runnable mRunnable = new Runnable() {
@Override
public void run() {
updateVideoSurfaces();
}
};
@Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
mHandler.removeCallbacks(mRunnable);
mHandler.post(mRunnable);
}
}
};
}
mSurfaceFrame.addOnLayoutChangeListener(mOnLayoutChangeListener);
}
SurfaceHolder.Callback surfaceViewCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
releasePlayer(); // 在这里调用销毁播放器的方法
}
};
private IVLCVout.Callback callback = new IVLCVout.Callback() {
@Override
public void onSurfacesCreated(IVLCVout ivlcVout) {
}
@Override
public void onSurfacesDestroyed(IVLCVout ivlcVout) {
}
};
private IVLCVout.OnNewVideoLayoutListener layoutListener = new IVLCVout.OnNewVideoLayoutListener() {
@Override
public void onNewVideoLayout(IVLCVout ivlcVout, int i, int i1, int i2, int i3, int i4, int i5) {
try {
screenWid = i;
screenHei = i1;
totalTime = mediaPlayer.getLength();
mCanSeek = mediaPlayer.isSeekable();
videoWidth = i;
videoHeight = i1;
LogUtil.e("--------onNewLayout------------");
if (mOnChangeListener != null) {
mOnChangeListener.onNewLayout();
}
//-------------------------
mVideoWidth = i;
mVideoHeight = i1;
mVideoVisibleWidth = i2;
mVideoVisibleHeight = i3;
mVideoSarNum = i4;
mVideoSarDen = i5;
updateVideoSurfaces();
Log.d(TAG, "Video dimensions: " + videoWidth + "x" + videoHeight);
} catch (Exception e) {
Log.d("vlc-newlayout", e.toString());
}
}
};
private MediaPlayer.EventListener eventListener = new MediaPlayer.EventListener() {
@Override
public void onEvent(MediaPlayer.Event event) {
try {
if (event.type == MediaPlayer.Event.Buffering) {
if (mOnChangeListener != null) {
mOnChangeListener.onBuffer(event.getBuffering());
}
} else if (event.type == MediaPlayer.Event.TimeChanged) {
if (mOnChangeListener != null) {
mOnChangeListener.onTimeChange(event);
}
} else if (event.type == MediaPlayer.Event.Playing) {
if (mOnChangeListener != null) {
mOnChangeListener.onPlaying();
}
} else if (event.type == MediaPlayer.Event.Paused) {
if (mOnChangeListener != null) {
mOnChangeListener.onPause();
}
} else if (event.type == MediaPlayer.Event.EncounteredError) {
if (mOnChangeListener != null) {
mOnChangeListener.onError();
}
}
//播放结束
if (mediaPlayer.getPlayerState() == Media.State.Ended && event.type == MediaPlayer.Event.EndReached) {
if (mOnChangeListener != null) {
mOnChangeListener.onEnd();
}
}
} catch (Exception e) {
Log.d("vlc-event", e.toString());
}
}
};
public void setOnChangeListener(OnChangeListener listener) {
mOnChangeListener = listener;
}
public void changeSurfaceSize(int width) {
this.screenWid = width;
this.screenHei = videoHeight;
ViewGroup.LayoutParams layoutParams = mSurface.getLayoutParams();
layoutParams.width = width;
layoutParams.height = (int) Math.ceil((float) videoHeight * (float) width / (float) videoWidth);
mSurface.setLayoutParams(layoutParams);
}
public void changeSurfaceSize(int width, int height) {
this.screenWid = width;
this.screenHei = height;
Log.d(TAG, "Video dimensions: " + videoWidth + "x" + videoHeight);
ViewGroup.LayoutParams layoutParams = mSurface.getLayoutParams();
float max = Math.max((float) videoWidth / (float) width, (float) videoHeight / (float) height);
layoutParams.width = (int) Math.ceil((float) videoWidth / max);
layoutParams.height = (int) Math.ceil((float) videoHeight / max);
mSurfaceHolder.setFixedSize(videoWidth, videoHeight);
mSurface.setLayoutParams(layoutParams);
}
public void setZOrderMediaOverlay(boolean isOverlay) {
mSurface.setZOrderMediaOverlay(isOverlay);
}
public void start() {
try {
if (mediaPlayer.getPlayerState() == Media.State.Ended) {
restart();
} else if (!mediaPlayer.isPlaying()) {
mediaPlayer.play();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void restart() {
if (mediaPlayer.getPlayerState() == Media.State.Ended) {
Media media = null;
if (isLocal) {
media = new Media(mLibVLC, url);
} else {
media = new Media(mLibVLC, Uri.parse(url));
}
setupMediaOptions(media);
mediaPlayer.setMedia(media);
}
mediaPlayer.play();
}
private void initializeMedia() {
Media media = null;
if (isLocal) {
media = new Media(mLibVLC, url);
} else {
media = new Media(mLibVLC, Uri.parse(url));
}
if (mediaPlayer.getMedia() == null) {
setupMediaOptions(media);
mediaPlayer.setMedia(media);
}
}
private Media createMedia(String mediaUrl) {
return new Media(mLibVLC, Uri.parse(mediaUrl));
}
private void setupMediaOptions(Media media) {
int cache = 300;
media.addOption(":network-caching=" + cache);
media.addOption(":file-caching=" + cache);
media.addOption(":live-caching=" + cache);
media.addOption(":sout-mux-caching=" + cache);
media.addOption(":codec=mediacodec,iomx,all");
media.setHWDecoderEnabled(true, false);
}
public void pausePlay(boolean hasToDetach) {
try {
if(mediaPlayer!=null){
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
//imgPlay.setBackgroundResource(R.drawable.videoviewx_play);
}
if (hasToDetach) {
vlcVout.detachViews();
vlcVout.removeCallback(callback);
mediaPlayer.setEventListener(null);
isAttachViewsReady = false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void resumePlay() {
try {
//设置视频界面
vlcVout.detachViews();
vlcVout.setVideoSurface(mSurface.getHolder().getSurface(), mSurface.getHolder());
//设置播放窗口的尺寸
vlcVout.setWindowSize(mSurface.getWidth(), mSurface.getHeight());
vlcVout.addCallback(callback);
vlcVout.setVideoView(mSurface);
vlcVout.attachViews(this.layoutListener);
isAttachViewsReady = true;
mediaPlayer.setEventListener(eventListener);
} catch (Exception e) {
e.printStackTrace();
}
}
public void releasePlayer() {
try {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
if (mLibVLC != null) {
mLibVLC.release();
mLibVLC = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean isAttachViewsReady() {
return isAttachViewsReady;
}
public long getTime() {
return mediaPlayer.getTime();
}
public long getLength() {
return mediaPlayer.getLength();
}
public void setTime(long time) {
mediaPlayer.setTime(time);
}
public void setNetWorkCache(int time) {
//mediaPlayer.setNetworkCaching(time);
}
public boolean isPlaying() {
if(mediaPlayer!=null){
return mediaPlayer.isPlaying();
}
return false;
}
public boolean isSeekable() {
return mediaPlayer.isSeekable();
}
public int getVolume() {
return mediaPlayer.getVolume();
}
public void setVolume(int volume) {
mediaPlayer.setVolume(volume);
}
public void seek(int delta) {
if (mediaPlayer.getLength() <= 0 || !mCanSeek)
return;
if (mediaPlayer.getPlayerState() == Media.State.Ended) {
restart();
}
long position = delta;
if (position < 0)
position = 0;
mediaPlayer.setTime(position);
}
private void updateVideoSurfaces() {
// int sw = getWindow().getDecorView().getWidth();
// int sh = getWindow().getDecorView().getHeight();
int sw = mSurface.getWidth();
int sh = mSurface.getHeight();
// sanity check
if (sw * sh == 0) {
Log.e(TAG, "Invalid surface size");
return;
} else {
Log.e(TAG, "surface size" + sw + "*--*" + sh);
}
if (mediaPlayer.getVLCVout() != null) {
mediaPlayer.getVLCVout().setWindowSize(sw, sh);
}
ViewGroup.LayoutParams lp = mSurface.getLayoutParams();
if (mVideoWidth * mVideoHeight == 0) {
/* Case of OpenGL vouts: handles the placement of the video using MediaPlayer API */
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
mSurface.setLayoutParams(lp);
lp = mSurfaceFrame.getLayoutParams();
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
mSurfaceFrame.setLayoutParams(lp);
changeMediaPlayerLayout(sw, sh);
return;
}
if (lp.width == lp.height && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
/* We handle the placement of the video using Android View LayoutParams */
mediaPlayer.setAspectRatio(null);
mediaPlayer.setScale(0);
}
double dw = sw, dh = sh;
final boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
if (sw > sh && isPortrait || sw < sh && !isPortrait) {
dw = sh;
dh = sw;
}
// compute the aspect ratio
double ar, vw;
if (mVideoSarDen == mVideoSarNum) {
/* No indication about the density, assuming 1:1 */
vw = mVideoVisibleWidth;
ar = (double) mVideoVisibleWidth / (double) mVideoVisibleHeight;
} else {
/* Use the specified aspect ratio */
vw = mVideoVisibleWidth * (double) mVideoSarNum / mVideoSarDen;
ar = vw / mVideoVisibleHeight;
}
// compute the display aspect ratio
double dar = dw / dh;
switch (CURRENT_SIZE) {
case SURFACE_BEST_FIT:
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_FIT_SCREEN:
if (dar >= ar)
dh = dw / ar; /* horizontal */
else
dw = dh * ar; /* vertical */
break;
case SURFACE_FILL:
break;
case SURFACE_16_9:
ar = 16.0 / 9.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_4_3:
ar = 4.0 / 3.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_ORIGINAL:
dh = mVideoVisibleHeight;
dw = vw;
break;
}
// set display size
lp.width = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
mSurface.setLayoutParams(lp);
// set frame size (crop if necessary)
lp = mSurfaceFrame.getLayoutParams();
lp.width = (int) Math.floor(dw);
lp.height = (int) Math.floor(dh);
mSurfaceFrame.setLayoutParams(lp);
mSurface.invalidate();
}
private void changeMediaPlayerLayout(int displayW, int displayH) {
/* Change the video placement using the MediaPlayer API */
switch (CURRENT_SIZE) {
case SURFACE_BEST_FIT:
try {
mediaPlayer.setAspectRatio(null);
mediaPlayer.setScale(0);
} catch (Exception e) {
e.printStackTrace();
}
break;
case SURFACE_FIT_SCREEN:
case SURFACE_FILL: {
Media.VideoTrack vtrack = mediaPlayer.getCurrentVideoTrack();
if (vtrack == null)
return;
final boolean videoSwapped = vtrack.orientation == Media.VideoTrack.Orientation.LeftBottom
|| vtrack.orientation == Media.VideoTrack.Orientation.RightTop;
if (CURRENT_SIZE == SURFACE_FIT_SCREEN) {
int videoW = vtrack.width;
int videoH = vtrack.height;
if (videoSwapped) {
int swap = videoW;
videoW = videoH;
videoH = swap;
}
if (vtrack.sarNum != vtrack.sarDen)
videoW = videoW * vtrack.sarNum / vtrack.sarDen;
float ar = videoW / (float) videoH;
float dar = displayW / (float) displayH;
float scale;
if (dar >= ar)
scale = displayW / (float) videoW; /* horizontal */
else
scale = displayH / (float) videoH; /* vertical */
mediaPlayer.setScale(scale);
mediaPlayer.setAspectRatio(null);
} else {
mediaPlayer.setScale(0);
mediaPlayer.setAspectRatio(!videoSwapped ? "" + displayW + ":" + displayH
: "" + displayH + ":" + displayW);
}
break;
}
case SURFACE_16_9:
mediaPlayer.setAspectRatio("16:9");
mediaPlayer.setScale(0);
break;
case SURFACE_4_3:
mediaPlayer.setAspectRatio("4:3");
mediaPlayer.setScale(0);
break;
case SURFACE_ORIGINAL:
mediaPlayer.setAspectRatio(null);
mediaPlayer.setScale(1);
break;
}
}
}
2.自定义View的XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/player_surface_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black">
<!--
the double FrameLayout is necessary here to do cropping on the bottom right
(which requires the surface not be centered), while keeping the result centered
-->
<SurfaceView
android:id="@+id/player_surface"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
</FrameLayout>
3.PlayerView中引用的‘LibVLCUtil’ VLC设置项已单例模式创建
public class LibVLCUtil {
private static LibVLC libVLC = null;
public synchronized static LibVLC getLibVLC(ArrayList<String> options) throws IllegalStateException {
if (libVLC == null) {
if (options == null) {
libVLC = new LibVLC(MyApplication.getInstance(),new ArrayList<String>());
} else {
libVLC = new LibVLC(MyApplication.getInstance(),options);
}
}
return libVLC;
}
}
4.PlayerView中引用的‘CloudVlcOptions’ VLC设置项已单例模式创建
public class CloudVlcOptions {
private static CloudVlcOptions instance=null;
private ArrayList<String> list=null;
private CloudVlcOptions(){
this.list=new ArrayList<>();
list.add(false ? "--audio-time-stretch" : "--no-audio-time-stretch");
list.add("--audio-resampler");
list.add("--aout=opensles");
list.add("--audio-time-stretch");
list.add("--network-caching="+300);
list.add("--clock-jitter="+300);
list.add("--file-caching=" + 300);
list.add("--demux=avformat");
list.add("-vvv");
}
public static CloudVlcOptions getInstance(){
if(instance==null){
instance=new CloudVlcOptions();
}
return instance;
}
public ArrayList<String> getOptions(){
return list;
}
}
如有出现图像失帧或出现
libvlc input source: attachment of directory-extractor failed...
类似的错误,可以尝试添加"--demux=avformat"
,或查看VLC官网议题:图像失帧,尝试是否对你有利。
5.Activity中使用
public class VideoPlayerTestActivity extends BaseActivity implements View.OnClickListener {
private String mUrl;
private File file;
private boolean isChangeDisplay = false;
private long timeCount = 0;
private PlayerView mPlayerView;
private RelativeLayout durationBarRl;
private ImageView playIconIv;
private TextView curDurationTv;
private TextView totalDurationTv;
private SeekBar progressSeekBar;
private ImageView shareIv;
private SimpleDateFormat sp = new SimpleDateFormat("mm:ss");
private boolean hasSetVideoPosition = false;
//发送心跳
private Timer mHeartBeatTimer = null;
private boolean isVisFront = false;
@Override
protected int getContentViewId() {
return R.layout.video_player;
}
@Override
protected void initView() {
super.initView();
DeviceTools.hideNavigationBar(this);
mUrl = getIntent().getStringExtra("url");
file = (File) getIntent().getSerializableExtra("file");
if (TextUtils.isEmpty(mUrl)) {
shareIv.setVisibility(View.VISIBLE);
} else {
shareIv.setVisibility(View.GONE);
}
initPlayer();
if(Config.CURRENT_DEVICE_TYPE != null){
if(Config.CURRENT_DEVICE_TYPE.equals(Type.DvrType.SG20_B.getVal()) || Config.CURRENT_DEVICE_TYPE.equals(Type.DvrType.SG18A.getVal())){
progressSeekBar.setEnabled(false);
}else {
progressSeekBar.setEnabled(true);
initSeekBar();
}
}else {
progressSeekBar.setEnabled(true);
initSeekBar();
}
}
@Override
protected void findViews() {
super.findViews();
mPlayerView = findViewById(R.id.pv_video);
durationBarRl = findViewById(R.id.rl_duration_bar);
playIconIv = findViewById(R.id.iv_play_icon);
curDurationTv = findViewById(R.id.tv_cur_duration);
totalDurationTv = findViewById(R.id.tv_total_duration);
progressSeekBar = findViewById(R.id.seek_bar_progress);
shareIv = findViewById(R.id.iv_share);
findViewById(R.id.iv_close).setOnClickListener(this);
mPlayerView.setOnClickListener(this);
playIconIv.setOnClickListener(this);
shareIv.setOnClickListener(this);
}
private void initSeekBar() {
progressSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
boolean isUser = false;
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
isUser = b;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (isUser) {
mPlayerView.seek(seekBar.getProgress());
}
}
});
}
private void share() {
if (file == null) {
return;
}
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.setType("*/*");
startActivity(intent);
}
private void initPlayer() {
//初始化播放器
if (!TextUtils.isEmpty(mUrl)) {
mPlayerView.initPlayer(mUrl);
}
if (file != null) {
mPlayerView.initPlayer(file);
}
//设置事件监听,监听缓冲进度等
mPlayerView.setOnChangeListener(new PlayerView.OnChangeListener() {
@Override
public void onBuffer(float buffer) {
if (buffer != 100) {
LoadingUtil.showLoadingDialog();
} else {
LoadingUtil.hideLoadingDialog();
if (hasSetVideoPosition) {
hasSetVideoPosition = false;
}
}
}
@Override
public void onEnd() {
playIconIv.setImageResource(R.mipmap.hz_dvr_icon_play);
ToastUtils.showToast(VideoPlayerTestActivity.this, MyApplication.getAppResources().getString(R.string.play_end));
LoadingUtil.hideLoadingDialog();
progressSeekBar.setProgress(progressSeekBar.getMax());
if (mPlayerView.getLength() > 0) {
Date curDate = new Date(mPlayerView.getLength());
curDurationTv.setText(sp.format(curDate));
}
}
@Override
public void onTimeChange(MediaPlayer.Event event) {
progressSeekBar.setProgress((int) mPlayerView.getTime());
Date curDate = new Date(mPlayerView.getTime());
curDurationTv.setText(sp.format(curDate));
}
@Override
public void onNewLayout() {
onPlayerLoadComplete();
mPlayerView.changeSurfaceSize(DeviceTools.getWindowHeight(),DeviceTools.getWindowWidth());
LogUtil.e("----------onNewLayout--------------");
if (isChangeDisplay) {
isChangeDisplay = false;
mPlayerView.start();
}
}
@Override
public void onPlaying() {
playIconIv.setImageResource(R.mipmap.hz_dvr_icon_pause);
}
@Override
public void onPause() {
playIconIv.setImageResource(R.mipmap.hz_dvr_icon_play);
}
@Override
public void onError() {
}
});
// 开始播放
mPlayerView.start();
hasSetVideoPosition = true;
}
private void onPlayerLoadComplete() {
Date curDate = new Date(mPlayerView.getTime());
Date totalDate = new Date(mPlayerView.getLength());
curDurationTv.setText(sp.format(curDate));
if (mPlayerView.getLength() > 0) {
totalDurationTv.setText(sp.format(totalDate));
progressSeekBar.setMax((int) mPlayerView.getLength());
Drawable drawable = new BitmapDrawable(getResources(), BitmapFactory.decodeResource(getResources(), R.mipmap.hz_box_dvr_video_slider));
progressSeekBar.setThumb(drawable);
} else {
progressSeekBar.setMax((int) timeCount);
totalDurationTv.setText(sp.format(timeCount));
}
progressSeekBar.setProgress((int) mPlayerView.getTime());
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mPlayerView!=null){
mPlayerView.releasePlayer();
}
}
@Override
protected void onResume() {
super.onResume();
mPlayerView.resumePlay();
mPlayerView.start();
}
@Override
protected void onPause() {
super.onPause();
mPlayerView.pausePlay(true);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.pv_video:
isVisFront = !isVisFront;
this.durationBarRl.setVisibility(isVisFront?View.GONE:View.VISIBLE);
break;
case R.id.iv_close:
finish();
break;
case R.id.iv_play_icon:
if (mPlayerView.isPlaying()) {
mPlayerView.pausePlay(false);
playIconIv.setImageResource(R.mipmap.hz_dvr_icon_play);
} else {
mPlayerView.start();
playIconIv.setImageResource(R.mipmap.hz_dvr_icon_pause);
}
break;
case R.id.iv_share:
share();
break;
}
}
}
6.Activity的XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tool="http://schemas.android.com/tools"
android:id="@+id/rl_parent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.corelink.page.widget.PlayerView
android:id="@+id/pv_video"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<RelativeLayout
android:id="@+id/rl_top_bar"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@color/black_alpha_50_percent"
android:paddingLeft="15dp"
android:paddingRight="15dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/camera_view"
android:textColor="@color/white_ffffff"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="@color/white_ffffff"
android:textSize="14sp" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:paddingLeft="13dp"
android:src="@mipmap/hz_dvr_close_setting_window" />
<ImageView
android:id="@+id/iv_share"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/iv_close"
android:paddingRight="13dp"
android:src="@mipmap/hz_dvr_file_icon_share" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_duration_bar"
android:layout_width="290dp"
android:layout_height="34dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="8dp"
android:background="@color/black_alpha_50_percent"
android:paddingLeft="7dp">
<ImageView
android:id="@+id/iv_play_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingRight="16dp"
android:src="@mipmap/hz_dvr_icon_pause" />
<TextView
android:id="@+id/tv_cur_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/iv_play_icon"
android:textColor="@color/white_ffffff"
android:textSize="12sp"
tool:text="00:00" />
<TextView
android:id="@+id/tv_total_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:paddingRight="7dp"
android:textColor="@color/white_ffffff"
android:textSize="12sp"
tool:text="00:00" />
<SeekBar
android:id="@+id/seek_bar_progress"
style="@style/seek_bar_vol1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="11dp"
android:layout_marginRight="11dp"
android:layout_toLeftOf="@id/tv_total_duration"
android:layout_toRightOf="@id/tv_cur_duration" />
</RelativeLayout>
</RelativeLayout>
7.资源文件
hz_dvr_icon_play
hz_dvr_icon_pause
hz_box_dvr_video_slider
hz_dvr_close_setting_window
hz_dvr_file_icon_share文章来源:https://www.toymoban.com/news/detail-786611.html
四、总结
使用 LibVLC 构建功能强大的 Android 视频播放器为开发人员提供了处理多媒体内容的多功能解决方案。按照这篇博文中概述的步骤,您可以创建具有高级播放控件、事件处理和布局调整功能丰富的视频播放器。LibVLC 的强大功能使其成为在 Android 应用程序中为用户提供无缝多媒体体验的绝佳选择。文章来源地址https://www.toymoban.com/news/detail-786611.html
到了这里,关于使用 LibVLC 构建自定义 Android 视频播放器:分步指南,降低延迟/图像失帧(附源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!