player.addListener(object:Player.EventListener{
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
Log.e("ExoPlayer","playWhenReady: $playWhenReady +$playbackState")
when (playbackState){
Player.STATE_BUFFERING->
Toast.makeText(this@MainActivity,"加载中",Toast.LENGTH_LONG).show()
Player.STATE_READY->
Toast.makeText(this@MainActivity,"播放中",Toast.LENGTH_LONG).show()
Player.STATE_ENDED->
Toast.makeText(this@MainActivity,"播放完成",Toast.LENGTH_LONG).show()
override fun onPlayerError(error: ExoPlaybackException?) {
Log.e("ExoPlayer","ExoPlaybackException: $error")
三,其他功能
1,Clipping a video(视频裁剪)
播放指定的范围:
MediaSource videoSource =
new ProgressiveMediaSource.Factory(...).createMediaSource(videoUri);
// Clip to start at 5 seconds and end at 10 seconds.
ClippingMediaSource clippingSource =
new ClippingMediaSource(
videoSource,
/* startPositionUs= */ 5_000_000,
/* endPositionUs= */ 10_000_000);
2,Side-loading a subtitle file(加载台词)
// Build the video MediaSource.
MediaSource videoSource =
new ProgressiveMediaSource.Factory(...).createMediaSource(videoUri);
// Build the subtitle MediaSource.
Format subtitleFormat = Format.createTextSampleFormat(
id, // An identifier for the track. May be null.
MimeTypes.APPLICATION_SUBRIP, // The mime type. Must be set correctly.
selectionFlags, // Selection flags for the track.
language); // The subtitle language. May be null.
MediaSource subtitleSource =
new SingleSampleMediaSource.Factory(...)
.createMediaSource(subtitleUri, subtitleFormat, C.TIME_UNSET);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
new MergingMediaSource(videoSource, subtitleSource);
3,Advanced composition(视频合并)
下面两种方式实现第一个视频播放两次,第二个播放一次
MediaSource firstSource =
new ProgressiveMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource =
new ProgressiveMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSourceTwice, secondSource);
MediaSource firstSource =
new ProgressiveMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource =
new ProgressiveMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSource, firstSource, secondSource);
还有其他功能,可以参看官方文档:Hello world! - ExoPlayer
四,自定义UI
1,简单自定义
这种方式是通过修改ExoPlayer预留的布局文件来实现定制化,但是这种方式只能修改特定的一些UI。
在源代码中可以找到
int controllerLayoutId = R.layout.exo_player_control_view;
也就是说ExoPlayer默认使用的是这个布局,我们可以在代码中新建一个名为:exo_player_control_view 的layout,或者在xml中添加
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/xxx_view"/>
指定我们的layout来实现覆盖的目的。如下:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@drawable/player_bottom_bg">
<TextView
android:id="@+id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="10dp"
android:textColor="@android:color/white"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:textColor="@android:color/white"
app:layout_constraintRight_toRightOf="parent" />
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_height="26dp"
android:layout_weight="1"
app:layout_constraintBottom_toBottomOf="@id/exo_position"
app:layout_constraintLeft_toRightOf="@id/exo_position"
app:layout_constraintRight_toLeftOf="@id/exo_duration"
app:layout_constraintTop_toTopOf="@id/exo_position"
app:played_color="#FFDE81"
app:unplayed_color="@android:color/black"
app:buffered_color="@android:color/darker_gray"/>
<ImageView
android:id="@+id/exo_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/play_btn"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/exo_progress" />
<ImageView
android:id="@+id/exo_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pause_btn"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/exo_progress" />
<android.support.constraint.Guideline
android:id="@+id/gl"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"
<ImageView
android:id="@+id/exo_rew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pre_btn"
android:layout_marginEnd="8dp"
app:layout_constraintRight_toLeftOf="@id/gl"
app:layout_constraintTop_toTopOf="@id/exo_play" />
<ImageView
android:id="@+id/exo_ffwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/next_btn"
android:layout_marginStart="8dp"
app:layout_constraintLeft_toRightOf="@id/gl"
app:layout_constraintTop_toTopOf="@id/exo_play" />
</android.support.constraint.ConstraintLayout>
</FrameLayout>
效果如下:
这种方式控件的id不能随便起,要与exoPlayer原来PlaybackControlView的布局控件id,名称一致,不然就人家的代码中是获取不到你的id的。
有这些控件id可以使用:
exo_play –>播放
exo_pause –>暂停
exo_rew –>后退
exo_ffwd –>前进
exo_prev –>上一个
exo_next –>下一个
exo_repeat_toggle –>重复模式开关
exo_duration –>视频总时长
exo_position –>当前播放位置
exo_progress –>播放进度
2,高级自定义
高级自定义可以实现任意的效果,如下:
1,首先需要重写: com.google.android.exoplayer2.ui.PlayerView。这里的“重写”是指新建一个类比如MyPlayerView,然后复制PlayerView中的代码到MyPlayerView。使用时引用MyPlayerView。
2,引入自定义的布局,如:senior_diy_player_control_view.xml,通过controller_layout_id属性设置布局,如下:
<com.example.exoplayerdemo.custom.MyPlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:show_buffering="always"
app:controller_layout_id="@layout/senior_diy_player_control_view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
3,改变了布局以后需要添加自定义的事件,需要重写com.google.android.exoplayer2.ui.PlayerControlView,跟上面一样新建一个MyPlayerControlView,然后把代码复制进去,同时需要把MyPlayerView中所有的PlayerControlView的引用改为MyPlayerControlView的引用,此时MyPlayerControlView就生效了,在这里可以找到原布局中的播放、暂停、快进、快退按钮:
private final View playButton;
private final View pauseButton;
private final View fastForwardButton;
private final View rewindButton;
以及对应的点击事件:
playButton = findViewById(R.id.exo_play);
if (playButton != null) {
playButton.setOnClickListener(componentListener);
pauseButton = findViewById(R.id.exo_pause);
if (pauseButton != null) {
pauseButton.setOnClickListener(componentListener);
以及具体的事件方法:
@Override
public void onClick(View view) {
if (playButton == view) {
} else if (pauseButton == view) {
接下来怎么做就不用多说了。
4,此时还不能改变进度条的样式,进度条通过com.google.android.exoplayer2.ui.DefaultTimeBar实现的,如果要改变进度条样式就需要重写DefaultTimeBar,和上面的一样,新建一个MyTimeBar,把代码复制进去,修改MyPlayerControlView中对DefaultTimeBar的引用,DefaultTimeBar继承自View,修改样式也就是自定义View的操作,比如我这里把原来的进度改成圆头的代码:
private void drawTimeBar(Canvas canvas) {
if (duration <= 0) {
// canvas.drawRect(progressBar.left, barTop, progressBar.right, barBottom, unplayedPaint);
// 改为圆角
canvas.drawRoundRect(progressBar.left, barTop, progressBar.right, barBottom, barHeight/2,barHeight/2, unplayedPaint);
return;
if (progressLeft < progressBar.right) {
// canvas.drawRect(progressLeft, barTop, progressBar.right, barBottom, unplayedPaint);
// 改为圆角
canvas.drawRoundRect(scrubberBar.left, barTop, progressBar.right, barBottom, barHeight/2,barHeight/2,unplayedPaint);
if (bufferedRight > bufferedLeft) {
// canvas.drawRect(bufferedLeft, barTop, bufferedRight, barBottom, bufferedPaint);
// 改为圆角
canvas.drawRoundRect(bufferedLeft, barTop, bufferedRight, barBottom, barHeight/2,barHeight/2,bufferedPaint);
if (scrubberBar.width() > 0) {
// canvas.drawRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, playedPaint);
// 改为圆角
canvas.drawRoundRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, barHeight/2,barHeight/2, playedPaint);
这里就不多说了。
代码地址:https://gitee.com/HappyAndroid666/ExoPlayerDemo,感觉有用的帮忙star一下
ExoPlayer是运行在YouTube app Android版本上的视频播放器。不仅功能强大,而且使用简单,可定制性强。ExoPlayer也是Google官方推荐的Android媒体播放器,可以在Android官方文档的音频和视频目录中找到。一,优点和缺点 优点: 1,支持DASH和SmoothStreaming这两种数据格式的资源,而Media......
ExoPlayer:歌官方开源的 Android 媒体播放器。易于定制和扩展,支持丰富的数据格式比如:FMP4、FLV、SmoothStreaming、MP3 等
——————
项目地址:https://ddl.ink/9nN
github加速:https://ddl.ink/dl/
ExoPlayer是Google推出的一种多媒体播放器框架,支持本地音视频的播放,同时网络视频也是支持的。
首先,把ExoPlayer的库导进来。注意,这里使用的ExoPlayer版本是2.16.1,其他版本在初始化ExoPlayer上会略有区别,大家可以参看说明文档。
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
在安卓设备上播放视频和音乐是很受欢迎的活动。Android框架提供了MediaPlayer作为一个快速的解决方案,可以用最少的代码来播放媒体。Android还提供低级别的媒体api框架,如MediaCodec、AudioTrack和MediaDrm,可用于构建自定义媒体播放器解决方案。
ExoPlayer是一款开源的应用级媒体播放器,基于Android的低级媒体API构建。本指南描述...
ExoPlayer简单介绍与使用
ExoPlayer是运行在YouTobe app上的视频播放器。它功能强大的同时又兼容低版本Android设备,在ExoPlayer V2版本上最低支持 android 16 也就是android 4.1版本。
功能强大在支持除Android内置媒体播放器支持的格式外增加自适应格式DASH 和SmoothStreaming, 同时ExoPlayer在代码结构设计上又支持高度定制和扩展。而对于我们普通开发者常使用在对二进制网络流媒体的播放处理。
ExoPlayer是google开源的应用级媒体播放器项目,目前已有1W+的start,并一直在维护。该开源项目包含ExoPlayer库和演示
demo,github地址:https://github.com/google/ExoPlayer。和官方文档https://exoplayer.dev/hello-world.html
二、优缺点比较
与Android内置的MediaPlayer相比,ExoPlayer具有许多优点:
*支持通过HTTP(D.
ExoPlayer1.简述与应用范围ExpPlayer是一个应用于Android系统4.1以上的,开源的,App等级的媒体API,它的开源项目包含了library和示例。它用Java实现了解封装,用MediaCodec实现硬件解码。ExoPlayer相较于MediaPlayer有很多优点:
支持基于http的移动流媒体协议,包括DASH,HLS,Smooth Stream。同时也支持文件流和udp流
ng-zorro-antd 是基于 Angular 开发的一款 UI 框架,提供了丰富的基础组件和样式,可以快速开发出美观、易用的 web 应用程序。同时,ng-zorro-antd 也支持自定义组件。
首先,我们需要理解组件的基本概念。组件是 Angular 应用程序中的基本构建块,它让我们可以封装功能,将页面拆分成可复用的部分。组件通常由 HTML 模板、组件类以及样式组成。
如何自定义 ng-zorro-antd 组件呢?我们可以通过继承 ng-zorro-antd 的基础组件,并添加自己的属性和方法来实现。比如,我们可以创建一个自定义的表单组件,继承自 ng-zorro-antd 中的 Form 组件,在该组件中添加一些自定义的输入属性和方法,以便满足自己的业务需求。
同时,我们还可以利用 ng-zorro-antd 的主题机制,对组件的样式进行自定义。ng-zorro-antd 提供了一些全局样式变量,可以用来改变组件的颜色、字体等样式属性。如果需要更灵活的样式定制,可以通过创建自己的主题文件,并引入到应用程序中来实现。
最后,我们需要注意的是,在自定义 ng-zorro-antd 组件时,要遵循良好的编程习惯,包括代码可读性、可维护性和可扩展性。同时,也要考虑到组件的性能和兼容性问题,确保在不同的浏览器和设备上都能够正常运行。
python调用c/c++代码以及解决ctypes.ArgumentError: argument 1: class 'TypeError': Don't know how to convert
11329
python调用c/c++代码以及解决ctypes.ArgumentError: argument 1: class 'TypeError': Don't know how to convert
嘟嘟嘟嘟,一只小猪:
关于BottomNavigationView的使用姿势都在这里了
我住隔壁我姓王a: