Commit eb47ac608b5e6dc7c812a7975fe42badde9be6a1
1 parent
beebfcb8
1 封装视频库
Showing
31 changed files
with
3769 additions
and
148 deletions
Too many changes to show.
To preserve performance only 31 of 124 files are displayed.
app/strike/src/main/java/com/cnlive/strike/demo/activity/CNplayerActivity.java
| 1 | package com.cnlive.strike.demo.activity; | 1 | package com.cnlive.strike.demo.activity; |
| 2 | 2 | ||
| 3 | import android.content.Intent; | 3 | import android.content.Intent; |
| 4 | -import android.os.Handler; | ||
| 5 | -import android.widget.Toast; | ||
| 6 | 4 | ||
| 7 | -import androidx.annotation.Nullable; | ||
| 8 | import androidx.databinding.ViewDataBinding; | 5 | import androidx.databinding.ViewDataBinding; |
| 9 | 6 | ||
| 10 | import com.alibaba.android.arouter.facade.annotation.Route; | 7 | import com.alibaba.android.arouter.facade.annotation.Route; |
| 11 | import com.cnlive.cloud.BuildConfig; | 8 | import com.cnlive.cloud.BuildConfig; |
| 12 | import com.cnlive.cloud.R; | 9 | import com.cnlive.cloud.R; |
| 13 | import com.cnlive.cloud.databinding.ActivityCnvideoBinding; | 10 | import com.cnlive.cloud.databinding.ActivityCnvideoBinding; |
| 14 | -import com.cnlive.cloud.user.auth.UserService; | ||
| 15 | import com.cnlive.core.libs.base.frame.activity.BaseActivity; | 11 | import com.cnlive.core.libs.base.frame.activity.BaseActivity; |
| 16 | -import com.cnlive.core.libs.base.util.AlertUtil; | ||
| 17 | import com.cnlive.core.libs.base.util.ChannelUtil; | 12 | import com.cnlive.core.libs.base.util.ChannelUtil; |
| 18 | import com.cnlive.core.libs.base.util.QMUITipDialogUtil; | 13 | import com.cnlive.core.libs.base.util.QMUITipDialogUtil; |
| 19 | import com.libs.video.util.Config; | 14 | import com.libs.video.util.Config; |
| 20 | -import com.libs.video.util.ProbeUtil; | ||
| 21 | -import com.libs.video.video.VideoView; | ||
| 22 | import com.libs.video.video.base.IMediaPlayer; | 15 | import com.libs.video.video.base.IMediaPlayer; |
| 23 | 16 | ||
| 24 | -import org.greenrobot.eventbus.Subscribe; | ||
| 25 | -import org.greenrobot.eventbus.ThreadMode; | ||
| 26 | - | ||
| 27 | @Route(path = CNplayerActivity.PATH) | 17 | @Route(path = CNplayerActivity.PATH) |
| 28 | public class CNplayerActivity extends BaseActivity implements IMediaPlayer.OnPreparedListener, | 18 | public class CNplayerActivity extends BaseActivity implements IMediaPlayer.OnPreparedListener, |
| 29 | IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnInfoListener, | 19 | IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnInfoListener, |
app/strike/src/main/java/com/cnlive/strike/demo/activity/ExoPlayerActivity.java
| @@ -3,32 +3,31 @@ package com.cnlive.strike.demo.activity; | @@ -3,32 +3,31 @@ package com.cnlive.strike.demo.activity; | ||
| 3 | import android.net.Uri; | 3 | import android.net.Uri; |
| 4 | import android.view.Surface; | 4 | import android.view.Surface; |
| 5 | import android.view.SurfaceControl; | 5 | import android.view.SurfaceControl; |
| 6 | -import android.view.SurfaceView; | ||
| 7 | -import android.view.View; | 6 | +import android.widget.Toast; |
| 8 | 7 | ||
| 9 | -import androidx.annotation.Nullable; | ||
| 10 | -import androidx.core.content.ContextCompat; | ||
| 11 | import androidx.databinding.ViewDataBinding; | 8 | import androidx.databinding.ViewDataBinding; |
| 12 | 9 | ||
| 10 | +import com.cnlive.cloud.BuildConfig; | ||
| 13 | import com.cnlive.cloud.R; | 11 | import com.cnlive.cloud.R; |
| 14 | import com.cnlive.cloud.databinding.ActivityExplayerBinding; | 12 | import com.cnlive.cloud.databinding.ActivityExplayerBinding; |
| 15 | import com.cnlive.core.libs.base.frame.activity.BaseActivity; | 13 | import com.cnlive.core.libs.base.frame.activity.BaseActivity; |
| 16 | -import com.google.android.exoplayer2.C; | ||
| 17 | -import com.google.android.exoplayer2.ExoPlaybackException; | ||
| 18 | -import com.google.android.exoplayer2.PlaybackParameters; | ||
| 19 | import com.google.android.exoplayer2.Player; | 14 | import com.google.android.exoplayer2.Player; |
| 20 | import com.google.android.exoplayer2.SimpleExoPlayer; | 15 | import com.google.android.exoplayer2.SimpleExoPlayer; |
| 21 | -import com.google.android.exoplayer2.Timeline; | ||
| 22 | -import com.google.android.exoplayer2.source.TrackGroupArray; | ||
| 23 | import com.google.android.exoplayer2.source.hls.HlsMediaSource; | 16 | import com.google.android.exoplayer2.source.hls.HlsMediaSource; |
| 24 | -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; | ||
| 25 | -import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; | ||
| 26 | import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; | 17 | import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; |
| 27 | import com.google.android.exoplayer2.util.Util; | 18 | import com.google.android.exoplayer2.util.Util; |
| 28 | import com.google.android.exoplayer2.video.VideoListener; | 19 | import com.google.android.exoplayer2.video.VideoListener; |
| 29 | -import com.libs.video.video.exoplayer.CNLiveBaseVideoControlView; | ||
| 30 | -import com.libs.video.video.exoplayer.MyTestControlView; | ||
| 31 | -import com.libs.video.video.exoplayer.VideoPlayer; | 20 | +import com.libs.video.video.exoplayer.play.CNVideoView; |
| 21 | +import com.libs.video.video.test.VideoPlayer; | ||
| 22 | +import com.libs.video.video.exoplayer.component.DefaultAdControlView; | ||
| 23 | +import com.libs.video.video.exoplayer.controller.StandardVideoController; | ||
| 24 | +import com.libs.video.video.exoplayer.play.VideoViewConfig; | ||
| 25 | +import com.libs.video.video.exoplayer.play.VideoViewManager; | ||
| 26 | +import com.libs.video.video.exoplayer.playerCore.exoplayer.ExoMediaPlayerFactory; | ||
| 27 | +import com.libs.video.video.exoplayer.render.surface.SurfaceRenderViewFactory; | ||
| 28 | + | ||
| 29 | +import java.util.ArrayList; | ||
| 30 | +import java.util.List; | ||
| 32 | 31 | ||
| 33 | public class ExoPlayerActivity extends BaseActivity implements VideoListener, Player.EventListener { | 32 | public class ExoPlayerActivity extends BaseActivity implements VideoListener, Player.EventListener { |
| 34 | ActivityExplayerBinding binding; | 33 | ActivityExplayerBinding binding; |
| @@ -37,6 +36,9 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | @@ -37,6 +36,9 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | ||
| 37 | SurfaceControl surfaceControl; | 36 | SurfaceControl surfaceControl; |
| 38 | Surface videoSurface; | 37 | Surface videoSurface; |
| 39 | private VideoPlayer videoPlayer; | 38 | private VideoPlayer videoPlayer; |
| 39 | + private static final String URL_AD = "https://gslb.miaopai.com/stream/IR3oMYDhrON5huCmf7sHCfnU5YKEkgO2.mp4"; | ||
| 40 | + | ||
| 41 | + private StandardVideoController mController; | ||
| 40 | 42 | ||
| 41 | @Override | 43 | @Override |
| 42 | protected int getBindLayoutId() { | 44 | protected int getBindLayoutId() { |
| @@ -51,9 +53,6 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | @@ -51,9 +53,6 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | ||
| 51 | @Override | 53 | @Override |
| 52 | protected void initView() { | 54 | protected void initView() { |
| 53 | initVideoPlayer(); | 55 | initVideoPlayer(); |
| 54 | - // Produces DataSource instances through which media data is loaded. | ||
| 55 | -// DataSource dataSource =new DefaultDataSource(context,Util.getUserAgent(context, "yourApplicationName"),true); | ||
| 56 | -// Create a data source factory. | ||
| 57 | DefaultHttpDataSourceFactory dataSourceFactory = | 56 | DefaultHttpDataSourceFactory dataSourceFactory = |
| 58 | new DefaultHttpDataSourceFactory(Util.getUserAgent(context, "app-name")); | 57 | new DefaultHttpDataSourceFactory(Util.getUserAgent(context, "app-name")); |
| 59 | 58 | ||
| @@ -76,50 +75,99 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | @@ -76,50 +75,99 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | ||
| 76 | // player.addListener(this); | 75 | // player.addListener(this); |
| 77 | // // Prepare the player with the source. | 76 | // // Prepare the player with the source. |
| 78 | player.prepare(hlsMediaSource); | 77 | player.prepare(hlsMediaSource); |
| 79 | -// //绑定视图 | ||
| 80 | -// binding.playerView.setPlayer(player); | ||
| 81 | -// player.setPlayWhenReady(true); | ||
| 82 | - //全屏 | ||
| 83 | -// player.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); | ||
| 84 | - //按比例放大 | ||
| 85 | -// player.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); | ||
| 86 | -// player.setForegroundMode(true); | ||
| 87 | -// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | ||
| 88 | -// surfaceControl = | ||
| 89 | -// new SurfaceControl.Builder() | ||
| 90 | -// .setName("测试") | ||
| 91 | -// .setBufferSize(/* width= */ 0, /* height= */ 0) | ||
| 92 | -// .build(); | ||
| 93 | -// videoSurface = new Surface(surfaceControl); | ||
| 94 | -// player.setVideoSurface(videoSurface); | ||
| 95 | -// } | ||
| 96 | -// CacheUtil cacheUtil=new CacheUtil(this); | ||
| 97 | -// try { | ||
| 98 | -// cacheUtil.getVideoUri("123456"); | ||
| 99 | -// cacheUtil.add("123456","test"); | ||
| 100 | -// cacheUtil.getVideoUri("123456"); | ||
| 101 | -// } catch (IllegalBlockSizeException e) { | ||
| 102 | -// e.printStackTrace(); | ||
| 103 | -// } catch (NoSuchPaddingException e) { | ||
| 104 | -// e.printStackTrace(); | ||
| 105 | -// } catch (BadPaddingException e) { | ||
| 106 | -// e.printStackTrace(); | ||
| 107 | -// } catch (InvalidKeySpecException e) { | ||
| 108 | -// e.printStackTrace(); | ||
| 109 | -// } catch (InvalidKeyException e) { | ||
| 110 | -// e.printStackTrace(); | ||
| 111 | -// } | ||
| 112 | -// | ||
| 113 | -// binding.playerView.setOnClickListener(new View.OnClickListener() { | 78 | + //播放器配置,注意:此为全局配置,按需开启 |
| 79 | + VideoViewManager.setConfig(VideoViewConfig.newBuilder() | ||
| 80 | + .setLogEnabled(BuildConfig.DEBUG)//调试的时候请打开日志,方便排错 | ||
| 81 | +// .setPlayerFactory(IjkPlayerFactory.create()) | ||
| 82 | +// .setPlayerFactory(ThunderMediaPlayerFactory.create()) | ||
| 83 | + .setPlayerFactory(ExoMediaPlayerFactory.create()) | ||
| 84 | + .setRenderViewFactory(SurfaceRenderViewFactory.create()) | ||
| 85 | + .setEnableOrientation(true) | ||
| 86 | +// .setEnableAudioFocus(false) | ||
| 87 | +// .setScreenScaleType(VideoView.SCREEN_SCALE_MATCH_PARENT) | ||
| 88 | +// .setAdaptCutout(false) | ||
| 89 | +// .setPlayOnMobileNetwork(true) | ||
| 90 | +// .setProgressManager(new ProgressManagerImpl()) | ||
| 91 | + .build()); | ||
| 92 | + mController = new StandardVideoController(this); | ||
| 93 | + DefaultAdControlView adControlView = new DefaultAdControlView(this); | ||
| 94 | + adControlView.setListener(new DefaultAdControlView.AdControlListener() { | ||
| 95 | + @Override | ||
| 96 | + public void onAdClick() { | ||
| 97 | + Toast.makeText(me, "广告点击跳转", Toast.LENGTH_SHORT).show(); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @Override | ||
| 101 | + public void onSkipAd() { | ||
| 102 | + playVideo(); | ||
| 103 | + } | ||
| 104 | + }); | ||
| 105 | + mController.addControlComponent(adControlView); | ||
| 106 | + binding.playerView.setUrl("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8"); | ||
| 107 | + binding.playerView.setVideoController(mController); | ||
| 108 | + binding.playerView.start(); | ||
| 109 | + //监听播放结束 | ||
| 110 | +// binding.playerView.addOnStateChangeListener(new CNVideoView.SimpleOnStateChangeListener() { | ||
| 114 | // @Override | 111 | // @Override |
| 115 | -// public void onClick(View v) { | ||
| 116 | -//// player.seekTo(player.getCurrentWindowIndex(),5000); | 112 | +// public void onPlayStateChanged(int playState) { |
| 113 | +// if (playState == CNVideoView.STATE_PLAYBACK_COMPLETED) { | ||
| 114 | +// playVideo(); | ||
| 115 | +// } | ||
| 117 | // } | 116 | // } |
| 118 | // }); | 117 | // }); |
| 119 | } | 118 | } |
| 120 | 119 | ||
| 120 | + @Override | ||
| 121 | + protected void onResume() { | ||
| 122 | + super.onResume(); | ||
| 123 | + if (binding.playerView != null) { | ||
| 124 | + binding.playerView.onResume(); | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Override | ||
| 129 | + public void onBackPressed() { | ||
| 130 | + if (!binding.playerView.isBackPressed()) { | ||
| 131 | + super.onBackPressed(); | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + @Override | ||
| 136 | + protected void onPause() { | ||
| 137 | + super.onPause(); | ||
| 138 | + if (binding.playerView != null) { | ||
| 139 | + binding.playerView.onPause(); | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + @Override | ||
| 144 | + protected void onDestroy() { | ||
| 145 | + super.onDestroy(); | ||
| 146 | + if (binding.playerView != null) { | ||
| 147 | + binding.playerView.onDestroy(); | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + /** | ||
| 152 | + * 播放正片 | ||
| 153 | + */ | ||
| 154 | + private void playVideo() { | ||
| 155 | + binding.playerView.release(); | ||
| 156 | + mController.removeAllControlComponent(); | ||
| 157 | + mController.addDefaultControlComponent("正片", false); | ||
| 158 | + //重新设置数据 | ||
| 159 | + List<String> list = new ArrayList<>(); | ||
| 160 | + list.add("http://sbslive.cnrmobile.com/storage/storage4/76/23/10/14de52115893fb5371b7e064f910ab7b.3gp"); | ||
| 161 | + list.add("https://qq.com-ok-qq.com/20191005/24249_f5e36323/index.m3u8"); | ||
| 162 | + list.add("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8"); | ||
| 163 | + list.add("http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8"); | ||
| 164 | + binding.playerView.setDataSource(list); | ||
| 165 | + //开始播放 | ||
| 166 | + binding.playerView.start(); | ||
| 167 | + } | ||
| 168 | + | ||
| 121 | private void initVideoPlayer() { | 169 | private void initVideoPlayer() { |
| 122 | - binding.playerView.setCustomControllView(new MyTestControlView(context)); | 170 | +// binding.playerView.setCustomControllView(new MyTestControlView(context)); |
| 123 | // Uri dashUri = Uri.parse("rtmp://tyzg.rtmps2.cnlive.com/live-tyzg-577/577_999"); | 171 | // Uri dashUri = Uri.parse("rtmp://tyzg.rtmps2.cnlive.com/live-tyzg-577/577_999"); |
| 124 | 172 | ||
| 125 | // Uri mp4VideoUri = Uri.parse("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8"); | 173 | // Uri mp4VideoUri = Uri.parse("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8"); |
| @@ -137,73 +185,4 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | @@ -137,73 +185,4 @@ public class ExoPlayerActivity extends BaseActivity implements VideoListener, Pl | ||
| 137 | // }); | 185 | // }); |
| 138 | } | 186 | } |
| 139 | 187 | ||
| 140 | - @Override | ||
| 141 | - public void onBackPressed() { | ||
| 142 | - binding.playerView.onBackPress(); | ||
| 143 | - } | ||
| 144 | - | ||
| 145 | - @Override | ||
| 146 | - public void onTimelineChanged(Timeline timeline, int reason) { | ||
| 147 | - | ||
| 148 | - } | ||
| 149 | - | ||
| 150 | - @Override | ||
| 151 | - public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) { | ||
| 152 | - | ||
| 153 | - } | ||
| 154 | - | ||
| 155 | - @Override | ||
| 156 | - public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { | ||
| 157 | - | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - @Override | ||
| 161 | - public void onLoadingChanged(boolean isLoading) { | ||
| 162 | - | ||
| 163 | - } | ||
| 164 | - | ||
| 165 | - @Override | ||
| 166 | - public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { | ||
| 167 | - logError(playbackState + ""); | ||
| 168 | - } | ||
| 169 | - | ||
| 170 | - @Override | ||
| 171 | - public void onPlaybackSuppressionReasonChanged(int playbackSuppressionReason) { | ||
| 172 | - | ||
| 173 | - } | ||
| 174 | - | ||
| 175 | - @Override | ||
| 176 | - public void onIsPlayingChanged(boolean isPlaying) { | ||
| 177 | - | ||
| 178 | - } | ||
| 179 | - | ||
| 180 | - @Override | ||
| 181 | - public void onRepeatModeChanged(int repeatMode) { | ||
| 182 | - | ||
| 183 | - } | ||
| 184 | - | ||
| 185 | - @Override | ||
| 186 | - public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { | ||
| 187 | - | ||
| 188 | - } | ||
| 189 | - | ||
| 190 | - @Override | ||
| 191 | - public void onPlayerError(ExoPlaybackException error) { | ||
| 192 | - | ||
| 193 | - } | ||
| 194 | - | ||
| 195 | - @Override | ||
| 196 | - public void onPositionDiscontinuity(int reason) { | ||
| 197 | - | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - @Override | ||
| 201 | - public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { | ||
| 202 | - | ||
| 203 | - } | ||
| 204 | - | ||
| 205 | - @Override | ||
| 206 | - public void onSeekProcessed() { | ||
| 207 | - | ||
| 208 | - } | ||
| 209 | } | 188 | } |
app/strike/src/main/res/layout/activity_cnvideo.xml
| @@ -6,9 +6,9 @@ | @@ -6,9 +6,9 @@ | ||
| 6 | android:layout_height="match_parent" | 6 | android:layout_height="match_parent" |
| 7 | android:orientation="vertical"> | 7 | android:orientation="vertical"> |
| 8 | 8 | ||
| 9 | - <com.libs.video.video.VideoView | 9 | + <com.libs.video.video.test.VideoView |
| 10 | android:id="@+id/videoview" | 10 | android:id="@+id/videoview" |
| 11 | android:layout_width="match_parent" | 11 | android:layout_width="match_parent" |
| 12 | - android:layout_height="300dp"></com.libs.video.video.VideoView> | 12 | + android:layout_height="300dp"></com.libs.video.video.test.VideoView> |
| 13 | </LinearLayout> | 13 | </LinearLayout> |
| 14 | </layout> | 14 | </layout> |
| 15 | \ No newline at end of file | 15 | \ No newline at end of file |
app/strike/src/main/res/layout/activity_explayer.xml
| @@ -7,14 +7,18 @@ | @@ -7,14 +7,18 @@ | ||
| 7 | android:background="@color/black" | 7 | android:background="@color/black" |
| 8 | android:orientation="vertical"> | 8 | android:orientation="vertical"> |
| 9 | 9 | ||
| 10 | - <com.libs.video.video.exoplayer.CNLiveVideoView | 10 | + <!-- <com.libs.video.video.test.CNLiveVideoView--> |
| 11 | + <!-- android:id="@+id/playerView"--> | ||
| 12 | + <!-- android:layout_width="match_parent"--> | ||
| 13 | + <!-- android:layout_height="@dimen/dp_200" />--> | ||
| 14 | + | ||
| 15 | + <com.libs.video.video.exoplayer.play.CNVideoView | ||
| 11 | android:id="@+id/playerView" | 16 | android:id="@+id/playerView" |
| 12 | android:layout_width="match_parent" | 17 | android:layout_width="match_parent" |
| 13 | - android:layout_height="@dimen/dp_200" /> | ||
| 14 | - | ||
| 15 | -<!-- <com.google.android.exoplayer2.ui.PlayerView--> | ||
| 16 | -<!-- android:layout_width="match_parent"--> | ||
| 17 | -<!-- android:id="@+id/playerView"--> | ||
| 18 | -<!-- android:layout_height="match_parent" />--> | 18 | + android:layout_height="match_parent" /> |
| 19 | + <!-- <com.google.android.exoplayer2.ui.PlayerView--> | ||
| 20 | + <!-- android:layout_width="match_parent"--> | ||
| 21 | + <!-- android:id="@+id/playerView"--> | ||
| 22 | + <!-- android:layout_height="match_parent" />--> | ||
| 19 | </LinearLayout> | 23 | </LinearLayout> |
| 20 | </layout> | 24 | </layout> |
| 21 | \ No newline at end of file | 25 | \ No newline at end of file |
cloud/user/src/main/java/com/cnlive/cloud/user/frame/presenter/LoginDialogPresenter.java
| @@ -262,7 +262,7 @@ public class LoginDialogPresenter extends BasePresenter<LoginDialogView> { | @@ -262,7 +262,7 @@ public class LoginDialogPresenter extends BasePresenter<LoginDialogView> { | ||
| 262 | /** | 262 | /** |
| 263 | * 销毁 | 263 | * 销毁 |
| 264 | */ | 264 | */ |
| 265 | - public void destroy() { | 265 | + public void onDestroy() { |
| 266 | if (timerUtil != null) { | 266 | if (timerUtil != null) { |
| 267 | timerUtil.cancel(); | 267 | timerUtil.cancel(); |
| 268 | } | 268 | } |
cloud/user/src/main/java/com/cnlive/cloud/user/frame/presenter/QuickLoginPresenter.java
| @@ -322,6 +322,9 @@ public class QuickLoginPresenter extends BasePresenter<QuickLoginView> { | @@ -322,6 +322,9 @@ public class QuickLoginPresenter extends BasePresenter<QuickLoginView> { | ||
| 322 | // }); | 322 | // }); |
| 323 | // } | 323 | // } |
| 324 | public void cancelLoad() { | 324 | public void cancelLoad() { |
| 325 | + if (timerUtil != null) { | ||
| 326 | + timerUtil.cancel(); | ||
| 327 | + } | ||
| 325 | if (event != null) { | 328 | if (event != null) { |
| 326 | if (!event.isDisposed()) { | 329 | if (!event.isDisposed()) { |
| 327 | event.dispose(); | 330 | event.dispose(); |
| @@ -336,4 +339,5 @@ public class QuickLoginPresenter extends BasePresenter<QuickLoginView> { | @@ -336,4 +339,5 @@ public class QuickLoginPresenter extends BasePresenter<QuickLoginView> { | ||
| 336 | otherEvent = null; | 339 | otherEvent = null; |
| 337 | } | 340 | } |
| 338 | } | 341 | } |
| 342 | + | ||
| 339 | } | 343 | } |
cloud/user/src/main/java/com/cnlive/cloud/user/ui/activity/LoginDialogActivity.java
| @@ -223,7 +223,7 @@ public class LoginDialogActivity extends BaseActivity<LoginDialogView, LoginDial | @@ -223,7 +223,7 @@ public class LoginDialogActivity extends BaseActivity<LoginDialogView, LoginDial | ||
| 223 | public void onDestroy() { | 223 | public void onDestroy() { |
| 224 | super.onDestroy(); | 224 | super.onDestroy(); |
| 225 | if (getMvpView() != null) getMvpView().hideProgress(); | 225 | if (getMvpView() != null) getMvpView().hideProgress(); |
| 226 | - if (getPresenter() != null) getPresenter().destroy(); | 226 | + if (getPresenter() != null) getPresenter().onDestroy(); |
| 227 | 227 | ||
| 228 | WechatLoginObservable.getInstance().deleteObserver(this); | 228 | WechatLoginObservable.getInstance().deleteObserver(this); |
| 229 | ThirdLoginObservable.getInstance().deleteObserver(this); | 229 | ThirdLoginObservable.getInstance().deleteObserver(this); |
cloud/user/src/main/java/com/cnlive/cloud/user/ui/fragment/QuickLoginFragment.java
| @@ -11,7 +11,6 @@ import androidx.databinding.ViewDataBinding; | @@ -11,7 +11,6 @@ import androidx.databinding.ViewDataBinding; | ||
| 11 | 11 | ||
| 12 | import com.cnlive.cloud.user.databinding.FragmentQuickLoginBinding; | 12 | import com.cnlive.cloud.user.databinding.FragmentQuickLoginBinding; |
| 13 | import com.cnlive.cloud.user.frame.presenter.QuickLoginPresenter; | 13 | import com.cnlive.cloud.user.frame.presenter.QuickLoginPresenter; |
| 14 | -import com.cnlive.cloud.user.ui.activity.LoginDialogActivity; | ||
| 15 | import com.cnlive.core.libs.base.application.AppConfig; | 14 | import com.cnlive.core.libs.base.application.AppConfig; |
| 16 | import com.cnlive.core.libs.base.frame.fragment.BaseFragment; | 15 | import com.cnlive.core.libs.base.frame.fragment.BaseFragment; |
| 17 | import com.cnlive.core.libs.base.util.DoublePressed; | 16 | import com.cnlive.core.libs.base.util.DoublePressed; |
| @@ -28,7 +27,6 @@ import com.cnlive.login.model.ThirdLoginInfo; | @@ -28,7 +27,6 @@ import com.cnlive.login.model.ThirdLoginInfo; | ||
| 28 | import com.cnlive.login.observer.ThirdLoginObservable; | 27 | import com.cnlive.login.observer.ThirdLoginObservable; |
| 29 | import com.cnlive.login.observer.WechatLoginObservable; | 28 | import com.cnlive.login.observer.WechatLoginObservable; |
| 30 | import com.cnlive.login.utils.ThirdLoginUtil; | 29 | import com.cnlive.login.utils.ThirdLoginUtil; |
| 31 | -import com.cnlive.share.BuildConfig; | ||
| 32 | import com.cnlive.share.data.WeChatInfo; | 30 | import com.cnlive.share.data.WeChatInfo; |
| 33 | import com.cnlive.share.util.Util; | 31 | import com.cnlive.share.util.Util; |
| 34 | 32 | ||
| @@ -238,7 +236,7 @@ public class QuickLoginFragment extends BaseFragment<QuickLoginView, QuickLoginP | @@ -238,7 +236,7 @@ public class QuickLoginFragment extends BaseFragment<QuickLoginView, QuickLoginP | ||
| 238 | public void onDestroy() { | 236 | public void onDestroy() { |
| 239 | super.onDestroy(); | 237 | super.onDestroy(); |
| 240 | if (getMvpView() != null) getMvpView().hideProgress(); | 238 | if (getMvpView() != null) getMvpView().hideProgress(); |
| 241 | - if (getPresenter() != null) getPresenter().destroy(); | 239 | + if (getPresenter() != null) getPresenter().cancelLoad(); |
| 242 | WechatLoginObservable.getInstance().deleteObserver(this); | 240 | WechatLoginObservable.getInstance().deleteObserver(this); |
| 243 | ThirdLoginObservable.getInstance().deleteObserver(this); | 241 | ThirdLoginObservable.getInstance().deleteObserver(this); |
| 244 | } | 242 | } |
cloud/video/src/main/AndroidManifest.xml
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 2 | package="com.cnlive.cloud.video"> | 2 | package="com.cnlive.cloud.video"> |
| 3 | + | ||
| 4 | + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||
| 3 | <application> | 5 | <application> |
| 4 | </application> | 6 | </application> |
| 5 | 7 |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/DefaultAdControlView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.util.AttributeSet; | ||
| 6 | +import android.view.LayoutInflater; | ||
| 7 | +import android.view.View; | ||
| 8 | +import android.view.animation.Animation; | ||
| 9 | +import android.widget.FrameLayout; | ||
| 10 | +import android.widget.ImageView; | ||
| 11 | +import android.widget.TextView; | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +import com.cnlive.cloud.video.R; | ||
| 15 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 16 | +import com.libs.video.video.exoplayer.controller.IControlComponent; | ||
| 17 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 18 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 19 | + | ||
| 20 | +import androidx.annotation.NonNull; | ||
| 21 | +import androidx.annotation.Nullable; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 广告播放视图 | ||
| 25 | + */ | ||
| 26 | +public class DefaultAdControlView extends FrameLayout implements IControlComponent, View.OnClickListener { | ||
| 27 | + | ||
| 28 | + protected TextView mAdTime, mAdDetail; | ||
| 29 | + protected ImageView mBack, mVolume, mFullScreen, mPlayButton; | ||
| 30 | + protected AdControlListener mListener; | ||
| 31 | + | ||
| 32 | + private ControlWrapper mControlWrapper;//控制API | ||
| 33 | + | ||
| 34 | + public DefaultAdControlView(@NonNull Context context) { | ||
| 35 | + super(context); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public DefaultAdControlView(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 39 | + super(context, attrs); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public DefaultAdControlView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 43 | + super(context, attrs, defStyleAttr); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + { | ||
| 47 | + LayoutInflater.from(getContext()).inflate(R.layout.layout_ad_control_view, this, true); | ||
| 48 | + mAdTime = findViewById(R.id.ad_time); | ||
| 49 | + mAdDetail = findViewById(R.id.ad_detail); | ||
| 50 | + mAdDetail.setText("了解详情>"); | ||
| 51 | + mBack = findViewById(R.id.back); | ||
| 52 | + mBack.setVisibility(GONE); | ||
| 53 | + mVolume = findViewById(R.id.iv_volume); | ||
| 54 | + mFullScreen = findViewById(R.id.fullscreen); | ||
| 55 | + mPlayButton = findViewById(R.id.iv_play); | ||
| 56 | + mPlayButton.setOnClickListener(this); | ||
| 57 | + mAdTime.setOnClickListener(this); | ||
| 58 | + mAdDetail.setOnClickListener(this); | ||
| 59 | + mBack.setOnClickListener(this); | ||
| 60 | + mVolume.setOnClickListener(this); | ||
| 61 | + mFullScreen.setOnClickListener(this); | ||
| 62 | + this.setOnClickListener(new OnClickListener() { | ||
| 63 | + @Override | ||
| 64 | + public void onClick(View v) { | ||
| 65 | + if (mListener != null) mListener.onAdClick(); | ||
| 66 | + } | ||
| 67 | + }); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 71 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 72 | + mControlWrapper = controlWrapper; | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + @Override | ||
| 76 | + public View getView() { | ||
| 77 | + return this; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @Override | ||
| 81 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 82 | + | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + @Override | ||
| 86 | + public void onPlayStateChanged(int playState) { | ||
| 87 | + switch (playState) { | ||
| 88 | + case CNVideoView.STATE_PLAYING: | ||
| 89 | + mControlWrapper.startProgress(); | ||
| 90 | + mPlayButton.setSelected(true); | ||
| 91 | + break; | ||
| 92 | + case CNVideoView.STATE_PAUSED: | ||
| 93 | + mPlayButton.setSelected(false); | ||
| 94 | + break; | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + @Override | ||
| 99 | + public void onPlayerStateChanged(int playerState) { | ||
| 100 | + switch (playerState) { | ||
| 101 | + case CNVideoView.PLAYER_NORMAL: | ||
| 102 | + mBack.setVisibility(GONE); | ||
| 103 | + mFullScreen.setSelected(false); | ||
| 104 | + break; | ||
| 105 | + case CNVideoView.PLAYER_FULL_SCREEN: | ||
| 106 | + mBack.setVisibility(VISIBLE); | ||
| 107 | + mFullScreen.setSelected(true); | ||
| 108 | + break; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + //暂未实现全面屏适配逻辑,需要你自己补全 | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + @Override | ||
| 115 | + public void setProgress(int duration, int position) { | ||
| 116 | + if (mAdTime != null) | ||
| 117 | + mAdTime.setText(String.format("%s | 跳过", (duration - position) / 1000)); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + @Override | ||
| 121 | + public void onLockStateChanged(boolean isLocked) { | ||
| 122 | + | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + @Override | ||
| 126 | + public void onClick(View v) { | ||
| 127 | + int id = v.getId(); | ||
| 128 | + if (id == R.id.back | id == R.id.fullscreen) { | ||
| 129 | + toggleFullScreen(); | ||
| 130 | + } else if (id == R.id.iv_volume) { | ||
| 131 | + doMute(); | ||
| 132 | + } else if (id == R.id.ad_detail) { | ||
| 133 | + if (mListener != null) mListener.onAdClick(); | ||
| 134 | + } else if (id == R.id.ad_time) { | ||
| 135 | + if (mListener != null) mListener.onSkipAd(); | ||
| 136 | + } else if (id == R.id.iv_play) { | ||
| 137 | + mControlWrapper.togglePlay(); | ||
| 138 | + } | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + private void doMute() { | ||
| 142 | + mControlWrapper.setMute(!mControlWrapper.isMute()); | ||
| 143 | + mVolume.setImageResource(mControlWrapper.isMute() ? R.drawable.cnvideo_ic_action_volume_up : R.drawable.cnvideo_ic_action_volume_off); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + /** | ||
| 147 | + * 横竖屏切换 | ||
| 148 | + */ | ||
| 149 | + private void toggleFullScreen() { | ||
| 150 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 151 | + mControlWrapper.toggleFullScreen(activity); | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + public void setListener(AdControlListener listener) { | ||
| 155 | + this.mListener = listener; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + public interface AdControlListener { | ||
| 159 | + | ||
| 160 | + void onAdClick(); | ||
| 161 | + | ||
| 162 | + void onSkipAd(); | ||
| 163 | + } | ||
| 164 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/DefaultCompleteView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.content.pm.ActivityInfo; | ||
| 6 | +import android.util.AttributeSet; | ||
| 7 | +import android.view.LayoutInflater; | ||
| 8 | +import android.view.View; | ||
| 9 | +import android.view.animation.Animation; | ||
| 10 | +import android.widget.FrameLayout; | ||
| 11 | +import android.widget.ImageView; | ||
| 12 | + | ||
| 13 | +import com.cnlive.cloud.video.R; | ||
| 14 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 15 | +import com.libs.video.video.exoplayer.controller.IControlComponent; | ||
| 16 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 17 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 18 | + | ||
| 19 | +import androidx.annotation.NonNull; | ||
| 20 | +import androidx.annotation.Nullable; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * 自动播放完成界面 | ||
| 24 | + */ | ||
| 25 | +public class DefaultCompleteView extends FrameLayout implements IControlComponent { | ||
| 26 | + | ||
| 27 | + private ControlWrapper mControlWrapper; | ||
| 28 | + | ||
| 29 | + private ImageView mStopFullscreen; | ||
| 30 | + | ||
| 31 | + public DefaultCompleteView(@NonNull Context context) { | ||
| 32 | + super(context); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public DefaultCompleteView(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 36 | + super(context, attrs); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public DefaultCompleteView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 40 | + super(context, attrs, defStyleAttr); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + { | ||
| 44 | + setVisibility(GONE); | ||
| 45 | + LayoutInflater.from(getContext()).inflate(R.layout.cnvideo_layout_complete_view, this, true); | ||
| 46 | + findViewById(R.id.iv_replay).setOnClickListener(new OnClickListener() { | ||
| 47 | + @Override | ||
| 48 | + public void onClick(View v) { | ||
| 49 | + mControlWrapper.replay(true); | ||
| 50 | + } | ||
| 51 | + }); | ||
| 52 | + mStopFullscreen = findViewById(R.id.stop_fullscreen); | ||
| 53 | + mStopFullscreen.setOnClickListener(new OnClickListener() { | ||
| 54 | + @Override | ||
| 55 | + public void onClick(View v) { | ||
| 56 | + if (mControlWrapper.isFullScreen()) { | ||
| 57 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 58 | + if (activity != null && !activity.isFinishing()) { | ||
| 59 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | ||
| 60 | + mControlWrapper.stopFullScreen(); | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + }); | ||
| 65 | + setClickable(true); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Override | ||
| 69 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 70 | + mControlWrapper = controlWrapper; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @Override | ||
| 74 | + public View getView() { | ||
| 75 | + return this; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @Override | ||
| 79 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 80 | + | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Override | ||
| 84 | + public void onPlayStateChanged(int playState) { | ||
| 85 | + if (playState == CNVideoView.STATE_PLAYBACK_COMPLETED) { | ||
| 86 | + setVisibility(VISIBLE); | ||
| 87 | + mStopFullscreen.setVisibility(mControlWrapper.isFullScreen() ? VISIBLE : GONE); | ||
| 88 | + bringToFront(); | ||
| 89 | + } else { | ||
| 90 | + setVisibility(GONE); | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + @Override | ||
| 95 | + public void onPlayerStateChanged(int playerState) { | ||
| 96 | + if (playerState == CNVideoView.PLAYER_FULL_SCREEN) { | ||
| 97 | + mStopFullscreen.setVisibility(VISIBLE); | ||
| 98 | + } else if (playerState == CNVideoView.PLAYER_NORMAL) { | ||
| 99 | + mStopFullscreen.setVisibility(GONE); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 103 | + if (activity != null && mControlWrapper.hasCutout()) { | ||
| 104 | + int orientation = activity.getRequestedOrientation(); | ||
| 105 | + int cutoutHeight = mControlWrapper.getCutoutHeight(); | ||
| 106 | + if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { | ||
| 107 | + LayoutParams sflp = (LayoutParams) mStopFullscreen.getLayoutParams(); | ||
| 108 | + sflp.setMargins(0, 0, 0, 0); | ||
| 109 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { | ||
| 110 | + LayoutParams sflp = (LayoutParams) mStopFullscreen.getLayoutParams(); | ||
| 111 | + sflp.setMargins(cutoutHeight, 0, 0, 0); | ||
| 112 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) { | ||
| 113 | + LayoutParams sflp = (LayoutParams) mStopFullscreen.getLayoutParams(); | ||
| 114 | + sflp.setMargins(0, 0, 0, 0); | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + @Override | ||
| 120 | + public void setProgress(int duration, int position) { | ||
| 121 | + | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + @Override | ||
| 125 | + public void onLockStateChanged(boolean isLock) { | ||
| 126 | + | ||
| 127 | + } | ||
| 128 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/DefaultErrorView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.util.AttributeSet; | ||
| 5 | +import android.view.LayoutInflater; | ||
| 6 | +import android.view.MotionEvent; | ||
| 7 | +import android.view.View; | ||
| 8 | +import android.view.ViewConfiguration; | ||
| 9 | +import android.view.animation.Animation; | ||
| 10 | +import android.widget.LinearLayout; | ||
| 11 | + | ||
| 12 | +import com.cnlive.cloud.video.R; | ||
| 13 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 14 | +import com.libs.video.video.exoplayer.controller.IControlComponent; | ||
| 15 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 16 | + | ||
| 17 | +import androidx.annotation.NonNull; | ||
| 18 | +import androidx.annotation.Nullable; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * 播放出错提示界面 | ||
| 22 | + * Created by dueeeke on 2017/4/13. | ||
| 23 | + */ | ||
| 24 | +public class DefaultErrorView extends LinearLayout implements IControlComponent { | ||
| 25 | + | ||
| 26 | + private float mDownX; | ||
| 27 | + private float mDownY; | ||
| 28 | + | ||
| 29 | + private ControlWrapper mControlWrapper; | ||
| 30 | + | ||
| 31 | + public DefaultErrorView(Context context) { | ||
| 32 | + this(context, null); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public DefaultErrorView(Context context, @Nullable AttributeSet attrs) { | ||
| 36 | + super(context, attrs); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public DefaultErrorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 40 | + super(context, attrs, defStyleAttr); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + { | ||
| 44 | + setVisibility(GONE); | ||
| 45 | + LayoutInflater.from(getContext()).inflate(R.layout.cnvideo_layout_error_view, this, true); | ||
| 46 | + findViewById(R.id.status_btn).setOnClickListener(new OnClickListener() { | ||
| 47 | + @Override | ||
| 48 | + public void onClick(View v) { | ||
| 49 | + setVisibility(GONE); | ||
| 50 | + mControlWrapper.replay(false); | ||
| 51 | + } | ||
| 52 | + }); | ||
| 53 | + setClickable(true); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Override | ||
| 57 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 58 | + mControlWrapper = controlWrapper; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + @Override | ||
| 62 | + public View getView() { | ||
| 63 | + return this; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + @Override | ||
| 67 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 68 | + | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + @Override | ||
| 72 | + public void onPlayStateChanged(int playState) { | ||
| 73 | + if (playState == CNVideoView.STATE_ERROR) { | ||
| 74 | + bringToFront(); | ||
| 75 | + setVisibility(VISIBLE); | ||
| 76 | + } else if (playState == CNVideoView.STATE_IDLE) { | ||
| 77 | + setVisibility(GONE); | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + @Override | ||
| 82 | + public void onPlayerStateChanged(int playerState) { | ||
| 83 | + | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + @Override | ||
| 87 | + public void setProgress(int duration, int position) { | ||
| 88 | + | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + @Override | ||
| 92 | + public void onLockStateChanged(boolean isLock) { | ||
| 93 | + | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + @Override | ||
| 97 | + public boolean dispatchTouchEvent(MotionEvent ev) { | ||
| 98 | + switch (ev.getAction()) { | ||
| 99 | + case MotionEvent.ACTION_DOWN: | ||
| 100 | + mDownX = ev.getX(); | ||
| 101 | + mDownY = ev.getY(); | ||
| 102 | + // True if the child does not want the parent to intercept touch events. | ||
| 103 | + getParent().requestDisallowInterceptTouchEvent(true); | ||
| 104 | + break; | ||
| 105 | + case MotionEvent.ACTION_MOVE: | ||
| 106 | + float absDeltaX = Math.abs(ev.getX() - mDownX); | ||
| 107 | + float absDeltaY = Math.abs(ev.getY() - mDownY); | ||
| 108 | + if (absDeltaX > ViewConfiguration.get(getContext()).getScaledTouchSlop() || | ||
| 109 | + absDeltaY > ViewConfiguration.get(getContext()).getScaledTouchSlop()) { | ||
| 110 | + getParent().requestDisallowInterceptTouchEvent(false); | ||
| 111 | + } | ||
| 112 | + case MotionEvent.ACTION_UP: | ||
| 113 | + break; | ||
| 114 | + } | ||
| 115 | + return super.dispatchTouchEvent(ev); | ||
| 116 | + } | ||
| 117 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/DefaultPrepareView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.util.AttributeSet; | ||
| 5 | +import android.view.LayoutInflater; | ||
| 6 | +import android.view.View; | ||
| 7 | +import android.view.animation.Animation; | ||
| 8 | +import android.widget.FrameLayout; | ||
| 9 | +import android.widget.ImageView; | ||
| 10 | +import android.widget.ProgressBar; | ||
| 11 | + | ||
| 12 | +import com.cnlive.cloud.video.R; | ||
| 13 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 14 | +import com.libs.video.video.exoplayer.controller.IControlComponent; | ||
| 15 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 16 | +import com.libs.video.video.exoplayer.play.VideoViewManager; | ||
| 17 | + | ||
| 18 | +import androidx.annotation.NonNull; | ||
| 19 | +import androidx.annotation.Nullable; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * 准备播放界面 | ||
| 23 | + */ | ||
| 24 | +public class DefaultPrepareView extends FrameLayout implements IControlComponent { | ||
| 25 | + | ||
| 26 | + private ControlWrapper mControlWrapper; | ||
| 27 | + | ||
| 28 | + private ImageView mThumb; | ||
| 29 | + private ImageView mStartPlay; | ||
| 30 | + private ProgressBar mLoading; | ||
| 31 | + private FrameLayout mNetWarning; | ||
| 32 | + | ||
| 33 | + public DefaultPrepareView(@NonNull Context context) { | ||
| 34 | + super(context); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public DefaultPrepareView(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 38 | + super(context, attrs); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public DefaultPrepareView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 42 | + super(context, attrs, defStyleAttr); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + { | ||
| 46 | + LayoutInflater.from(getContext()).inflate(R.layout.cnvideo_layout_prepare_view, this, true); | ||
| 47 | + mThumb = findViewById(R.id.thumb); | ||
| 48 | + mStartPlay = findViewById(R.id.start_play); | ||
| 49 | + mLoading = findViewById(R.id.loading); | ||
| 50 | + mNetWarning = findViewById(R.id.net_warning_layout); | ||
| 51 | + findViewById(R.id.status_btn).setOnClickListener(new OnClickListener() { | ||
| 52 | + @Override | ||
| 53 | + public void onClick(View v) { | ||
| 54 | + mNetWarning.setVisibility(GONE); | ||
| 55 | + VideoViewManager.instance().setPlayOnMobileNetwork(true); | ||
| 56 | + mControlWrapper.start(); | ||
| 57 | + } | ||
| 58 | + }); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 设置点击此界面开始播放 | ||
| 63 | + */ | ||
| 64 | + public void setClickStart() { | ||
| 65 | + setOnClickListener(new OnClickListener() { | ||
| 66 | + @Override | ||
| 67 | + public void onClick(View v) { | ||
| 68 | + mControlWrapper.start(); | ||
| 69 | + } | ||
| 70 | + }); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @Override | ||
| 74 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 75 | + mControlWrapper = controlWrapper; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @Override | ||
| 79 | + public View getView() { | ||
| 80 | + return this; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Override | ||
| 84 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 85 | + | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + public void onPlayStateChanged(int playState) { | ||
| 90 | + switch (playState) { | ||
| 91 | + case CNVideoView.STATE_PREPARING: | ||
| 92 | + bringToFront(); | ||
| 93 | + setVisibility(VISIBLE); | ||
| 94 | + mStartPlay.setVisibility(View.GONE); | ||
| 95 | + mNetWarning.setVisibility(GONE); | ||
| 96 | + mLoading.setVisibility(View.VISIBLE); | ||
| 97 | + break; | ||
| 98 | + case CNVideoView.STATE_PLAYING: | ||
| 99 | + case CNVideoView.STATE_PAUSED: | ||
| 100 | + case CNVideoView.STATE_ERROR: | ||
| 101 | + case CNVideoView.STATE_BUFFERING: | ||
| 102 | + case CNVideoView.STATE_BUFFERED: | ||
| 103 | + case CNVideoView.STATE_PLAYBACK_COMPLETED: | ||
| 104 | + setVisibility(GONE); | ||
| 105 | + break; | ||
| 106 | + case CNVideoView.STATE_IDLE: | ||
| 107 | + setVisibility(VISIBLE); | ||
| 108 | + bringToFront(); | ||
| 109 | + mLoading.setVisibility(View.GONE); | ||
| 110 | + mNetWarning.setVisibility(GONE); | ||
| 111 | + mStartPlay.setVisibility(View.VISIBLE); | ||
| 112 | + mThumb.setVisibility(View.VISIBLE); | ||
| 113 | + break; | ||
| 114 | + case CNVideoView.STATE_START_ABORT: | ||
| 115 | + setVisibility(VISIBLE); | ||
| 116 | + mNetWarning.setVisibility(VISIBLE); | ||
| 117 | + mNetWarning.bringToFront(); | ||
| 118 | + break; | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + @Override | ||
| 123 | + public void onPlayerStateChanged(int playerState) { | ||
| 124 | + | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + @Override | ||
| 128 | + public void setProgress(int duration, int position) { | ||
| 129 | + | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + @Override | ||
| 133 | + public void onLockStateChanged(boolean isLocked) { | ||
| 134 | + | ||
| 135 | + } | ||
| 136 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/DefaultTitleView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.BroadcastReceiver; | ||
| 5 | +import android.content.Context; | ||
| 6 | +import android.content.Intent; | ||
| 7 | +import android.content.IntentFilter; | ||
| 8 | +import android.content.pm.ActivityInfo; | ||
| 9 | +import android.os.Bundle; | ||
| 10 | +import android.util.AttributeSet; | ||
| 11 | +import android.view.LayoutInflater; | ||
| 12 | +import android.view.View; | ||
| 13 | +import android.view.animation.Animation; | ||
| 14 | +import android.widget.FrameLayout; | ||
| 15 | +import android.widget.ImageView; | ||
| 16 | +import android.widget.LinearLayout; | ||
| 17 | +import android.widget.TextView; | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +import com.cnlive.cloud.video.R; | ||
| 21 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 22 | +import com.libs.video.video.exoplayer.controller.IControlComponent; | ||
| 23 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 24 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 25 | + | ||
| 26 | +import androidx.annotation.NonNull; | ||
| 27 | +import androidx.annotation.Nullable; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * 播放器顶部标题栏 | ||
| 31 | + */ | ||
| 32 | +public class DefaultTitleView extends FrameLayout implements IControlComponent { | ||
| 33 | + | ||
| 34 | + private ControlWrapper mControlWrapper; | ||
| 35 | + | ||
| 36 | + private LinearLayout mTitleContainer; | ||
| 37 | + private MarqueeTextView mTitle; | ||
| 38 | + private TextView mSysTime;//系统当前时间 | ||
| 39 | + | ||
| 40 | + private BatteryReceiver mBatteryReceiver; | ||
| 41 | + private boolean mIsRegister;//是否注册BatteryReceiver | ||
| 42 | + | ||
| 43 | + public DefaultTitleView(@NonNull Context context) { | ||
| 44 | + super(context); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public DefaultTitleView(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 48 | + super(context, attrs); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public DefaultTitleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 52 | + super(context, attrs, defStyleAttr); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + { | ||
| 56 | + setVisibility(GONE); | ||
| 57 | + LayoutInflater.from(getContext()).inflate(R.layout.cnvideo_layout_title_view, this, true); | ||
| 58 | + mTitleContainer = findViewById(R.id.title_container); | ||
| 59 | + ImageView back = findViewById(R.id.back); | ||
| 60 | + back.setOnClickListener(new OnClickListener() { | ||
| 61 | + @Override | ||
| 62 | + public void onClick(View v) { | ||
| 63 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 64 | + if (activity != null && mControlWrapper.isFullScreen()) { | ||
| 65 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | ||
| 66 | + mControlWrapper.stopFullScreen(); | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + }); | ||
| 70 | + mTitle = findViewById(R.id.title); | ||
| 71 | + mSysTime = findViewById(R.id.sys_time); | ||
| 72 | + //电量 | ||
| 73 | + ImageView batteryLevel = findViewById(R.id.iv_battery); | ||
| 74 | + mBatteryReceiver = new BatteryReceiver(batteryLevel); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + public void setTitle(String title) { | ||
| 78 | + mTitle.setText(title); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + @Override | ||
| 82 | + protected void onDetachedFromWindow() { | ||
| 83 | + super.onDetachedFromWindow(); | ||
| 84 | + if (mIsRegister) { | ||
| 85 | + getContext().unregisterReceiver(mBatteryReceiver); | ||
| 86 | + mIsRegister = false; | ||
| 87 | + } | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + @Override | ||
| 91 | + protected void onAttachedToWindow() { | ||
| 92 | + super.onAttachedToWindow(); | ||
| 93 | + if (!mIsRegister) { | ||
| 94 | + getContext().registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); | ||
| 95 | + mIsRegister = true; | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + @Override | ||
| 100 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 101 | + mControlWrapper = controlWrapper; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + @Override | ||
| 105 | + public View getView() { | ||
| 106 | + return this; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Override | ||
| 110 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 111 | + //只在全屏时才有效 | ||
| 112 | + if (!mControlWrapper.isFullScreen()) return; | ||
| 113 | + if (isVisible) { | ||
| 114 | + if (getVisibility() == GONE) { | ||
| 115 | + mSysTime.setText(PlayerUtils.getCurrentSystemTime()); | ||
| 116 | + setVisibility(VISIBLE); | ||
| 117 | + if (anim != null) { | ||
| 118 | + startAnimation(anim); | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + } else { | ||
| 122 | + if (getVisibility() == VISIBLE) { | ||
| 123 | + if (anim != null) { | ||
| 124 | + setVisibility(GONE); | ||
| 125 | + startAnimation(anim); | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + @Override | ||
| 132 | + public void onPlayStateChanged(int playState) { | ||
| 133 | + switch (playState) { | ||
| 134 | + case CNVideoView.STATE_IDLE: | ||
| 135 | + case CNVideoView.STATE_START_ABORT: | ||
| 136 | + case CNVideoView.STATE_PREPARING: | ||
| 137 | + case CNVideoView.STATE_PREPARED: | ||
| 138 | + case CNVideoView.STATE_ERROR: | ||
| 139 | + case CNVideoView.STATE_PLAYBACK_COMPLETED: | ||
| 140 | + setVisibility(GONE); | ||
| 141 | + break; | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + @Override | ||
| 146 | + public void onPlayerStateChanged(int playerState) { | ||
| 147 | + if (playerState == CNVideoView.PLAYER_FULL_SCREEN) { | ||
| 148 | + if (mControlWrapper.isShowing() && !mControlWrapper.isLocked()) { | ||
| 149 | + setVisibility(VISIBLE); | ||
| 150 | + mSysTime.setText(PlayerUtils.getCurrentSystemTime()); | ||
| 151 | + } | ||
| 152 | + mTitle.setNeedFocus(true); | ||
| 153 | + } else { | ||
| 154 | + setVisibility(GONE); | ||
| 155 | + mTitle.setNeedFocus(false); | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 159 | + if (activity != null && mControlWrapper.hasCutout()) { | ||
| 160 | + int orientation = activity.getRequestedOrientation(); | ||
| 161 | + int cutoutHeight = mControlWrapper.getCutoutHeight(); | ||
| 162 | + if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { | ||
| 163 | + mTitleContainer.setPadding(0, 0, 0, 0); | ||
| 164 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { | ||
| 165 | + mTitleContainer.setPadding(cutoutHeight, 0, 0, 0); | ||
| 166 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) { | ||
| 167 | + mTitleContainer.setPadding(0, 0, cutoutHeight, 0); | ||
| 168 | + } | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + @Override | ||
| 173 | + public void setProgress(int duration, int position) { | ||
| 174 | + | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + @Override | ||
| 178 | + public void onLockStateChanged(boolean isLocked) { | ||
| 179 | + if (isLocked) { | ||
| 180 | + setVisibility(GONE); | ||
| 181 | + } else { | ||
| 182 | + setVisibility(VISIBLE); | ||
| 183 | + mSysTime.setText(PlayerUtils.getCurrentSystemTime()); | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + private static class BatteryReceiver extends BroadcastReceiver { | ||
| 188 | + private ImageView pow; | ||
| 189 | + | ||
| 190 | + public BatteryReceiver(ImageView pow) { | ||
| 191 | + this.pow = pow; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + @Override | ||
| 195 | + public void onReceive(Context context, Intent intent) { | ||
| 196 | + Bundle extras = intent.getExtras(); | ||
| 197 | + if (extras == null) return; | ||
| 198 | + int current = extras.getInt("level");// 获得当前电量 | ||
| 199 | + int total = extras.getInt("scale");// 获得总电量 | ||
| 200 | + int percent = current * 100 / total; | ||
| 201 | + pow.getDrawable().setLevel(percent); | ||
| 202 | + } | ||
| 203 | + } | ||
| 204 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/DefaultVodControlView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.content.pm.ActivityInfo; | ||
| 6 | +import android.os.Build; | ||
| 7 | +import android.util.AttributeSet; | ||
| 8 | +import android.view.LayoutInflater; | ||
| 9 | +import android.view.View; | ||
| 10 | +import android.view.ViewGroup; | ||
| 11 | +import android.view.animation.AlphaAnimation; | ||
| 12 | +import android.view.animation.Animation; | ||
| 13 | +import android.widget.FrameLayout; | ||
| 14 | +import android.widget.ImageView; | ||
| 15 | +import android.widget.LinearLayout; | ||
| 16 | +import android.widget.ProgressBar; | ||
| 17 | +import android.widget.SeekBar; | ||
| 18 | +import android.widget.TextView; | ||
| 19 | + | ||
| 20 | +import com.cnlive.cloud.video.R; | ||
| 21 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 22 | +import com.libs.video.video.exoplayer.controller.IControlComponent; | ||
| 23 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 24 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 25 | + | ||
| 26 | +import androidx.annotation.NonNull; | ||
| 27 | +import androidx.annotation.Nullable; | ||
| 28 | + | ||
| 29 | +import static com.libs.video.video.exoplayer.util.PlayerUtils.stringForTime; | ||
| 30 | + | ||
| 31 | + | ||
| 32 | +/** | ||
| 33 | + * 点播底部控制栏 | ||
| 34 | + */ | ||
| 35 | +public class DefaultVodControlView extends FrameLayout implements IControlComponent, View.OnClickListener, SeekBar.OnSeekBarChangeListener { | ||
| 36 | + | ||
| 37 | + protected ControlWrapper mControlWrapper; | ||
| 38 | + | ||
| 39 | + private TextView mTotalTime, mCurrTime; | ||
| 40 | + private ImageView mFullScreen; | ||
| 41 | + private LinearLayout mBottomContainer; | ||
| 42 | + private SeekBar mVideoProgress; | ||
| 43 | + private ProgressBar mBottomProgress; | ||
| 44 | + private ImageView mPlayButton; | ||
| 45 | + | ||
| 46 | + private boolean mIsDragging; | ||
| 47 | + | ||
| 48 | + private boolean mIsShowBottomProgress = true; | ||
| 49 | + | ||
| 50 | + public DefaultVodControlView(@NonNull Context context) { | ||
| 51 | + super(context); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + public DefaultVodControlView(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 55 | + super(context, attrs); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + public DefaultVodControlView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 59 | + super(context, attrs, defStyleAttr); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + | ||
| 63 | + { | ||
| 64 | + setVisibility(GONE); | ||
| 65 | + LayoutInflater.from(getContext()).inflate(getLayoutId(), this, true); | ||
| 66 | + mFullScreen = findViewById(R.id.fullscreen); | ||
| 67 | + mFullScreen.setOnClickListener(this); | ||
| 68 | + mBottomContainer = findViewById(R.id.bottom_container); | ||
| 69 | + mVideoProgress = findViewById(R.id.seekBar); | ||
| 70 | + mVideoProgress.setOnSeekBarChangeListener(this); | ||
| 71 | + mTotalTime = findViewById(R.id.total_time); | ||
| 72 | + mCurrTime = findViewById(R.id.curr_time); | ||
| 73 | + mPlayButton = findViewById(R.id.iv_play); | ||
| 74 | + mPlayButton.setOnClickListener(this); | ||
| 75 | + mBottomProgress = findViewById(R.id.bottom_progress); | ||
| 76 | + | ||
| 77 | + //5.1以下系统SeekBar高度需要设置成WRAP_CONTENT | ||
| 78 | + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) { | ||
| 79 | + mVideoProgress.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + protected int getLayoutId() { | ||
| 84 | + return R.layout.cnvideo_layout_vod_control_view; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * 是否显示底部进度条,默认显示 | ||
| 89 | + */ | ||
| 90 | + public void showBottomProgress(boolean isShow) { | ||
| 91 | + mIsShowBottomProgress = isShow; | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + @Override | ||
| 95 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 96 | + mControlWrapper = controlWrapper; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + @Override | ||
| 100 | + public View getView() { | ||
| 101 | + return this; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + @Override | ||
| 105 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 106 | + if (isVisible) { | ||
| 107 | + mBottomContainer.setVisibility(VISIBLE); | ||
| 108 | + if (anim != null) { | ||
| 109 | + mBottomContainer.startAnimation(anim); | ||
| 110 | + } | ||
| 111 | + if (mIsShowBottomProgress) { | ||
| 112 | + mBottomProgress.setVisibility(GONE); | ||
| 113 | + } | ||
| 114 | + } else { | ||
| 115 | + mBottomContainer.setVisibility(GONE); | ||
| 116 | + if (anim != null) { | ||
| 117 | + mBottomContainer.startAnimation(anim); | ||
| 118 | + } | ||
| 119 | + if (mIsShowBottomProgress) { | ||
| 120 | + mBottomProgress.setVisibility(VISIBLE); | ||
| 121 | + AlphaAnimation animation = new AlphaAnimation(0f, 1f); | ||
| 122 | + animation.setDuration(300); | ||
| 123 | + mBottomProgress.startAnimation(animation); | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Override | ||
| 129 | + public void onPlayStateChanged(int playState) { | ||
| 130 | + switch (playState) { | ||
| 131 | + case CNVideoView.STATE_IDLE: | ||
| 132 | + case CNVideoView.STATE_PLAYBACK_COMPLETED: | ||
| 133 | + setVisibility(GONE); | ||
| 134 | + mBottomProgress.setProgress(0); | ||
| 135 | + mBottomProgress.setSecondaryProgress(0); | ||
| 136 | + mVideoProgress.setProgress(0); | ||
| 137 | + mVideoProgress.setSecondaryProgress(0); | ||
| 138 | + break; | ||
| 139 | + case CNVideoView.STATE_START_ABORT: | ||
| 140 | + case CNVideoView.STATE_PREPARING: | ||
| 141 | + case CNVideoView.STATE_PREPARED: | ||
| 142 | + case CNVideoView.STATE_ERROR: | ||
| 143 | + setVisibility(GONE); | ||
| 144 | + break; | ||
| 145 | + case CNVideoView.STATE_PLAYING: | ||
| 146 | + mPlayButton.setSelected(true); | ||
| 147 | + if (mIsShowBottomProgress) { | ||
| 148 | + if (mControlWrapper.isShowing()) { | ||
| 149 | + mBottomProgress.setVisibility(GONE); | ||
| 150 | + mBottomContainer.setVisibility(VISIBLE); | ||
| 151 | + } else { | ||
| 152 | + mBottomContainer.setVisibility(GONE); | ||
| 153 | + mBottomProgress.setVisibility(VISIBLE); | ||
| 154 | + } | ||
| 155 | + } else { | ||
| 156 | + mBottomContainer.setVisibility(GONE); | ||
| 157 | + } | ||
| 158 | + setVisibility(VISIBLE); | ||
| 159 | + //开始刷新进度 | ||
| 160 | + mControlWrapper.startProgress(); | ||
| 161 | + break; | ||
| 162 | + case CNVideoView.STATE_PAUSED: | ||
| 163 | + mPlayButton.setSelected(false); | ||
| 164 | + break; | ||
| 165 | + case CNVideoView.STATE_BUFFERING: | ||
| 166 | + case CNVideoView.STATE_BUFFERED: | ||
| 167 | + mPlayButton.setSelected(mControlWrapper.isPlaying()); | ||
| 168 | + break; | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + @Override | ||
| 173 | + public void onPlayerStateChanged(int playerState) { | ||
| 174 | + switch (playerState) { | ||
| 175 | + case CNVideoView.PLAYER_NORMAL: | ||
| 176 | + mFullScreen.setSelected(false); | ||
| 177 | + break; | ||
| 178 | + case CNVideoView.PLAYER_FULL_SCREEN: | ||
| 179 | + mFullScreen.setSelected(true); | ||
| 180 | + break; | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 184 | + if (activity != null && mControlWrapper.hasCutout()) { | ||
| 185 | + int orientation = activity.getRequestedOrientation(); | ||
| 186 | + int cutoutHeight = mControlWrapper.getCutoutHeight(); | ||
| 187 | + if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { | ||
| 188 | + mBottomContainer.setPadding(0, 0, 0, 0); | ||
| 189 | + mBottomProgress.setPadding(0, 0, 0, 0); | ||
| 190 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { | ||
| 191 | + mBottomContainer.setPadding(cutoutHeight, 0, 0, 0); | ||
| 192 | + mBottomProgress.setPadding(cutoutHeight, 0, 0, 0); | ||
| 193 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) { | ||
| 194 | + mBottomContainer.setPadding(0, 0, cutoutHeight, 0); | ||
| 195 | + mBottomProgress.setPadding(0, 0, cutoutHeight, 0); | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + @Override | ||
| 201 | + public void setProgress(int duration, int position) { | ||
| 202 | + if (mIsDragging) { | ||
| 203 | + return; | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + if (mVideoProgress != null) { | ||
| 207 | + if (duration > 0) { | ||
| 208 | + mVideoProgress.setEnabled(true); | ||
| 209 | + int pos = (int) (position * 1.0 / duration * mVideoProgress.getMax()); | ||
| 210 | + mVideoProgress.setProgress(pos); | ||
| 211 | + mBottomProgress.setProgress(pos); | ||
| 212 | + } else { | ||
| 213 | + mVideoProgress.setEnabled(false); | ||
| 214 | + } | ||
| 215 | + int percent = mControlWrapper.getBufferedPercentage(); | ||
| 216 | + if (percent >= 95) { //解决缓冲进度不能100%问题 | ||
| 217 | + mVideoProgress.setSecondaryProgress(mVideoProgress.getMax()); | ||
| 218 | + mBottomProgress.setSecondaryProgress(mBottomProgress.getMax()); | ||
| 219 | + } else { | ||
| 220 | + mVideoProgress.setSecondaryProgress(percent * 10); | ||
| 221 | + mBottomProgress.setSecondaryProgress(percent * 10); | ||
| 222 | + } | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + if (mTotalTime != null) | ||
| 226 | + mTotalTime.setText(stringForTime(duration)); | ||
| 227 | + if (mCurrTime != null) | ||
| 228 | + mCurrTime.setText(stringForTime(position)); | ||
| 229 | + } | ||
| 230 | + | ||
| 231 | + @Override | ||
| 232 | + public void onLockStateChanged(boolean isLocked) { | ||
| 233 | + onVisibilityChanged(!isLocked, null); | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + @Override | ||
| 237 | + public void onClick(View v) { | ||
| 238 | + int id = v.getId(); | ||
| 239 | + if (id == R.id.fullscreen) { | ||
| 240 | + toggleFullScreen(); | ||
| 241 | + } else if (id == R.id.iv_play) { | ||
| 242 | + mControlWrapper.togglePlay(); | ||
| 243 | + } | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + /** | ||
| 247 | + * 横竖屏切换 | ||
| 248 | + */ | ||
| 249 | + private void toggleFullScreen() { | ||
| 250 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 251 | + mControlWrapper.toggleFullScreen(activity); | ||
| 252 | + if (mControlWrapper.isFullScreen()){ | ||
| 253 | + mBottomProgress.setVisibility(GONE); | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + @Override | ||
| 258 | + public void onStartTrackingTouch(SeekBar seekBar) { | ||
| 259 | + mIsDragging = true; | ||
| 260 | + mControlWrapper.stopProgress(); | ||
| 261 | + mControlWrapper.stopFadeOut(); | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + @Override | ||
| 265 | + public void onStopTrackingTouch(SeekBar seekBar) { | ||
| 266 | + long duration = mControlWrapper.getDuration(); | ||
| 267 | + long newPosition = (duration * seekBar.getProgress()) / mVideoProgress.getMax(); | ||
| 268 | + mControlWrapper.seekTo((int) newPosition); | ||
| 269 | + mIsDragging = false; | ||
| 270 | + mControlWrapper.startProgress(); | ||
| 271 | + mControlWrapper.startFadeOut(); | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + @Override | ||
| 275 | + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { | ||
| 276 | + if (!fromUser) { | ||
| 277 | + return; | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + long duration = mControlWrapper.getDuration(); | ||
| 281 | + long newPosition = (duration * progress) / mVideoProgress.getMax(); | ||
| 282 | + if (mCurrTime != null) | ||
| 283 | + mCurrTime.setText(stringForTime((int) newPosition)); | ||
| 284 | + } | ||
| 285 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/GestureView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.animation.Animator; | ||
| 4 | +import android.animation.AnimatorListenerAdapter; | ||
| 5 | +import android.content.Context; | ||
| 6 | +import android.util.AttributeSet; | ||
| 7 | +import android.view.LayoutInflater; | ||
| 8 | +import android.view.View; | ||
| 9 | +import android.view.animation.Animation; | ||
| 10 | +import android.widget.FrameLayout; | ||
| 11 | +import android.widget.ImageView; | ||
| 12 | +import android.widget.LinearLayout; | ||
| 13 | +import android.widget.ProgressBar; | ||
| 14 | +import android.widget.TextView; | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +import com.cnlive.cloud.video.R; | ||
| 18 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 19 | +import com.libs.video.video.exoplayer.controller.IGestureComponent; | ||
| 20 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 21 | + | ||
| 22 | +import androidx.annotation.NonNull; | ||
| 23 | +import androidx.annotation.Nullable; | ||
| 24 | + | ||
| 25 | +import static com.libs.video.video.exoplayer.util.PlayerUtils.stringForTime; | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +/** | ||
| 29 | + * 手势控制 | ||
| 30 | + */ | ||
| 31 | +public class GestureView extends FrameLayout implements IGestureComponent { | ||
| 32 | + | ||
| 33 | + public GestureView(@NonNull Context context) { | ||
| 34 | + super(context); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public GestureView(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 38 | + super(context, attrs); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public GestureView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 42 | + super(context, attrs, defStyleAttr); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + private ControlWrapper mControlWrapper; | ||
| 46 | + | ||
| 47 | + private ImageView mIcon; | ||
| 48 | + private ProgressBar mProgressPercent; | ||
| 49 | + private TextView mTextPercent; | ||
| 50 | + | ||
| 51 | + private LinearLayout mCenterContainer; | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + { | ||
| 55 | + setVisibility(GONE); | ||
| 56 | + LayoutInflater.from(getContext()).inflate(R.layout.cnvideo_layout_gesture_control_view, this, true); | ||
| 57 | + mIcon = findViewById(R.id.iv_icon); | ||
| 58 | + mProgressPercent = findViewById(R.id.pro_percent); | ||
| 59 | + mTextPercent = findViewById(R.id.tv_percent); | ||
| 60 | + mCenterContainer = findViewById(R.id.center_container); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Override | ||
| 64 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 65 | + mControlWrapper = controlWrapper; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Override | ||
| 69 | + public View getView() { | ||
| 70 | + return this; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @Override | ||
| 74 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 75 | + | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @Override | ||
| 79 | + public void onPlayerStateChanged(int playerState) { | ||
| 80 | + | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Override | ||
| 84 | + public void onStartSlide() { | ||
| 85 | + mControlWrapper.hide(); | ||
| 86 | + mCenterContainer.setVisibility(VISIBLE); | ||
| 87 | + mCenterContainer.setAlpha(1f); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + @Override | ||
| 91 | + public void onStopSlide() { | ||
| 92 | + mCenterContainer.animate() | ||
| 93 | + .alpha(0f) | ||
| 94 | + .setDuration(300) | ||
| 95 | + .setListener(new AnimatorListenerAdapter() { | ||
| 96 | + @Override | ||
| 97 | + public void onAnimationEnd(Animator animation) { | ||
| 98 | + super.onAnimationEnd(animation); | ||
| 99 | + mCenterContainer.setVisibility(GONE); | ||
| 100 | + } | ||
| 101 | + }) | ||
| 102 | + .start(); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + @Override | ||
| 106 | + public void onPositionChange(int slidePosition, int currentPosition, int duration) { | ||
| 107 | + mProgressPercent.setVisibility(GONE); | ||
| 108 | + if (slidePosition > currentPosition) { | ||
| 109 | + mIcon.setImageResource(R.drawable.cnvideo_ic_action_fast_forward); | ||
| 110 | + } else { | ||
| 111 | + mIcon.setImageResource(R.drawable.cnvideo_ic_action_fast_rewind); | ||
| 112 | + } | ||
| 113 | + mTextPercent.setText(String.format("%s/%s", stringForTime(slidePosition), stringForTime(duration))); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + @Override | ||
| 117 | + public void onBrightnessChange(int percent) { | ||
| 118 | + mProgressPercent.setVisibility(VISIBLE); | ||
| 119 | + mIcon.setImageResource(R.drawable.cnvideo_ic_action_brightness); | ||
| 120 | + mTextPercent.setText(percent + "%"); | ||
| 121 | + mProgressPercent.setProgress(percent); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + @Override | ||
| 125 | + public void onVolumeChange(int percent) { | ||
| 126 | + | ||
| 127 | + mProgressPercent.setVisibility(VISIBLE); | ||
| 128 | + if (percent <= 0) { | ||
| 129 | + mIcon.setImageResource(R.drawable.cnvideo_ic_action_volume_off); | ||
| 130 | + } else { | ||
| 131 | + mIcon.setImageResource(R.drawable.cnvideo_ic_action_volume_up); | ||
| 132 | + } | ||
| 133 | + mTextPercent.setText(percent + "%"); | ||
| 134 | + mProgressPercent.setProgress(percent); | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + @Override | ||
| 138 | + public void onPlayStateChanged(int playState) { | ||
| 139 | + if (playState == CNVideoView.STATE_IDLE | ||
| 140 | + || playState == CNVideoView.STATE_START_ABORT | ||
| 141 | + || playState == CNVideoView.STATE_PREPARING | ||
| 142 | + || playState == CNVideoView.STATE_PREPARED | ||
| 143 | + || playState == CNVideoView.STATE_ERROR | ||
| 144 | + || playState == CNVideoView.STATE_PLAYBACK_COMPLETED) { | ||
| 145 | + setVisibility(GONE); | ||
| 146 | + } else { | ||
| 147 | + setVisibility(VISIBLE); | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + @Override | ||
| 152 | + public void setProgress(int duration, int position) { | ||
| 153 | + | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + @Override | ||
| 157 | + public void onLockStateChanged(boolean isLock) { | ||
| 158 | + | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/LiveControlView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.content.pm.ActivityInfo; | ||
| 6 | +import android.util.AttributeSet; | ||
| 7 | +import android.view.LayoutInflater; | ||
| 8 | +import android.view.View; | ||
| 9 | +import android.view.animation.Animation; | ||
| 10 | +import android.widget.FrameLayout; | ||
| 11 | +import android.widget.ImageView; | ||
| 12 | +import android.widget.LinearLayout; | ||
| 13 | + | ||
| 14 | +import com.cnlive.cloud.video.R; | ||
| 15 | +import com.libs.video.video.exoplayer.controller.ControlWrapper; | ||
| 16 | +import com.libs.video.video.exoplayer.controller.IControlComponent; | ||
| 17 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 18 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 19 | + | ||
| 20 | +import androidx.annotation.NonNull; | ||
| 21 | +import androidx.annotation.Nullable; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 直播底部控制栏 | ||
| 25 | + */ | ||
| 26 | +public class LiveControlView extends FrameLayout implements IControlComponent, View.OnClickListener { | ||
| 27 | + | ||
| 28 | + private ControlWrapper mControlWrapper; | ||
| 29 | + | ||
| 30 | + private ImageView mFullScreen; | ||
| 31 | + private LinearLayout mBottomContainer; | ||
| 32 | + private ImageView mPlayButton; | ||
| 33 | + | ||
| 34 | + public LiveControlView(@NonNull Context context) { | ||
| 35 | + super(context); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public LiveControlView(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 39 | + super(context, attrs); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public LiveControlView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 43 | + super(context, attrs, defStyleAttr); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + { | ||
| 48 | + setVisibility(GONE); | ||
| 49 | + LayoutInflater.from(getContext()).inflate(R.layout.cnvideo_layout_live_control_view, this, true); | ||
| 50 | + mFullScreen = findViewById(R.id.fullscreen); | ||
| 51 | + mFullScreen.setOnClickListener(this); | ||
| 52 | + mBottomContainer = findViewById(R.id.bottom_container); | ||
| 53 | + mPlayButton = findViewById(R.id.iv_play); | ||
| 54 | + mPlayButton.setOnClickListener(this); | ||
| 55 | + ImageView refresh = findViewById(R.id.iv_refresh); | ||
| 56 | + refresh.setOnClickListener(this); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + @Override | ||
| 60 | + public void attach(@NonNull ControlWrapper controlWrapper) { | ||
| 61 | + mControlWrapper = controlWrapper; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + @Override | ||
| 65 | + public View getView() { | ||
| 66 | + return this; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Override | ||
| 70 | + public void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 71 | + if (isVisible) { | ||
| 72 | + if (getVisibility() == GONE) { | ||
| 73 | + setVisibility(VISIBLE); | ||
| 74 | + if (anim != null) { | ||
| 75 | + startAnimation(anim); | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + } else { | ||
| 79 | + if (getVisibility() == VISIBLE) { | ||
| 80 | + setVisibility(GONE); | ||
| 81 | + if (anim != null) { | ||
| 82 | + startAnimation(anim); | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + public void onPlayStateChanged(int playState) { | ||
| 90 | + switch (playState) { | ||
| 91 | + case CNVideoView.STATE_IDLE: | ||
| 92 | + case CNVideoView.STATE_START_ABORT: | ||
| 93 | + case CNVideoView.STATE_PREPARING: | ||
| 94 | + case CNVideoView.STATE_PREPARED: | ||
| 95 | + case CNVideoView.STATE_ERROR: | ||
| 96 | + case CNVideoView.STATE_PLAYBACK_COMPLETED: | ||
| 97 | + setVisibility(GONE); | ||
| 98 | + break; | ||
| 99 | + case CNVideoView.STATE_PLAYING: | ||
| 100 | + mPlayButton.setSelected(true); | ||
| 101 | + break; | ||
| 102 | + case CNVideoView.STATE_PAUSED: | ||
| 103 | + mPlayButton.setSelected(false); | ||
| 104 | + break; | ||
| 105 | + case CNVideoView.STATE_BUFFERING: | ||
| 106 | + case CNVideoView.STATE_BUFFERED: | ||
| 107 | + mPlayButton.setSelected(mControlWrapper.isPlaying()); | ||
| 108 | + break; | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + @Override | ||
| 113 | + public void onPlayerStateChanged(int playerState) { | ||
| 114 | + switch (playerState) { | ||
| 115 | + case CNVideoView.PLAYER_NORMAL: | ||
| 116 | + mFullScreen.setSelected(false); | ||
| 117 | + break; | ||
| 118 | + case CNVideoView.PLAYER_FULL_SCREEN: | ||
| 119 | + mFullScreen.setSelected(true); | ||
| 120 | + break; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 124 | + if (activity != null && mControlWrapper.hasCutout()) { | ||
| 125 | + int orientation = activity.getRequestedOrientation(); | ||
| 126 | + int cutoutHeight = mControlWrapper.getCutoutHeight(); | ||
| 127 | + if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { | ||
| 128 | + mBottomContainer.setPadding(0, 0, 0, 0); | ||
| 129 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { | ||
| 130 | + mBottomContainer.setPadding(cutoutHeight, 0, 0, 0); | ||
| 131 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) { | ||
| 132 | + mBottomContainer.setPadding(0, 0, cutoutHeight, 0); | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + @Override | ||
| 138 | + public void setProgress(int duration, int position) { | ||
| 139 | + | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + @Override | ||
| 143 | + public void onLockStateChanged(boolean isLocked) { | ||
| 144 | + onVisibilityChanged(!isLocked, null); | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + @Override | ||
| 148 | + public void onClick(View v) { | ||
| 149 | + int id = v.getId(); | ||
| 150 | + if (id == R.id.fullscreen) { | ||
| 151 | + toggleFullScreen(); | ||
| 152 | + } else if (id == R.id.iv_play) { | ||
| 153 | + mControlWrapper.togglePlay(); | ||
| 154 | + } else if (id == R.id.iv_refresh) { | ||
| 155 | + mControlWrapper.replay(true); | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + /** | ||
| 160 | + * 横竖屏切换 | ||
| 161 | + */ | ||
| 162 | + private void toggleFullScreen() { | ||
| 163 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 164 | + mControlWrapper.toggleFullScreen(activity); | ||
| 165 | + } | ||
| 166 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/component/MarqueeTextView.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.component; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.util.AttributeSet; | ||
| 5 | +import android.widget.TextView; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 实现跑马灯效果的TextView | ||
| 9 | + */ | ||
| 10 | +public class MarqueeTextView extends TextView { | ||
| 11 | + private boolean mNeedFocus; | ||
| 12 | + public MarqueeTextView(Context context) { | ||
| 13 | + super(context); | ||
| 14 | + } | ||
| 15 | + public MarqueeTextView(Context context, AttributeSet attrs) { | ||
| 16 | + super(context, attrs); | ||
| 17 | + } | ||
| 18 | + public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) { | ||
| 19 | + super(context, attrs, defStyleAttr); | ||
| 20 | + } | ||
| 21 | + //返回textview是否处在选中的状态 | ||
| 22 | + //而只有选中的textview才能够实现跑马灯效果 | ||
| 23 | + @Override | ||
| 24 | + public boolean isFocused() { | ||
| 25 | + if (mNeedFocus) { | ||
| 26 | + return false; | ||
| 27 | + } | ||
| 28 | + return super.isFocused(); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + public void setNeedFocus(boolean needFocus) { | ||
| 32 | + mNeedFocus = needFocus; | ||
| 33 | + } | ||
| 34 | +} | ||
| 0 | \ No newline at end of file | 35 | \ No newline at end of file |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/BaseVideoController.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.content.pm.ActivityInfo; | ||
| 6 | +import android.util.AttributeSet; | ||
| 7 | +import android.view.LayoutInflater; | ||
| 8 | +import android.view.OrientationEventListener; | ||
| 9 | +import android.view.View; | ||
| 10 | +import android.view.animation.AlphaAnimation; | ||
| 11 | +import android.view.animation.Animation; | ||
| 12 | +import android.widget.FrameLayout; | ||
| 13 | + | ||
| 14 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 15 | +import com.libs.video.video.exoplayer.play.VideoViewManager; | ||
| 16 | +import com.libs.video.video.exoplayer.util.CutoutUtil; | ||
| 17 | +import com.libs.video.video.exoplayer.util.L; | ||
| 18 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 19 | + | ||
| 20 | +import java.util.Iterator; | ||
| 21 | +import java.util.LinkedHashMap; | ||
| 22 | +import java.util.Map; | ||
| 23 | + | ||
| 24 | +import androidx.annotation.AttrRes; | ||
| 25 | +import androidx.annotation.CallSuper; | ||
| 26 | +import androidx.annotation.NonNull; | ||
| 27 | +import androidx.annotation.Nullable; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * 控制器基类 | ||
| 31 | + */ | ||
| 32 | +public abstract class BaseVideoController extends FrameLayout | ||
| 33 | + implements IVideoController, | ||
| 34 | + OrientationHelper.OnOrientationChangeListener { | ||
| 35 | + | ||
| 36 | + //播放器包装类,集合了MediaPlayerControl的api和IVideoController的api | ||
| 37 | + protected ControlWrapper mControlWrapper; | ||
| 38 | + | ||
| 39 | + @Nullable | ||
| 40 | + protected Activity mActivity; | ||
| 41 | + | ||
| 42 | + //控制器是否处于显示状态 | ||
| 43 | + protected boolean mShowing; | ||
| 44 | + | ||
| 45 | + //是否处于锁定状态 | ||
| 46 | + protected boolean mIsLocked; | ||
| 47 | + | ||
| 48 | + //播放视图隐藏超时 | ||
| 49 | + protected int mDefaultTimeout = 4000; | ||
| 50 | + | ||
| 51 | + //是否开启根据屏幕方向进入/退出全屏 | ||
| 52 | + private boolean mEnableOrientation; | ||
| 53 | + //屏幕方向监听辅助类 | ||
| 54 | + protected OrientationHelper mOrientationHelper; | ||
| 55 | + | ||
| 56 | + //用户设置是否适配刘海屏 | ||
| 57 | + private boolean mAdaptCutout; | ||
| 58 | + //是否有刘海 | ||
| 59 | + private Boolean mHasCutout; | ||
| 60 | + //刘海的高度 | ||
| 61 | + private int mCutoutHeight; | ||
| 62 | + | ||
| 63 | + //是否开始刷新进度 | ||
| 64 | + private boolean mIsStartProgress; | ||
| 65 | + | ||
| 66 | + //保存了所有的控制组件 | ||
| 67 | + protected LinkedHashMap<IControlComponent, Boolean> mControlComponents = new LinkedHashMap<>(); | ||
| 68 | + | ||
| 69 | + private Animation mShowAnim; | ||
| 70 | + private Animation mHideAnim; | ||
| 71 | + | ||
| 72 | + public BaseVideoController(@NonNull Context context) { | ||
| 73 | + this(context, null); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + public BaseVideoController(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 77 | + this(context, attrs, 0); | ||
| 78 | + | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + public BaseVideoController(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { | ||
| 82 | + super(context, attrs, defStyleAttr); | ||
| 83 | + initView(); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + protected void initView() { | ||
| 87 | + if (getLayoutId() != 0) { | ||
| 88 | + LayoutInflater.from(getContext()).inflate(getLayoutId(), this, true); | ||
| 89 | + } | ||
| 90 | + mOrientationHelper = new OrientationHelper(getContext().getApplicationContext()); | ||
| 91 | + mEnableOrientation = VideoViewManager.getConfig().mEnableOrientation; | ||
| 92 | + mAdaptCutout = VideoViewManager.getConfig().mAdaptCutout; | ||
| 93 | + | ||
| 94 | + mShowAnim = new AlphaAnimation(0f, 1f); | ||
| 95 | + mShowAnim.setDuration(300); | ||
| 96 | + mHideAnim = new AlphaAnimation(1f, 0f); | ||
| 97 | + mHideAnim.setDuration(300); | ||
| 98 | + | ||
| 99 | + mActivity = PlayerUtils.scanForActivity(getContext()); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + /** | ||
| 103 | + * 设置控制器布局文件,子类必须实现 | ||
| 104 | + */ | ||
| 105 | + protected abstract int getLayoutId(); | ||
| 106 | + | ||
| 107 | + /** | ||
| 108 | + * 重要:此方法用于将{@link CNVideoView} 和控制器绑定 | ||
| 109 | + */ | ||
| 110 | + @CallSuper | ||
| 111 | + public void setMediaPlayer(MediaPlayerControl mediaPlayer) { | ||
| 112 | + mControlWrapper = new ControlWrapper(mediaPlayer, this); | ||
| 113 | + //绑定ControlComponent和Controller | ||
| 114 | + for (Map.Entry<IControlComponent, Boolean> next : mControlComponents.entrySet()) { | ||
| 115 | + IControlComponent component = next.getKey(); | ||
| 116 | + component.attach(mControlWrapper); | ||
| 117 | + } | ||
| 118 | + //开始监听设备方向 | ||
| 119 | + mOrientationHelper.setOnOrientationChangeListener(this); | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + /** | ||
| 123 | + * 添加控制组件,最后面添加的在最下面,合理组织添加顺序,可让ControlComponent位于不同的层级 | ||
| 124 | + */ | ||
| 125 | + public void addControlComponent(IControlComponent... component) { | ||
| 126 | + for (IControlComponent item : component) { | ||
| 127 | + addControlComponent(item, false); | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + /** | ||
| 132 | + * 添加控制组件,最后面添加的在最下面,合理组织添加顺序,可让ControlComponent位于不同的层级 | ||
| 133 | + * | ||
| 134 | + * @param isPrivate 是否为独有的组件,如果是就不添加到控制器中 | ||
| 135 | + */ | ||
| 136 | + public void addControlComponent(IControlComponent component, boolean isPrivate) { | ||
| 137 | + mControlComponents.put(component, isPrivate); | ||
| 138 | + if (mControlWrapper != null) { | ||
| 139 | + component.attach(mControlWrapper); | ||
| 140 | + } | ||
| 141 | + View view = component.getView(); | ||
| 142 | + if (view != null && !isPrivate) { | ||
| 143 | + addView(view, 0); | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + /** | ||
| 148 | + * 移除控制组件 | ||
| 149 | + */ | ||
| 150 | + public void removeControlComponent(IControlComponent component) { | ||
| 151 | + removeView(component.getView()); | ||
| 152 | + mControlComponents.remove(component); | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + /** | ||
| 156 | + * 移除所有控制组件 | ||
| 157 | + */ | ||
| 158 | + public void removeAllControlComponent() { | ||
| 159 | + for (Map.Entry<IControlComponent, Boolean> next : mControlComponents.entrySet()) { | ||
| 160 | + removeView(next.getKey().getView()); | ||
| 161 | + } | ||
| 162 | + mControlComponents.clear(); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + /** | ||
| 166 | + * 移除所有私有控制组件 | ||
| 167 | + */ | ||
| 168 | + public void removeAllPrivateComponents() { | ||
| 169 | + Iterator<Map.Entry<IControlComponent, Boolean>> it = mControlComponents.entrySet().iterator(); | ||
| 170 | + while (it.hasNext()) { | ||
| 171 | + Map.Entry<IControlComponent, Boolean> next = it.next(); | ||
| 172 | + if (next.getValue()) { | ||
| 173 | + it.remove(); | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + /** | ||
| 179 | + * {@link CNVideoView}调用此方法向控制器设置播放状态 | ||
| 180 | + */ | ||
| 181 | + @CallSuper | ||
| 182 | + public void setPlayState(int playState) { | ||
| 183 | + handlePlayStateChanged(playState); | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + /** | ||
| 187 | + * {@link CNVideoView}调用此方法向控制器设置播放器状态 | ||
| 188 | + */ | ||
| 189 | + @CallSuper | ||
| 190 | + public void setPlayerState(final int playerState) { | ||
| 191 | + handlePlayerStateChanged(playerState); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + /** | ||
| 195 | + * 设置播放视图自动隐藏超时 | ||
| 196 | + */ | ||
| 197 | + public void setDismissTimeout(int timeout) { | ||
| 198 | + if (timeout > 0) { | ||
| 199 | + mDefaultTimeout = timeout; | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + /** | ||
| 204 | + * 隐藏播放视图 | ||
| 205 | + */ | ||
| 206 | + @Override | ||
| 207 | + public void hide() { | ||
| 208 | + if (mShowing) { | ||
| 209 | + stopFadeOut(); | ||
| 210 | + handleVisibilityChanged(false, mHideAnim); | ||
| 211 | + mShowing = false; | ||
| 212 | + } | ||
| 213 | + } | ||
| 214 | + | ||
| 215 | + /** | ||
| 216 | + * 显示播放视图 | ||
| 217 | + */ | ||
| 218 | + @Override | ||
| 219 | + public void show() { | ||
| 220 | + if (!mShowing) { | ||
| 221 | + handleVisibilityChanged(true, mShowAnim); | ||
| 222 | + startFadeOut(); | ||
| 223 | + mShowing = true; | ||
| 224 | + } | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + @Override | ||
| 228 | + public boolean isShowing() { | ||
| 229 | + return mShowing; | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + /** | ||
| 233 | + * 开始计时 | ||
| 234 | + */ | ||
| 235 | + @Override | ||
| 236 | + public void startFadeOut() { | ||
| 237 | + //重新开始计时 | ||
| 238 | + stopFadeOut(); | ||
| 239 | + postDelayed(mFadeOut, mDefaultTimeout); | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + /** | ||
| 243 | + * 取消计时 | ||
| 244 | + */ | ||
| 245 | + @Override | ||
| 246 | + public void stopFadeOut() { | ||
| 247 | + removeCallbacks(mFadeOut); | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + /** | ||
| 251 | + * 隐藏播放视图Runnable | ||
| 252 | + */ | ||
| 253 | + public final Runnable mFadeOut = new Runnable() { | ||
| 254 | + @Override | ||
| 255 | + public void run() { | ||
| 256 | + hide(); | ||
| 257 | + } | ||
| 258 | + }; | ||
| 259 | + | ||
| 260 | + @Override | ||
| 261 | + public void setLocked(boolean locked) { | ||
| 262 | + mIsLocked = locked; | ||
| 263 | + handleLockStateChanged(locked); | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + @Override | ||
| 267 | + public boolean isLocked() { | ||
| 268 | + return mIsLocked; | ||
| 269 | + } | ||
| 270 | + | ||
| 271 | + /** | ||
| 272 | + * 开始刷新进度,注意:需在STATE_PLAYING时调用才会开始刷新进度 | ||
| 273 | + */ | ||
| 274 | + @Override | ||
| 275 | + public void startProgress() { | ||
| 276 | + if (mIsStartProgress) return; | ||
| 277 | + post(mShowProgress); | ||
| 278 | + mIsStartProgress = true; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + /** | ||
| 282 | + * 停止刷新进度 | ||
| 283 | + */ | ||
| 284 | + @Override | ||
| 285 | + public void stopProgress() { | ||
| 286 | + if (!mIsStartProgress) return; | ||
| 287 | + removeCallbacks(mShowProgress); | ||
| 288 | + mIsStartProgress = false; | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + /** | ||
| 292 | + * 刷新进度Runnable | ||
| 293 | + */ | ||
| 294 | + private Runnable mShowProgress = new Runnable() { | ||
| 295 | + @Override | ||
| 296 | + public void run() { | ||
| 297 | + int pos = setProgress(); | ||
| 298 | + if (mControlWrapper.isPlaying()) { | ||
| 299 | + postDelayed(mShowProgress, 1000 - (pos % 1000)); | ||
| 300 | + } else { | ||
| 301 | + mIsStartProgress = false; | ||
| 302 | + } | ||
| 303 | + } | ||
| 304 | + }; | ||
| 305 | + | ||
| 306 | + private int setProgress() { | ||
| 307 | + int position = (int) mControlWrapper.getCurrentPosition(); | ||
| 308 | + int duration = (int) mControlWrapper.getDuration(); | ||
| 309 | + handleSetProgress(duration, position); | ||
| 310 | + return position; | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + /** | ||
| 314 | + * 设置是否适配刘海屏 | ||
| 315 | + */ | ||
| 316 | + public void setAdaptCutout(boolean adaptCutout) { | ||
| 317 | + mAdaptCutout = adaptCutout; | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + @Override | ||
| 321 | + protected void onAttachedToWindow() { | ||
| 322 | + super.onAttachedToWindow(); | ||
| 323 | + checkCutout(); | ||
| 324 | + } | ||
| 325 | + | ||
| 326 | + /** | ||
| 327 | + * 检查是否需要适配刘海 | ||
| 328 | + */ | ||
| 329 | + private void checkCutout() { | ||
| 330 | + if (!mAdaptCutout) return; | ||
| 331 | + if (mActivity != null && mHasCutout == null) { | ||
| 332 | + mHasCutout = CutoutUtil.allowDisplayToCutout(mActivity); | ||
| 333 | + if (mHasCutout) { | ||
| 334 | + //竖屏下的状态栏高度可认为是刘海的高度 | ||
| 335 | + mCutoutHeight = (int) PlayerUtils.getStatusBarHeightPortrait(mActivity); | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + L.d("hasCutout: " + mHasCutout + " cutout height: " + mCutoutHeight); | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + /** | ||
| 342 | + * 是否有刘海屏 | ||
| 343 | + */ | ||
| 344 | + @Override | ||
| 345 | + public boolean hasCutout() { | ||
| 346 | + return mHasCutout != null && mHasCutout; | ||
| 347 | + } | ||
| 348 | + | ||
| 349 | + /** | ||
| 350 | + * 刘海的高度 | ||
| 351 | + */ | ||
| 352 | + @Override | ||
| 353 | + public int getCutoutHeight() { | ||
| 354 | + return mCutoutHeight; | ||
| 355 | + } | ||
| 356 | + | ||
| 357 | + /** | ||
| 358 | + * 显示移动网络播放提示 | ||
| 359 | + * | ||
| 360 | + * @return 返回显示移动网络播放提示的条件,false:不显示, true显示 | ||
| 361 | + * 此处默认根据手机网络类型来决定是否显示,开发者可以重写相关逻辑 | ||
| 362 | + */ | ||
| 363 | + public boolean showNetWarning() { | ||
| 364 | + return PlayerUtils.getNetworkType(getContext()) == PlayerUtils.NETWORK_MOBILE | ||
| 365 | + && !VideoViewManager.instance().playOnMobileNetwork(); | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + /** | ||
| 369 | + * 播放和暂停 | ||
| 370 | + */ | ||
| 371 | + protected void togglePlay() { | ||
| 372 | + mControlWrapper.togglePlay(); | ||
| 373 | + } | ||
| 374 | + | ||
| 375 | + /** | ||
| 376 | + * 横竖屏切换 | ||
| 377 | + */ | ||
| 378 | + protected void toggleFullScreen() { | ||
| 379 | + mControlWrapper.toggleFullScreen(mActivity); | ||
| 380 | + } | ||
| 381 | + | ||
| 382 | + /** | ||
| 383 | + * 子类中请使用此方法来进入全屏 | ||
| 384 | + * | ||
| 385 | + * @return 是否成功进入全屏 | ||
| 386 | + */ | ||
| 387 | + protected boolean startFullScreen() { | ||
| 388 | + if (mActivity == null || mActivity.isFinishing()) return false; | ||
| 389 | + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); | ||
| 390 | + mControlWrapper.startFullScreen(); | ||
| 391 | + return true; | ||
| 392 | + } | ||
| 393 | + | ||
| 394 | + /** | ||
| 395 | + * 子类中请使用此方法来退出全屏 | ||
| 396 | + * | ||
| 397 | + * @return 是否成功退出全屏 | ||
| 398 | + */ | ||
| 399 | + protected boolean stopFullScreen() { | ||
| 400 | + if (mActivity == null || mActivity.isFinishing()) return false; | ||
| 401 | + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | ||
| 402 | + mControlWrapper.stopFullScreen(); | ||
| 403 | + return true; | ||
| 404 | + } | ||
| 405 | + | ||
| 406 | + /** | ||
| 407 | + * 改变返回键逻辑,用于activity | ||
| 408 | + */ | ||
| 409 | + public boolean onBackPressed() { | ||
| 410 | + return false; | ||
| 411 | + } | ||
| 412 | + | ||
| 413 | + @Override | ||
| 414 | + public void onWindowFocusChanged(boolean hasWindowFocus) { | ||
| 415 | + super.onWindowFocusChanged(hasWindowFocus); | ||
| 416 | + if (mControlWrapper.isPlaying() | ||
| 417 | + && (mEnableOrientation || mControlWrapper.isFullScreen())) { | ||
| 418 | + if (hasWindowFocus) { | ||
| 419 | + postDelayed(new Runnable() { | ||
| 420 | + @Override | ||
| 421 | + public void run() { | ||
| 422 | + mOrientationHelper.enable(); | ||
| 423 | + } | ||
| 424 | + }, 800); | ||
| 425 | + } else { | ||
| 426 | + mOrientationHelper.disable(); | ||
| 427 | + } | ||
| 428 | + } | ||
| 429 | + } | ||
| 430 | + | ||
| 431 | + /** | ||
| 432 | + * 是否自动旋转, 默认不自动旋转 | ||
| 433 | + */ | ||
| 434 | + public void setEnableOrientation(boolean enableOrientation) { | ||
| 435 | + mEnableOrientation = enableOrientation; | ||
| 436 | + } | ||
| 437 | + | ||
| 438 | + private int mOrientation = 0; | ||
| 439 | + | ||
| 440 | + @CallSuper | ||
| 441 | + @Override | ||
| 442 | + public void onOrientationChanged(int orientation) { | ||
| 443 | + if (mActivity == null || mActivity.isFinishing()) return; | ||
| 444 | + | ||
| 445 | + //记录用户手机上一次放置的位置 | ||
| 446 | + int lastOrientation = mOrientation; | ||
| 447 | + | ||
| 448 | + if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) { | ||
| 449 | + //手机平放时,检测不到有效的角度 | ||
| 450 | + //重置为原始位置 -1 | ||
| 451 | + mOrientation = -1; | ||
| 452 | + return; | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + if (orientation > 350 || orientation < 10) { | ||
| 456 | + int o = mActivity.getRequestedOrientation(); | ||
| 457 | + //手动切换横竖屏 | ||
| 458 | + if (o == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE && lastOrientation == 0) return; | ||
| 459 | + if (mOrientation == 0) return; | ||
| 460 | + //0度,用户竖直拿着手机 | ||
| 461 | + mOrientation = 0; | ||
| 462 | + onOrientationPortrait(mActivity); | ||
| 463 | + } else if (orientation > 80 && orientation < 100) { | ||
| 464 | + | ||
| 465 | + int o = mActivity.getRequestedOrientation(); | ||
| 466 | + //手动切换横竖屏 | ||
| 467 | + if (o == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT && lastOrientation == 90) return; | ||
| 468 | + if (mOrientation == 90) return; | ||
| 469 | + //90度,用户右侧横屏拿着手机 | ||
| 470 | + mOrientation = 90; | ||
| 471 | + onOrientationReverseLandscape(mActivity); | ||
| 472 | + } else if (orientation > 260 && orientation < 280) { | ||
| 473 | + int o = mActivity.getRequestedOrientation(); | ||
| 474 | + //手动切换横竖屏 | ||
| 475 | + if (o == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT && lastOrientation == 270) return; | ||
| 476 | + if (mOrientation == 270) return; | ||
| 477 | + //270度,用户左侧横屏拿着手机 | ||
| 478 | + mOrientation = 270; | ||
| 479 | + onOrientationLandscape(mActivity); | ||
| 480 | + } | ||
| 481 | + } | ||
| 482 | + | ||
| 483 | + /** | ||
| 484 | + * 竖屏 | ||
| 485 | + */ | ||
| 486 | + protected void onOrientationPortrait(Activity activity) { | ||
| 487 | + //屏幕锁定的情况 | ||
| 488 | + if (mIsLocked) return; | ||
| 489 | + //没有开启设备方向监听的情况 | ||
| 490 | + if (!mEnableOrientation) return; | ||
| 491 | + | ||
| 492 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | ||
| 493 | + mControlWrapper.stopFullScreen(); | ||
| 494 | + } | ||
| 495 | + | ||
| 496 | + /** | ||
| 497 | + * 横屏 | ||
| 498 | + */ | ||
| 499 | + protected void onOrientationLandscape(Activity activity) { | ||
| 500 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); | ||
| 501 | + if (mControlWrapper.isFullScreen()) { | ||
| 502 | + handlePlayerStateChanged(CNVideoView.PLAYER_FULL_SCREEN); | ||
| 503 | + } else { | ||
| 504 | + mControlWrapper.startFullScreen(); | ||
| 505 | + } | ||
| 506 | + } | ||
| 507 | + | ||
| 508 | + /** | ||
| 509 | + * 反向横屏 | ||
| 510 | + */ | ||
| 511 | + protected void onOrientationReverseLandscape(Activity activity) { | ||
| 512 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); | ||
| 513 | + if (mControlWrapper.isFullScreen()) { | ||
| 514 | + handlePlayerStateChanged(CNVideoView.PLAYER_FULL_SCREEN); | ||
| 515 | + } else { | ||
| 516 | + mControlWrapper.startFullScreen(); | ||
| 517 | + } | ||
| 518 | + } | ||
| 519 | + | ||
| 520 | + //------------------------ start handle event change ------------------------// | ||
| 521 | + | ||
| 522 | + private void handleVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 523 | + if (!mIsLocked) { //没锁住时才向ControlComponent下发此事件 | ||
| 524 | + for (Map.Entry<IControlComponent, Boolean> next | ||
| 525 | + : mControlComponents.entrySet()) { | ||
| 526 | + IControlComponent component = next.getKey(); | ||
| 527 | + component.onVisibilityChanged(isVisible, anim); | ||
| 528 | + } | ||
| 529 | + } | ||
| 530 | + onVisibilityChanged(isVisible, anim); | ||
| 531 | + } | ||
| 532 | + | ||
| 533 | + /** | ||
| 534 | + * 子类重写此方法监听控制的显示和隐藏 | ||
| 535 | + * | ||
| 536 | + * @param isVisible 是否可见 | ||
| 537 | + * @param anim 显示/隐藏动画 | ||
| 538 | + */ | ||
| 539 | + protected void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 540 | + | ||
| 541 | + } | ||
| 542 | + | ||
| 543 | + private void handlePlayStateChanged(int playState) { | ||
| 544 | + for (Map.Entry<IControlComponent, Boolean> next | ||
| 545 | + : mControlComponents.entrySet()) { | ||
| 546 | + IControlComponent component = next.getKey(); | ||
| 547 | + component.onPlayStateChanged(playState); | ||
| 548 | + } | ||
| 549 | + onPlayStateChanged(playState); | ||
| 550 | + } | ||
| 551 | + | ||
| 552 | + /** | ||
| 553 | + * 子类重写此方法并在其中更新控制器在不同播放状态下的ui | ||
| 554 | + */ | ||
| 555 | + @CallSuper | ||
| 556 | + protected void onPlayStateChanged(int playState) { | ||
| 557 | + switch (playState) { | ||
| 558 | + case CNVideoView.STATE_IDLE: | ||
| 559 | + mOrientationHelper.disable(); | ||
| 560 | + mOrientation = 0; | ||
| 561 | + mIsLocked = false; | ||
| 562 | + mShowing = false; | ||
| 563 | + removeAllPrivateComponents(); | ||
| 564 | + break; | ||
| 565 | + case CNVideoView.STATE_PLAYBACK_COMPLETED: | ||
| 566 | + mIsLocked = false; | ||
| 567 | + mShowing = false; | ||
| 568 | + break; | ||
| 569 | + case CNVideoView.STATE_ERROR: | ||
| 570 | + mShowing = false; | ||
| 571 | + break; | ||
| 572 | + } | ||
| 573 | + } | ||
| 574 | + | ||
| 575 | + private void handlePlayerStateChanged(int playerState) { | ||
| 576 | + for (Map.Entry<IControlComponent, Boolean> next | ||
| 577 | + : mControlComponents.entrySet()) { | ||
| 578 | + IControlComponent component = next.getKey(); | ||
| 579 | + component.onPlayerStateChanged(playerState); | ||
| 580 | + } | ||
| 581 | + onPlayerStateChanged(playerState); | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + /** | ||
| 585 | + * 子类重写此方法并在其中更新控制器在不同播放器状态下的ui | ||
| 586 | + */ | ||
| 587 | + @CallSuper | ||
| 588 | + protected void onPlayerStateChanged(int playerState) { | ||
| 589 | + switch (playerState) { | ||
| 590 | + case CNVideoView.PLAYER_NORMAL: | ||
| 591 | + if (mEnableOrientation) { | ||
| 592 | + mOrientationHelper.enable(); | ||
| 593 | + } else { | ||
| 594 | + mOrientationHelper.disable(); | ||
| 595 | + } | ||
| 596 | + if (hasCutout()) { | ||
| 597 | + CutoutUtil.adaptCutoutAboveAndroidP(getContext(), false); | ||
| 598 | + } | ||
| 599 | + break; | ||
| 600 | + case CNVideoView.PLAYER_FULL_SCREEN: | ||
| 601 | + //在全屏时强制监听设备方向 | ||
| 602 | + mOrientationHelper.enable(); | ||
| 603 | + if (hasCutout()) { | ||
| 604 | + CutoutUtil.adaptCutoutAboveAndroidP(getContext(), true); | ||
| 605 | + } | ||
| 606 | + break; | ||
| 607 | + case CNVideoView.PLAYER_TINY_SCREEN: | ||
| 608 | + mOrientationHelper.disable(); | ||
| 609 | + break; | ||
| 610 | + } | ||
| 611 | + } | ||
| 612 | + | ||
| 613 | + private void handleSetProgress(int duration, int position) { | ||
| 614 | + for (Map.Entry<IControlComponent, Boolean> next | ||
| 615 | + : mControlComponents.entrySet()) { | ||
| 616 | + IControlComponent component = next.getKey(); | ||
| 617 | + component.setProgress(duration, position); | ||
| 618 | + } | ||
| 619 | + setProgress(duration, position); | ||
| 620 | + } | ||
| 621 | + | ||
| 622 | + /** | ||
| 623 | + * 刷新进度回调,子类可在此方法监听进度刷新,然后更新ui | ||
| 624 | + * | ||
| 625 | + * @param duration 视频总时长 | ||
| 626 | + * @param position 视频当前时长 | ||
| 627 | + */ | ||
| 628 | + protected void setProgress(int duration, int position) { | ||
| 629 | + | ||
| 630 | + } | ||
| 631 | + | ||
| 632 | + private void handleLockStateChanged(boolean isLocked) { | ||
| 633 | + for (Map.Entry<IControlComponent, Boolean> next | ||
| 634 | + : mControlComponents.entrySet()) { | ||
| 635 | + IControlComponent component = next.getKey(); | ||
| 636 | + component.onLockStateChanged(isLocked); | ||
| 637 | + } | ||
| 638 | + onLockStateChanged(isLocked); | ||
| 639 | + } | ||
| 640 | + | ||
| 641 | + /** | ||
| 642 | + * 子类可重写此方法监听锁定状态发生改变,然后更新ui | ||
| 643 | + */ | ||
| 644 | + protected void onLockStateChanged(boolean isLocked) { | ||
| 645 | + | ||
| 646 | + } | ||
| 647 | + | ||
| 648 | + //------------------------ end handle event change ------------------------// | ||
| 649 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/ControlWrapper.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.pm.ActivityInfo; | ||
| 5 | +import android.graphics.Bitmap; | ||
| 6 | + | ||
| 7 | +import androidx.annotation.NonNull; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 此类的目的是为了在ControlComponent中既能调用VideoView的api又能调用BaseVideoController的api, | ||
| 11 | + * 并对部分api做了封装,方便使用 | ||
| 12 | + */ | ||
| 13 | +public class ControlWrapper implements MediaPlayerControl, IVideoController { | ||
| 14 | + | ||
| 15 | + private MediaPlayerControl mPlayerControl; | ||
| 16 | + private IVideoController mController; | ||
| 17 | + | ||
| 18 | + public ControlWrapper(@NonNull MediaPlayerControl playerControl, @NonNull IVideoController controller) { | ||
| 19 | + mPlayerControl = playerControl; | ||
| 20 | + mController = controller; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public int getMediaCount() { | ||
| 25 | + return mPlayerControl.getMediaCount(); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public void start() { | ||
| 30 | + mPlayerControl.start(); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + @Override | ||
| 34 | + public void pause() { | ||
| 35 | + mPlayerControl.pause(); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + public long getDuration() { | ||
| 40 | + return mPlayerControl.getDuration(); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Override | ||
| 44 | + public long getCurrentPosition() { | ||
| 45 | + return mPlayerControl.getCurrentPosition(); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @Override | ||
| 49 | + public void seekTo(long pos) { | ||
| 50 | + mPlayerControl.seekTo(pos); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + @Override | ||
| 54 | + public boolean isPlaying() { | ||
| 55 | + return mPlayerControl.isPlaying(); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Override | ||
| 59 | + public int getBufferedPercentage() { | ||
| 60 | + return mPlayerControl.getBufferedPercentage(); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Override | ||
| 64 | + public void startFullScreen() { | ||
| 65 | + mPlayerControl.startFullScreen(); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Override | ||
| 69 | + public void stopFullScreen() { | ||
| 70 | + mPlayerControl.stopFullScreen(); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @Override | ||
| 74 | + public boolean isFullScreen() { | ||
| 75 | + return mPlayerControl.isFullScreen(); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 设置是否静音 | ||
| 80 | + * | ||
| 81 | + * @param isMute | ||
| 82 | + */ | ||
| 83 | + @Override | ||
| 84 | + public void setMute(boolean isMute) { | ||
| 85 | + mPlayerControl.setMute(isMute); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + public boolean isMute() { | ||
| 90 | + return mPlayerControl.isMute(); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Override | ||
| 94 | + public void setScreenScaleType(int screenScaleType) { | ||
| 95 | + mPlayerControl.setScreenScaleType(screenScaleType); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + @Override | ||
| 99 | + public void setSpeed(float speed) { | ||
| 100 | + mPlayerControl.setSpeed(speed); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + @Override | ||
| 104 | + public long getTcpSpeed() { | ||
| 105 | + return mPlayerControl.getTcpSpeed(); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + @Override | ||
| 109 | + public void replay(boolean resetPosition) { | ||
| 110 | + mPlayerControl.replay(resetPosition); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + @Override | ||
| 114 | + public void setMirrorRotation(boolean enable) { | ||
| 115 | + mPlayerControl.setMirrorRotation(enable); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + @Override | ||
| 119 | + public Bitmap doScreenShot() { | ||
| 120 | + return mPlayerControl.doScreenShot(); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + @Override | ||
| 124 | + public int[] getVideoSize() { | ||
| 125 | + return mPlayerControl.getVideoSize(); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Override | ||
| 129 | + public void setRotation(float rotation) { | ||
| 130 | + mPlayerControl.setRotation(rotation); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + @Override | ||
| 134 | + public void startTinyScreen() { | ||
| 135 | + mPlayerControl.startTinyScreen(); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + @Override | ||
| 139 | + public void stopTinyScreen() { | ||
| 140 | + mPlayerControl.stopTinyScreen(); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + @Override | ||
| 144 | + public boolean isTinyScreen() { | ||
| 145 | + return mPlayerControl.isTinyScreen(); | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + /** | ||
| 149 | + * 播放和暂停 | ||
| 150 | + */ | ||
| 151 | + public void togglePlay() { | ||
| 152 | + if (isPlaying()) { | ||
| 153 | + pause(); | ||
| 154 | + } else { | ||
| 155 | + start(); | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + /** | ||
| 160 | + * 横竖屏切换,会旋转屏幕 | ||
| 161 | + */ | ||
| 162 | + public void toggleFullScreen(Activity activity) { | ||
| 163 | + if (activity == null || activity.isFinishing()) | ||
| 164 | + return; | ||
| 165 | + if (isFullScreen()) { | ||
| 166 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | ||
| 167 | + stopFullScreen(); | ||
| 168 | + } else { | ||
| 169 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); | ||
| 170 | + startFullScreen(); | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + /** | ||
| 175 | + * 横竖屏切换,不会旋转屏幕 | ||
| 176 | + */ | ||
| 177 | + public void toggleFullScreen() { | ||
| 178 | + if (isFullScreen()) { | ||
| 179 | + stopFullScreen(); | ||
| 180 | + } else { | ||
| 181 | + startFullScreen(); | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + /** | ||
| 186 | + * 横竖屏切换,根据适配宽高决定是否旋转屏幕 | ||
| 187 | + */ | ||
| 188 | + public void toggleFullScreenByVideoSize(Activity activity) { | ||
| 189 | + if (activity == null || activity.isFinishing()) | ||
| 190 | + return; | ||
| 191 | + int[] size = getVideoSize(); | ||
| 192 | + int width = size[0]; | ||
| 193 | + int height = size[1]; | ||
| 194 | + if (isFullScreen()) { | ||
| 195 | + stopFullScreen(); | ||
| 196 | + if (width > height) { | ||
| 197 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | ||
| 198 | + } | ||
| 199 | + } else { | ||
| 200 | + startFullScreen(); | ||
| 201 | + if (width > height) { | ||
| 202 | + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + @Override | ||
| 208 | + public void startFadeOut() { | ||
| 209 | + mController.startFadeOut(); | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + @Override | ||
| 213 | + public void stopFadeOut() { | ||
| 214 | + mController.stopFadeOut(); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + @Override | ||
| 218 | + public boolean isShowing() { | ||
| 219 | + return mController.isShowing(); | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + @Override | ||
| 223 | + public void setLocked(boolean locked) { | ||
| 224 | + mController.setLocked(locked); | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + @Override | ||
| 228 | + public boolean isLocked() { | ||
| 229 | + return mController.isLocked(); | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + @Override | ||
| 233 | + public void startProgress() { | ||
| 234 | + mController.startProgress(); | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + @Override | ||
| 238 | + public void stopProgress() { | ||
| 239 | + mController.stopProgress(); | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + @Override | ||
| 243 | + public void hide() { | ||
| 244 | + mController.hide(); | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + @Override | ||
| 248 | + public void show() { | ||
| 249 | + mController.show(); | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + @Override | ||
| 253 | + public boolean hasCutout() { | ||
| 254 | + return mController.hasCutout(); | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + @Override | ||
| 258 | + public int getCutoutHeight() { | ||
| 259 | + return mController.getCutoutHeight(); | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + /** | ||
| 263 | + * 切换锁定状态 | ||
| 264 | + */ | ||
| 265 | + public void toggleLockState() { | ||
| 266 | + setLocked(!isLocked()); | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | + | ||
| 270 | + /** | ||
| 271 | + * 切换显示/隐藏状态 | ||
| 272 | + */ | ||
| 273 | + public void toggleShowState() { | ||
| 274 | + if (isShowing()) { | ||
| 275 | + hide(); | ||
| 276 | + } else { | ||
| 277 | + show(); | ||
| 278 | + } | ||
| 279 | + } | ||
| 280 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/GestureVideoController.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.media.AudioManager; | ||
| 6 | +import android.util.AttributeSet; | ||
| 7 | +import android.view.GestureDetector; | ||
| 8 | +import android.view.MotionEvent; | ||
| 9 | +import android.view.View; | ||
| 10 | +import android.view.Window; | ||
| 11 | +import android.view.WindowManager; | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 15 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 16 | + | ||
| 17 | +import java.util.Map; | ||
| 18 | + | ||
| 19 | +import androidx.annotation.NonNull; | ||
| 20 | +import androidx.annotation.Nullable; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * 包含手势操作的VideoController | ||
| 24 | + */ | ||
| 25 | + | ||
| 26 | +public abstract class GestureVideoController extends BaseVideoController implements | ||
| 27 | + GestureDetector.OnGestureListener, | ||
| 28 | + GestureDetector.OnDoubleTapListener, | ||
| 29 | + View.OnTouchListener { | ||
| 30 | + | ||
| 31 | + private GestureDetector mGestureDetector; | ||
| 32 | + private AudioManager mAudioManager; | ||
| 33 | + private boolean mIsGestureEnabled = true; | ||
| 34 | + private int mStreamVolume; | ||
| 35 | + private float mBrightness; | ||
| 36 | + private int mPosition; | ||
| 37 | + private boolean mNeedSeek; | ||
| 38 | + private boolean mFirstTouch; | ||
| 39 | + private boolean mChangePosition; | ||
| 40 | + private boolean mChangeBrightness; | ||
| 41 | + private boolean mChangeVolume; | ||
| 42 | + | ||
| 43 | + private boolean mCanChangePosition = true; | ||
| 44 | + | ||
| 45 | + private boolean mEnableInNormal; | ||
| 46 | + | ||
| 47 | + private boolean mCanSlide; | ||
| 48 | + | ||
| 49 | + private int mCurPlayState; | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + public GestureVideoController(@NonNull Context context) { | ||
| 53 | + super(context); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public GestureVideoController(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 57 | + super(context, attrs); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + public GestureVideoController(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | ||
| 61 | + super(context, attrs, defStyleAttr); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + @Override | ||
| 65 | + protected void initView() { | ||
| 66 | + super.initView(); | ||
| 67 | + mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); | ||
| 68 | + mGestureDetector = new GestureDetector(getContext(), this); | ||
| 69 | + setOnTouchListener(this); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + /** | ||
| 73 | + * 设置是否可以滑动调节进度,默认可以 | ||
| 74 | + */ | ||
| 75 | + public void setCanChangePosition(boolean canChangePosition) { | ||
| 76 | + mCanChangePosition = canChangePosition; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 是否在竖屏模式下开始手势控制,默认关闭 | ||
| 81 | + */ | ||
| 82 | + public void setEnableInNormal(boolean enableInNormal) { | ||
| 83 | + mEnableInNormal = enableInNormal; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + /** | ||
| 87 | + * 是否开启手势空控制,默认开启,关闭之后,双击播放暂停以及手势调节进度,音量,亮度功能将关闭 | ||
| 88 | + */ | ||
| 89 | + public void setGestureEnabled(boolean gestureEnabled) { | ||
| 90 | + mIsGestureEnabled = gestureEnabled; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Override | ||
| 94 | + public void setPlayerState(int playerState) { | ||
| 95 | + super.setPlayerState(playerState); | ||
| 96 | + if (playerState == CNVideoView.PLAYER_NORMAL) { | ||
| 97 | + mCanSlide = mEnableInNormal; | ||
| 98 | + } else if (playerState == CNVideoView.PLAYER_FULL_SCREEN) { | ||
| 99 | + mCanSlide = true; | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + @Override | ||
| 104 | + public void setPlayState(int playState) { | ||
| 105 | + super.setPlayState(playState); | ||
| 106 | + mCurPlayState = playState; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + private boolean isInPlaybackState() { | ||
| 110 | + return mControlWrapper != null | ||
| 111 | + && mCurPlayState != CNVideoView.STATE_ERROR | ||
| 112 | + && mCurPlayState != CNVideoView.STATE_IDLE | ||
| 113 | + && mCurPlayState != CNVideoView.STATE_PREPARING | ||
| 114 | + && mCurPlayState != CNVideoView.STATE_PREPARED | ||
| 115 | + && mCurPlayState != CNVideoView.STATE_START_ABORT | ||
| 116 | + && mCurPlayState != CNVideoView.STATE_PLAYBACK_COMPLETED; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + @Override | ||
| 120 | + public boolean onTouch(View v, MotionEvent event) { | ||
| 121 | + return mGestureDetector.onTouchEvent(event); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + /** | ||
| 125 | + * 手指按下的瞬间 | ||
| 126 | + */ | ||
| 127 | + @Override | ||
| 128 | + public boolean onDown(MotionEvent e) { | ||
| 129 | + if (!isInPlaybackState() //不处于播放状态 | ||
| 130 | + || !mIsGestureEnabled //关闭了手势 | ||
| 131 | + || PlayerUtils.isEdge(getContext(), e)) //处于屏幕边沿 | ||
| 132 | + return true; | ||
| 133 | + mStreamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); | ||
| 134 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 135 | + if (activity == null) { | ||
| 136 | + mBrightness = 0; | ||
| 137 | + } else { | ||
| 138 | + mBrightness = activity.getWindow().getAttributes().screenBrightness; | ||
| 139 | + } | ||
| 140 | + mFirstTouch = true; | ||
| 141 | + mChangePosition = false; | ||
| 142 | + mChangeBrightness = false; | ||
| 143 | + mChangeVolume = false; | ||
| 144 | + return true; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + /** | ||
| 148 | + * 单击 | ||
| 149 | + */ | ||
| 150 | + @Override | ||
| 151 | + public boolean onSingleTapConfirmed(MotionEvent e) { | ||
| 152 | + if (isInPlaybackState()) { | ||
| 153 | + mControlWrapper.toggleShowState(); | ||
| 154 | + } | ||
| 155 | + return true; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + /** | ||
| 159 | + * 双击 | ||
| 160 | + */ | ||
| 161 | + @Override | ||
| 162 | + public boolean onDoubleTap(MotionEvent e) { | ||
| 163 | + if (!isLocked() && isInPlaybackState()) togglePlay(); | ||
| 164 | + return true; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + /** | ||
| 168 | + * 在屏幕上滑动 | ||
| 169 | + */ | ||
| 170 | + @Override | ||
| 171 | + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { | ||
| 172 | + if (!isInPlaybackState() //不处于播放状态 | ||
| 173 | + || !mIsGestureEnabled //关闭了手势 | ||
| 174 | + || !mCanSlide //关闭了滑动手势 | ||
| 175 | + || isLocked() //锁住了屏幕 | ||
| 176 | + || PlayerUtils.isEdge(getContext(), e1)) //处于屏幕边沿 | ||
| 177 | + return true; | ||
| 178 | + float deltaX = e1.getX() - e2.getX(); | ||
| 179 | + float deltaY = e1.getY() - e2.getY(); | ||
| 180 | + if (mFirstTouch) { | ||
| 181 | + mChangePosition = Math.abs(distanceX) >= Math.abs(distanceY); | ||
| 182 | + if (!mChangePosition) { | ||
| 183 | + //半屏宽度 | ||
| 184 | + int halfScreen = PlayerUtils.getScreenWidth(getContext(), true) / 2; | ||
| 185 | + if (e2.getX() > halfScreen) { | ||
| 186 | + mChangeVolume = true; | ||
| 187 | + } else { | ||
| 188 | + mChangeBrightness = true; | ||
| 189 | + } | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + if (mChangePosition) { | ||
| 193 | + //根据用户设置是否可以滑动调节进度来决定最终是否可以滑动调节进度 | ||
| 194 | + mChangePosition = mCanChangePosition; | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + if (mChangePosition || mChangeBrightness || mChangeVolume) { | ||
| 198 | + for (Map.Entry<IControlComponent, Boolean> next : mControlComponents.entrySet()) { | ||
| 199 | + IControlComponent component = next.getKey(); | ||
| 200 | + if (component instanceof IGestureComponent) { | ||
| 201 | + ((IGestureComponent) component).onStartSlide(); | ||
| 202 | + } | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + mFirstTouch = false; | ||
| 206 | + } | ||
| 207 | + if (mChangePosition) { | ||
| 208 | + slideToChangePosition(deltaX); | ||
| 209 | + } else if (mChangeBrightness) { | ||
| 210 | + slideToChangeBrightness(deltaY); | ||
| 211 | + } else if (mChangeVolume) { | ||
| 212 | + slideToChangeVolume(deltaY); | ||
| 213 | + } | ||
| 214 | + return true; | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + protected void slideToChangePosition(float deltaX) { | ||
| 218 | + deltaX = -deltaX; | ||
| 219 | + int width = getMeasuredWidth(); | ||
| 220 | + int duration = (int) mControlWrapper.getDuration(); | ||
| 221 | + int currentPosition = (int) mControlWrapper.getCurrentPosition(); | ||
| 222 | + int position = (int) (deltaX / width * 120000 + currentPosition); | ||
| 223 | + if (position > duration) position = duration; | ||
| 224 | + if (position < 0) position = 0; | ||
| 225 | + for (Map.Entry<IControlComponent, Boolean> next : mControlComponents.entrySet()) { | ||
| 226 | + IControlComponent component = next.getKey(); | ||
| 227 | + if (component instanceof IGestureComponent) { | ||
| 228 | + ((IGestureComponent) component).onPositionChange(position, currentPosition, duration); | ||
| 229 | + } | ||
| 230 | + } | ||
| 231 | + mPosition = position; | ||
| 232 | + mNeedSeek = true; | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + protected void slideToChangeBrightness(float deltaY) { | ||
| 236 | + Activity activity = PlayerUtils.scanForActivity(getContext()); | ||
| 237 | + if (activity == null) return; | ||
| 238 | + Window window = activity.getWindow(); | ||
| 239 | + WindowManager.LayoutParams attributes = window.getAttributes(); | ||
| 240 | + int height = getMeasuredHeight(); | ||
| 241 | + if (mBrightness == -1.0f) mBrightness = 0.5f; | ||
| 242 | + float brightness = deltaY * 2 / height * 1.0f + mBrightness; | ||
| 243 | + if (brightness < 0) { | ||
| 244 | + brightness = 0f; | ||
| 245 | + } | ||
| 246 | + if (brightness > 1.0f) brightness = 1.0f; | ||
| 247 | + int percent = (int) (brightness * 100); | ||
| 248 | + attributes.screenBrightness = brightness; | ||
| 249 | + window.setAttributes(attributes); | ||
| 250 | + for (Map.Entry<IControlComponent, Boolean> next : mControlComponents.entrySet()) { | ||
| 251 | + IControlComponent component = next.getKey(); | ||
| 252 | + if (component instanceof IGestureComponent) { | ||
| 253 | + ((IGestureComponent) component).onBrightnessChange(percent); | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + protected void slideToChangeVolume(float deltaY) { | ||
| 259 | + int streamMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); | ||
| 260 | + int height = getMeasuredHeight(); | ||
| 261 | + float deltaV = deltaY * 2 / height * streamMaxVolume; | ||
| 262 | + float index = mStreamVolume + deltaV; | ||
| 263 | + if (index > streamMaxVolume) index = streamMaxVolume; | ||
| 264 | + if (index < 0) index = 0; | ||
| 265 | + int percent = (int) (index / streamMaxVolume * 100); | ||
| 266 | + mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int) index, 0); | ||
| 267 | + for (Map.Entry<IControlComponent, Boolean> next : mControlComponents.entrySet()) { | ||
| 268 | + IControlComponent component = next.getKey(); | ||
| 269 | + if (component instanceof IGestureComponent) { | ||
| 270 | + ((IGestureComponent) component).onVolumeChange(percent); | ||
| 271 | + } | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + @Override | ||
| 276 | + public boolean onTouchEvent(MotionEvent event) { | ||
| 277 | + boolean detectedUp = event.getAction() == MotionEvent.ACTION_UP; | ||
| 278 | + if (!mGestureDetector.onTouchEvent(event) && detectedUp) { | ||
| 279 | + for (Map.Entry<IControlComponent, Boolean> next : mControlComponents.entrySet()) { | ||
| 280 | + IControlComponent component = next.getKey(); | ||
| 281 | + if (component instanceof IGestureComponent) { | ||
| 282 | + ((IGestureComponent) component).onStopSlide(); | ||
| 283 | + } | ||
| 284 | + } | ||
| 285 | + if (mNeedSeek) { | ||
| 286 | + mControlWrapper.seekTo(mPosition); | ||
| 287 | + mNeedSeek = false; | ||
| 288 | + } | ||
| 289 | + } | ||
| 290 | + return super.onTouchEvent(event); | ||
| 291 | + } | ||
| 292 | + | ||
| 293 | + @Override | ||
| 294 | + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { | ||
| 295 | + return false; | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + @Override | ||
| 299 | + public void onLongPress(MotionEvent e) { | ||
| 300 | + | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + @Override | ||
| 304 | + public void onShowPress(MotionEvent e) { | ||
| 305 | + | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + @Override | ||
| 309 | + public boolean onDoubleTapEvent(MotionEvent e) { | ||
| 310 | + return false; | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + | ||
| 314 | + @Override | ||
| 315 | + public boolean onSingleTapUp(MotionEvent e) { | ||
| 316 | + return false; | ||
| 317 | + } | ||
| 318 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/IControlComponent.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +import android.view.View; | ||
| 4 | +import android.view.animation.Animation; | ||
| 5 | + | ||
| 6 | +import androidx.annotation.NonNull; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 视频控制组件接口 | ||
| 10 | + */ | ||
| 11 | +public interface IControlComponent { | ||
| 12 | + | ||
| 13 | + void attach(@NonNull ControlWrapper controlWrapper); | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 获取当前控制组件对应的View | ||
| 17 | + * | ||
| 18 | + * @return | ||
| 19 | + */ | ||
| 20 | + View getView(); | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * UI显示与隐藏 | ||
| 24 | + * | ||
| 25 | + * @param isVisible | ||
| 26 | + * @param anim | ||
| 27 | + */ | ||
| 28 | + void onVisibilityChanged(boolean isVisible, Animation anim); | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 播放状态改变 | ||
| 32 | + * | ||
| 33 | + * @param playState | ||
| 34 | + */ | ||
| 35 | + void onPlayStateChanged(int playState); | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 播放器状态改变 | ||
| 39 | + * | ||
| 40 | + * @param playerState | ||
| 41 | + */ | ||
| 42 | + void onPlayerStateChanged(int playerState); | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 设置进度 | ||
| 46 | + * | ||
| 47 | + * @param duration | ||
| 48 | + * @param position | ||
| 49 | + */ | ||
| 50 | + void setProgress(int duration, int position); | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 设置是否锁定 | ||
| 54 | + * | ||
| 55 | + * @param isLocked | ||
| 56 | + */ | ||
| 57 | + void onLockStateChanged(boolean isLocked); | ||
| 58 | + | ||
| 59 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/IGestureComponent.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +public interface IGestureComponent extends IControlComponent { | ||
| 4 | + /** | ||
| 5 | + * 开始滑动 | ||
| 6 | + */ | ||
| 7 | + void onStartSlide(); | ||
| 8 | + | ||
| 9 | + /** | ||
| 10 | + * 结束滑动 | ||
| 11 | + */ | ||
| 12 | + void onStopSlide(); | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 滑动调整进度 | ||
| 16 | + * @param slidePosition 滑动进度 | ||
| 17 | + * @param currentPosition 当前播放进度 | ||
| 18 | + * @param duration 视频总长度 | ||
| 19 | + */ | ||
| 20 | + void onPositionChange(int slidePosition, int currentPosition, int duration); | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 滑动调整亮度 | ||
| 24 | + * @param percent 亮度百分比 | ||
| 25 | + */ | ||
| 26 | + void onBrightnessChange(int percent); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 滑动调整音量 | ||
| 30 | + * @param percent 音量百分比 | ||
| 31 | + */ | ||
| 32 | + void onVolumeChange(int percent); | ||
| 33 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/IVideoController.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +public interface IVideoController { | ||
| 4 | + /** | ||
| 5 | + * 开始控制视图自动隐藏倒计时 | ||
| 6 | + */ | ||
| 7 | + void startFadeOut(); | ||
| 8 | + | ||
| 9 | + /** | ||
| 10 | + * 取消控制视图自动隐藏倒计时 | ||
| 11 | + */ | ||
| 12 | + void stopFadeOut(); | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 控制视图是否处于显示状态 | ||
| 16 | + */ | ||
| 17 | + boolean isShowing(); | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 设置锁定状态 | ||
| 21 | + * @param locked 是否锁定 | ||
| 22 | + */ | ||
| 23 | + void setLocked(boolean locked); | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 是否处于锁定状态 | ||
| 27 | + */ | ||
| 28 | + boolean isLocked(); | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 开始刷新进度 | ||
| 32 | + */ | ||
| 33 | + void startProgress(); | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 停止刷新进度 | ||
| 37 | + */ | ||
| 38 | + void stopProgress(); | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 显示控制视图 | ||
| 42 | + */ | ||
| 43 | + void hide(); | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 隐藏控制视图 | ||
| 47 | + */ | ||
| 48 | + void show(); | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 是否需要适配刘海 | ||
| 52 | + */ | ||
| 53 | + boolean hasCutout(); | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 获取刘海的高度 | ||
| 57 | + */ | ||
| 58 | + int getCutoutHeight(); | ||
| 59 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/MediaPlayerControl.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +import android.graphics.Bitmap; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 播放器控制接口 | ||
| 7 | + */ | ||
| 8 | +public interface MediaPlayerControl { | ||
| 9 | + int getMediaCount(); | ||
| 10 | + | ||
| 11 | + void start(); | ||
| 12 | + | ||
| 13 | + void pause(); | ||
| 14 | + | ||
| 15 | + long getDuration(); | ||
| 16 | + | ||
| 17 | + long getCurrentPosition(); | ||
| 18 | + | ||
| 19 | + void seekTo(long pos); | ||
| 20 | + | ||
| 21 | + boolean isPlaying(); | ||
| 22 | + | ||
| 23 | + int getBufferedPercentage(); | ||
| 24 | + | ||
| 25 | + void startFullScreen(); | ||
| 26 | + | ||
| 27 | + void stopFullScreen(); | ||
| 28 | + | ||
| 29 | + boolean isFullScreen(); | ||
| 30 | + | ||
| 31 | + void setMute(boolean isMute); | ||
| 32 | + | ||
| 33 | + boolean isMute(); | ||
| 34 | + | ||
| 35 | + void setScreenScaleType(int screenScaleType); | ||
| 36 | + | ||
| 37 | + void setSpeed(float speed); | ||
| 38 | + | ||
| 39 | + long getTcpSpeed(); | ||
| 40 | + | ||
| 41 | + void replay(boolean resetPosition); | ||
| 42 | + | ||
| 43 | + void setMirrorRotation(boolean enable); | ||
| 44 | + | ||
| 45 | + Bitmap doScreenShot(); | ||
| 46 | + | ||
| 47 | + int[] getVideoSize(); | ||
| 48 | + | ||
| 49 | + void setRotation(float rotation); | ||
| 50 | + | ||
| 51 | + void startTinyScreen(); | ||
| 52 | + | ||
| 53 | + void stopTinyScreen(); | ||
| 54 | + | ||
| 55 | + boolean isTinyScreen(); | ||
| 56 | +} | ||
| 0 | \ No newline at end of file | 57 | \ No newline at end of file |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/OrientationHelper.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.view.OrientationEventListener; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 设备方向监听 | ||
| 8 | + */ | ||
| 9 | +public class OrientationHelper extends OrientationEventListener { | ||
| 10 | + | ||
| 11 | + private long mLastTime; | ||
| 12 | + | ||
| 13 | + private OnOrientationChangeListener mOnOrientationChangeListener; | ||
| 14 | + | ||
| 15 | + public OrientationHelper(Context context) { | ||
| 16 | + super(context); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + public void onOrientationChanged(int orientation) { | ||
| 21 | + long currentTime = System.currentTimeMillis(); | ||
| 22 | + if (currentTime - mLastTime < 300) return;//300毫秒检测一次 | ||
| 23 | + if (mOnOrientationChangeListener != null) { | ||
| 24 | + mOnOrientationChangeListener.onOrientationChanged(orientation); | ||
| 25 | + } | ||
| 26 | + mLastTime = currentTime; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + public interface OnOrientationChangeListener { | ||
| 31 | + void onOrientationChanged(int orientation); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public void setOnOrientationChangeListener(OnOrientationChangeListener onOrientationChangeListener) { | ||
| 35 | + mOnOrientationChangeListener = onOrientationChangeListener; | ||
| 36 | + } | ||
| 37 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/controller/StandardVideoController.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.controller; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.content.pm.ActivityInfo; | ||
| 5 | +import android.util.AttributeSet; | ||
| 6 | +import android.view.View; | ||
| 7 | +import android.view.ViewGroup; | ||
| 8 | +import android.view.animation.Animation; | ||
| 9 | +import android.widget.FrameLayout; | ||
| 10 | +import android.widget.ImageView; | ||
| 11 | +import android.widget.ProgressBar; | ||
| 12 | +import android.widget.Toast; | ||
| 13 | + | ||
| 14 | +import com.cnlive.cloud.video.R; | ||
| 15 | +import com.libs.video.video.exoplayer.component.DefaultCompleteView; | ||
| 16 | +import com.libs.video.video.exoplayer.component.DefaultErrorView; | ||
| 17 | +import com.libs.video.video.exoplayer.component.GestureView; | ||
| 18 | +import com.libs.video.video.exoplayer.component.LiveControlView; | ||
| 19 | +import com.libs.video.video.exoplayer.component.DefaultPrepareView; | ||
| 20 | +import com.libs.video.video.exoplayer.component.DefaultTitleView; | ||
| 21 | +import com.libs.video.video.exoplayer.component.DefaultVodControlView; | ||
| 22 | +import com.libs.video.video.exoplayer.play.CNVideoView; | ||
| 23 | +import com.libs.video.video.exoplayer.util.PlayerUtils; | ||
| 24 | + | ||
| 25 | +import androidx.annotation.AttrRes; | ||
| 26 | +import androidx.annotation.NonNull; | ||
| 27 | +import androidx.annotation.Nullable; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * 直播/点播控制器 | ||
| 31 | + */ | ||
| 32 | + | ||
| 33 | +public class StandardVideoController extends GestureVideoController implements View.OnClickListener { | ||
| 34 | + | ||
| 35 | + protected ImageView mLockButton;//锁屏按钮 | ||
| 36 | + | ||
| 37 | + protected ProgressBar mLoadingProgress; | ||
| 38 | + | ||
| 39 | + public StandardVideoController(@NonNull Context context) { | ||
| 40 | + this(context, null); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public StandardVideoController(@NonNull Context context, @Nullable AttributeSet attrs) { | ||
| 44 | + this(context, attrs, 0); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public StandardVideoController(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { | ||
| 48 | + super(context, attrs, defStyleAttr); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + @Override | ||
| 52 | + protected int getLayoutId() { | ||
| 53 | + return R.layout.cnvideo_layout_standard_controller; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Override | ||
| 57 | + protected void initView() { | ||
| 58 | + super.initView(); | ||
| 59 | + mLockButton = findViewById(R.id.lock); | ||
| 60 | + mLockButton.setOnClickListener(this); | ||
| 61 | + mLoadingProgress = findViewById(R.id.loading); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 快速添加各个组件 | ||
| 66 | + * | ||
| 67 | + * @param title 标题 | ||
| 68 | + * @param isLive 是否为直播 | ||
| 69 | + */ | ||
| 70 | + public void addDefaultControlComponent(String title, boolean isLive) { | ||
| 71 | + DefaultCompleteView completeView = new DefaultCompleteView(getContext()); | ||
| 72 | + DefaultErrorView errorView = new DefaultErrorView(getContext()); | ||
| 73 | + DefaultPrepareView prepareView = new DefaultPrepareView(getContext()); | ||
| 74 | + prepareView.setClickStart(); | ||
| 75 | + DefaultTitleView titleView = new DefaultTitleView(getContext()); | ||
| 76 | + titleView.setTitle(title); | ||
| 77 | + addControlComponent(completeView, errorView, prepareView, titleView); | ||
| 78 | + if (isLive) { | ||
| 79 | + addControlComponent(new LiveControlView(getContext())); | ||
| 80 | + } else { | ||
| 81 | + addControlComponent(new DefaultVodControlView(getContext())); | ||
| 82 | + } | ||
| 83 | + addControlComponent(new GestureView(getContext())); | ||
| 84 | + setCanChangePosition(!isLive); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + @Override | ||
| 88 | + public void onClick(View v) { | ||
| 89 | + int i = v.getId(); | ||
| 90 | + if (i == R.id.lock) { | ||
| 91 | + mControlWrapper.toggleLockState(); | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + @Override | ||
| 96 | + protected void onLockStateChanged(boolean isLocked) { | ||
| 97 | + if (isLocked) { | ||
| 98 | + mLockButton.setSelected(true); | ||
| 99 | + Toast.makeText(getContext(), R.string.cnvideo_locked, Toast.LENGTH_SHORT).show(); | ||
| 100 | + } else { | ||
| 101 | + mLockButton.setSelected(false); | ||
| 102 | + Toast.makeText(getContext(), R.string.cnvideo_unlocked, Toast.LENGTH_SHORT).show(); | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + @Override | ||
| 107 | + protected void onVisibilityChanged(boolean isVisible, Animation anim) { | ||
| 108 | + if (mControlWrapper.isFullScreen()) { | ||
| 109 | + if (isVisible) { | ||
| 110 | + if (mLockButton.getVisibility() == GONE) { | ||
| 111 | + mLockButton.setVisibility(VISIBLE); | ||
| 112 | + if (anim != null) { | ||
| 113 | + mLockButton.startAnimation(anim); | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + } else { | ||
| 117 | + mLockButton.setVisibility(GONE); | ||
| 118 | + if (anim != null) { | ||
| 119 | + mLockButton.startAnimation(anim); | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + @Override | ||
| 126 | + protected void onPlayerStateChanged(int playerState) { | ||
| 127 | + super.onPlayerStateChanged(playerState); | ||
| 128 | + switch (playerState) { | ||
| 129 | + case CNVideoView.PLAYER_NORMAL: | ||
| 130 | + setLayoutParams(new FrameLayout.LayoutParams( | ||
| 131 | + ViewGroup.LayoutParams.MATCH_PARENT, | ||
| 132 | + ViewGroup.LayoutParams.MATCH_PARENT)); | ||
| 133 | + mLockButton.setVisibility(GONE); | ||
| 134 | + break; | ||
| 135 | + case CNVideoView.PLAYER_FULL_SCREEN: | ||
| 136 | + if (isShowing()) { | ||
| 137 | + mLockButton.setVisibility(VISIBLE); | ||
| 138 | + } else { | ||
| 139 | + mLockButton.setVisibility(GONE); | ||
| 140 | + } | ||
| 141 | + break; | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + if (mActivity != null && hasCutout()) { | ||
| 145 | + int orientation = mActivity.getRequestedOrientation(); | ||
| 146 | + int dp24 = PlayerUtils.dp2px(getContext(), 24); | ||
| 147 | + int cutoutHeight = getCutoutHeight(); | ||
| 148 | + if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { | ||
| 149 | + FrameLayout.LayoutParams lblp = (FrameLayout.LayoutParams) mLockButton.getLayoutParams(); | ||
| 150 | + lblp.setMargins(dp24, 0, dp24, 0); | ||
| 151 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { | ||
| 152 | + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mLockButton.getLayoutParams(); | ||
| 153 | + layoutParams.setMargins(dp24 + cutoutHeight, 0, dp24 + cutoutHeight, 0); | ||
| 154 | + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) { | ||
| 155 | + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mLockButton.getLayoutParams(); | ||
| 156 | + layoutParams.setMargins(dp24, 0, dp24, 0); | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + @Override | ||
| 163 | + protected void onPlayStateChanged(int playState) { | ||
| 164 | + super.onPlayStateChanged(playState); | ||
| 165 | + switch (playState) { | ||
| 166 | + //调用release方法会回到此状态 | ||
| 167 | + case CNVideoView.STATE_IDLE: | ||
| 168 | + mLockButton.setSelected(false); | ||
| 169 | + mLoadingProgress.setVisibility(GONE); | ||
| 170 | + break; | ||
| 171 | + case CNVideoView.STATE_PLAYING: | ||
| 172 | + case CNVideoView.STATE_PAUSED: | ||
| 173 | + case CNVideoView.STATE_PREPARED: | ||
| 174 | + case CNVideoView.STATE_ERROR: | ||
| 175 | + case CNVideoView.STATE_BUFFERED: | ||
| 176 | + mLoadingProgress.setVisibility(GONE); | ||
| 177 | + break; | ||
| 178 | + case CNVideoView.STATE_PREPARING: | ||
| 179 | + case CNVideoView.STATE_BUFFERING: | ||
| 180 | + mLoadingProgress.setVisibility(VISIBLE); | ||
| 181 | + break; | ||
| 182 | + case CNVideoView.STATE_PLAYBACK_COMPLETED: | ||
| 183 | + mLoadingProgress.setVisibility(GONE); | ||
| 184 | + mLockButton.setVisibility(GONE); | ||
| 185 | + mLockButton.setSelected(false); | ||
| 186 | + break; | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + @Override | ||
| 191 | + public boolean onBackPressed() { | ||
| 192 | + if (isLocked()) { | ||
| 193 | + show(); | ||
| 194 | + Toast.makeText(getContext(), R.string.cnvideo_lock_tip, Toast.LENGTH_SHORT).show(); | ||
| 195 | + return true; | ||
| 196 | + } | ||
| 197 | + if (mControlWrapper.isFullScreen()) { | ||
| 198 | + return stopFullScreen(); | ||
| 199 | + } | ||
| 200 | + return super.onBackPressed(); | ||
| 201 | + } | ||
| 202 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/play/AbstractPlayer.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.play; | ||
| 2 | + | ||
| 3 | +import android.content.res.AssetFileDescriptor; | ||
| 4 | +import android.view.Surface; | ||
| 5 | +import android.view.SurfaceHolder; | ||
| 6 | + | ||
| 7 | +import java.util.Map; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 抽象播放器 | ||
| 11 | + * Created by ShinnyYang | ||
| 12 | + */ | ||
| 13 | + | ||
| 14 | +public abstract class AbstractPlayer { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 开始渲染视频画面 | ||
| 18 | + */ | ||
| 19 | + public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 缓冲开始 | ||
| 23 | + */ | ||
| 24 | + public static final int MEDIA_INFO_BUFFERING_START = 701; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 缓冲结束 | ||
| 28 | + */ | ||
| 29 | + public static final int MEDIA_INFO_BUFFERING_END = 702; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 视频旋转信息 | ||
| 33 | + */ | ||
| 34 | + public static final int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 播放器事件回调 | ||
| 38 | + */ | ||
| 39 | + protected PlayerEventListener mPlayerEventListener; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 初始化播放器实例 | ||
| 43 | + */ | ||
| 44 | + public abstract void initPlayer(); | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 设置播放地址 | ||
| 48 | + * | ||
| 49 | + * @param path 播放地址 | ||
| 50 | + * @param headers 播放地址请求头 | ||
| 51 | + */ | ||
| 52 | + public abstract void setDataSource(String path, Map<String, String> headers); | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * 用于播放raw和asset里面的视频文件 | ||
| 56 | + */ | ||
| 57 | + public abstract void setDataSource(AssetFileDescriptor fd); | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * 播放 | ||
| 61 | + */ | ||
| 62 | + public abstract void start(); | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 暂停 | ||
| 66 | + */ | ||
| 67 | + public abstract void pause(); | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 停止 | ||
| 71 | + */ | ||
| 72 | + public abstract void stop(); | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * 准备开始播放(异步) | ||
| 76 | + */ | ||
| 77 | + public abstract void prepareAsync(); | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 重置播放器 | ||
| 81 | + */ | ||
| 82 | + public abstract void reset(); | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * 是否正在播放 | ||
| 86 | + */ | ||
| 87 | + public abstract boolean isPlaying(); | ||
| 88 | + | ||
| 89 | + /** | ||
| 90 | + * 调整进度 | ||
| 91 | + */ | ||
| 92 | + public abstract void seekTo(long time); | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + * 释放播放器 | ||
| 96 | + */ | ||
| 97 | + public abstract void release(); | ||
| 98 | + | ||
| 99 | + /** | ||
| 100 | + * 获取当前播放的位置 | ||
| 101 | + */ | ||
| 102 | + public abstract long getCurrentPosition(); | ||
| 103 | + | ||
| 104 | + /** | ||
| 105 | + * 获取视频总时长 | ||
| 106 | + */ | ||
| 107 | + public abstract long getDuration(); | ||
| 108 | + | ||
| 109 | + /** | ||
| 110 | + * 获取缓冲百分比 | ||
| 111 | + */ | ||
| 112 | + public abstract int getBufferedPercentage(); | ||
| 113 | + | ||
| 114 | + /** | ||
| 115 | + * 设置渲染视频的View,主要用于TextureView | ||
| 116 | + */ | ||
| 117 | + public abstract void setSurface(Surface surface); | ||
| 118 | + | ||
| 119 | + /** | ||
| 120 | + * 设置渲染视频的View,主要用于SurfaceView | ||
| 121 | + */ | ||
| 122 | + public abstract void setDisplay(SurfaceHolder holder); | ||
| 123 | + | ||
| 124 | + /** | ||
| 125 | + * 设置音量 | ||
| 126 | + */ | ||
| 127 | + public abstract void setVolume(float v1, float v2); | ||
| 128 | + | ||
| 129 | + /** | ||
| 130 | + * 设置是否循环播放 | ||
| 131 | + */ | ||
| 132 | + public abstract void setLooping(boolean isLooping); | ||
| 133 | + | ||
| 134 | + /** | ||
| 135 | + * 设置其他播放配置 | ||
| 136 | + */ | ||
| 137 | + public abstract void setOptions(); | ||
| 138 | + | ||
| 139 | + /** | ||
| 140 | + * 设置播放速度 | ||
| 141 | + */ | ||
| 142 | + public abstract void setSpeed(float speed); | ||
| 143 | + | ||
| 144 | + /** | ||
| 145 | + * 获取当前缓冲的网速 | ||
| 146 | + */ | ||
| 147 | + public abstract long getTcpSpeed(); | ||
| 148 | + | ||
| 149 | + /** | ||
| 150 | + * 绑定VideoView | ||
| 151 | + */ | ||
| 152 | + public void setPlayerEventListener(PlayerEventListener playerEventListener) { | ||
| 153 | + this.mPlayerEventListener = playerEventListener; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + public interface PlayerEventListener { | ||
| 157 | + | ||
| 158 | + void onError(); | ||
| 159 | + | ||
| 160 | + void onCompletion(); | ||
| 161 | + | ||
| 162 | + void onInfo(int what, int extra); | ||
| 163 | + | ||
| 164 | + void onPrepared(); | ||
| 165 | + | ||
| 166 | + void onVideoSizeChanged(int width, int height); | ||
| 167 | + | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/play/AndroidMediaPlayer.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.play; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.content.res.AssetFileDescriptor; | ||
| 5 | +import android.media.AudioManager; | ||
| 6 | +import android.media.MediaPlayer; | ||
| 7 | +import android.net.Uri; | ||
| 8 | +import android.os.Build; | ||
| 9 | +import android.view.Surface; | ||
| 10 | +import android.view.SurfaceHolder; | ||
| 11 | + | ||
| 12 | +import java.util.Map; | ||
| 13 | + | ||
| 14 | +public class AndroidMediaPlayer extends AbstractPlayer { | ||
| 15 | + | ||
| 16 | + protected MediaPlayer mMediaPlayer; | ||
| 17 | + private int mBufferedPercent; | ||
| 18 | + private Context mAppContext; | ||
| 19 | + private boolean mIsPreparing; | ||
| 20 | + | ||
| 21 | + public AndroidMediaPlayer(Context context) { | ||
| 22 | + mAppContext = context.getApplicationContext(); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void initPlayer() { | ||
| 27 | + mMediaPlayer = new MediaPlayer(); | ||
| 28 | + setOptions(); | ||
| 29 | + mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); | ||
| 30 | + mMediaPlayer.setOnErrorListener(onErrorListener); | ||
| 31 | + mMediaPlayer.setOnCompletionListener(onCompletionListener); | ||
| 32 | + mMediaPlayer.setOnInfoListener(onInfoListener); | ||
| 33 | + mMediaPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener); | ||
| 34 | + mMediaPlayer.setOnPreparedListener(onPreparedListener); | ||
| 35 | + mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + public void setDataSource(String path, Map<String, String> headers) { | ||
| 40 | + try { | ||
| 41 | + mMediaPlayer.setDataSource(mAppContext, Uri.parse(path), headers); | ||
| 42 | + } catch (Exception e) { | ||
| 43 | + mPlayerEventListener.onError(); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + @Override | ||
| 48 | + public void setDataSource(AssetFileDescriptor fd) { | ||
| 49 | + try { | ||
| 50 | + mMediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); | ||
| 51 | + } catch (Exception e) { | ||
| 52 | + mPlayerEventListener.onError(); | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Override | ||
| 57 | + public void start() { | ||
| 58 | + try { | ||
| 59 | + mMediaPlayer.start(); | ||
| 60 | + } catch (IllegalStateException e) { | ||
| 61 | + mPlayerEventListener.onError(); | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + public void pause() { | ||
| 67 | + try { | ||
| 68 | + mMediaPlayer.pause(); | ||
| 69 | + } catch (IllegalStateException e) { | ||
| 70 | + mPlayerEventListener.onError(); | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + @Override | ||
| 75 | + public void stop() { | ||
| 76 | + try { | ||
| 77 | + mMediaPlayer.stop(); | ||
| 78 | + } catch (IllegalStateException e) { | ||
| 79 | + mPlayerEventListener.onError(); | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Override | ||
| 84 | + public void prepareAsync() { | ||
| 85 | + try { | ||
| 86 | + mIsPreparing = true; | ||
| 87 | + mMediaPlayer.prepareAsync(); | ||
| 88 | + } catch (IllegalStateException e) { | ||
| 89 | + mPlayerEventListener.onError(); | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Override | ||
| 94 | + public void reset() { | ||
| 95 | + mMediaPlayer.reset(); | ||
| 96 | + mMediaPlayer.setSurface(null); | ||
| 97 | + mMediaPlayer.setDisplay(null); | ||
| 98 | + mMediaPlayer.setVolume(1, 1); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + @Override | ||
| 102 | + public boolean isPlaying() { | ||
| 103 | + return mMediaPlayer.isPlaying(); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + @Override | ||
| 107 | + public void seekTo(long time) { | ||
| 108 | + try { | ||
| 109 | + mMediaPlayer.seekTo((int) time); | ||
| 110 | + } catch (IllegalStateException e) { | ||
| 111 | + mPlayerEventListener.onError(); | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + @Override | ||
| 116 | + public void release() { | ||
| 117 | + mMediaPlayer.setOnErrorListener(null); | ||
| 118 | + mMediaPlayer.setOnCompletionListener(null); | ||
| 119 | + mMediaPlayer.setOnInfoListener(null); | ||
| 120 | + mMediaPlayer.setOnBufferingUpdateListener(null); | ||
| 121 | + mMediaPlayer.setOnPreparedListener(null); | ||
| 122 | + mMediaPlayer.setOnVideoSizeChangedListener(null); | ||
| 123 | + new Thread() { | ||
| 124 | + @Override | ||
| 125 | + public void run() { | ||
| 126 | + try { | ||
| 127 | + mMediaPlayer.release(); | ||
| 128 | + } catch (Exception e) { | ||
| 129 | + e.printStackTrace(); | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + }.start(); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + @Override | ||
| 136 | + public long getCurrentPosition() { | ||
| 137 | + return mMediaPlayer.getCurrentPosition(); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + @Override | ||
| 141 | + public long getDuration() { | ||
| 142 | + return mMediaPlayer.getDuration(); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + @Override | ||
| 146 | + public int getBufferedPercentage() { | ||
| 147 | + return mBufferedPercent; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + @Override | ||
| 151 | + public void setSurface(Surface surface) { | ||
| 152 | + try { | ||
| 153 | + mMediaPlayer.setSurface(surface); | ||
| 154 | + } catch (Exception e) { | ||
| 155 | + mPlayerEventListener.onError(); | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + @Override | ||
| 160 | + public void setDisplay(SurfaceHolder holder) { | ||
| 161 | + try { | ||
| 162 | + mMediaPlayer.setDisplay(holder); | ||
| 163 | + } catch (Exception e) { | ||
| 164 | + mPlayerEventListener.onError(); | ||
| 165 | + } | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + @Override | ||
| 169 | + public void setVolume(float v1, float v2) { | ||
| 170 | + mMediaPlayer.setVolume(v1, v2); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + @Override | ||
| 174 | + public void setLooping(boolean isLooping) { | ||
| 175 | + mMediaPlayer.setLooping(isLooping); | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + @Override | ||
| 179 | + public void setOptions() { | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + @Override | ||
| 183 | + public void setSpeed(float speed) { | ||
| 184 | + // only support above Android M | ||
| 185 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
| 186 | + try { | ||
| 187 | + mMediaPlayer.setPlaybackParams(mMediaPlayer.getPlaybackParams().setSpeed(speed)); | ||
| 188 | + } catch (Exception e) { | ||
| 189 | + mPlayerEventListener.onError(); | ||
| 190 | + } | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + @Override | ||
| 195 | + public long getTcpSpeed() { | ||
| 196 | + // no support | ||
| 197 | + return 0; | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() { | ||
| 201 | + @Override | ||
| 202 | + public boolean onError(MediaPlayer mp, int what, int extra) { | ||
| 203 | + mPlayerEventListener.onError(); | ||
| 204 | + return true; | ||
| 205 | + } | ||
| 206 | + }; | ||
| 207 | + | ||
| 208 | + private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() { | ||
| 209 | + @Override | ||
| 210 | + public void onCompletion(MediaPlayer mp) { | ||
| 211 | + mPlayerEventListener.onCompletion(); | ||
| 212 | + } | ||
| 213 | + }; | ||
| 214 | + | ||
| 215 | + private MediaPlayer.OnInfoListener onInfoListener = new MediaPlayer.OnInfoListener() { | ||
| 216 | + @Override | ||
| 217 | + public boolean onInfo(MediaPlayer mp, int what, int extra) { | ||
| 218 | + //解决MEDIA_INFO_VIDEO_RENDERING_START多次回调问题 | ||
| 219 | + if (what == AbstractPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { | ||
| 220 | + if (mIsPreparing) { | ||
| 221 | + mPlayerEventListener.onInfo(what, extra); | ||
| 222 | + mIsPreparing = false; | ||
| 223 | + } | ||
| 224 | + } else { | ||
| 225 | + mPlayerEventListener.onInfo(what, extra); | ||
| 226 | + } | ||
| 227 | + return true; | ||
| 228 | + } | ||
| 229 | + }; | ||
| 230 | + | ||
| 231 | + private MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { | ||
| 232 | + @Override | ||
| 233 | + public void onBufferingUpdate(MediaPlayer mp, int percent) { | ||
| 234 | + mBufferedPercent = percent; | ||
| 235 | + } | ||
| 236 | + }; | ||
| 237 | + | ||
| 238 | + | ||
| 239 | + private MediaPlayer.OnPreparedListener onPreparedListener = new MediaPlayer.OnPreparedListener() { | ||
| 240 | + @Override | ||
| 241 | + public void onPrepared(MediaPlayer mp) { | ||
| 242 | + mPlayerEventListener.onPrepared(); | ||
| 243 | + start(); | ||
| 244 | + } | ||
| 245 | + }; | ||
| 246 | + | ||
| 247 | + private MediaPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener = new MediaPlayer.OnVideoSizeChangedListener() { | ||
| 248 | + @Override | ||
| 249 | + public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { | ||
| 250 | + int videoWidth = mp.getVideoWidth(); | ||
| 251 | + int videoHeight = mp.getVideoHeight(); | ||
| 252 | + if (videoWidth != 0 && videoHeight != 0) { | ||
| 253 | + mPlayerEventListener.onVideoSizeChanged(videoWidth, videoHeight); | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + }; | ||
| 257 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/play/AndroidMediaPlayerFactory.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.play; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | + | ||
| 5 | +public class AndroidMediaPlayerFactory extends PlayerFactory<AndroidMediaPlayer> { | ||
| 6 | + | ||
| 7 | + public static AndroidMediaPlayerFactory create() { | ||
| 8 | + return new AndroidMediaPlayerFactory(); | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + @Override | ||
| 12 | + public AndroidMediaPlayer createPlayer(Context context) { | ||
| 13 | + return new AndroidMediaPlayer(context); | ||
| 14 | + } | ||
| 15 | +} |
cloud/video/src/main/java/com/libs/video/video/exoplayer/play/AudioFocusHelper.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer.play; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.media.AudioManager; | ||
| 5 | +import android.os.Handler; | ||
| 6 | +import android.os.Looper; | ||
| 7 | + | ||
| 8 | +import java.lang.ref.WeakReference; | ||
| 9 | + | ||
| 10 | +import androidx.annotation.NonNull; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 音频焦点改变监听 | ||
| 14 | + */ | ||
| 15 | +final class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener { | ||
| 16 | + | ||
| 17 | + private Handler mHandler = new Handler(Looper.getMainLooper()); | ||
| 18 | + | ||
| 19 | + private WeakReference<CNVideoView> mWeakVideoView; | ||
| 20 | + | ||
| 21 | + private AudioManager mAudioManager; | ||
| 22 | + | ||
| 23 | + private boolean mStartRequested = false; | ||
| 24 | + private boolean mPausedForLoss = false; | ||
| 25 | + private int mCurrentFocus = 0; | ||
| 26 | + | ||
| 27 | + AudioFocusHelper(@NonNull CNVideoView videoView) { | ||
| 28 | + mWeakVideoView = new WeakReference<>(videoView); | ||
| 29 | + mAudioManager = (AudioManager) videoView.getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public void onAudioFocusChange(final int focusChange) { | ||
| 34 | + if (mCurrentFocus == focusChange) { | ||
| 35 | + return; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + //由于onAudioFocusChange有可能在子线程调用, | ||
| 39 | + //故通过此方式切换到主线程去执行 | ||
| 40 | + mHandler.post(new Runnable() { | ||
| 41 | + @Override | ||
| 42 | + public void run() { | ||
| 43 | + handleAudioFocusChange(focusChange); | ||
| 44 | + } | ||
| 45 | + }); | ||
| 46 | + | ||
| 47 | + mCurrentFocus = focusChange; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + private void handleAudioFocusChange(int focusChange) { | ||
| 51 | + final CNVideoView videoView = mWeakVideoView.get(); | ||
| 52 | + if (videoView == null) { | ||
| 53 | + return; | ||
| 54 | + } | ||
| 55 | + switch (focusChange) { | ||
| 56 | + case AudioManager.AUDIOFOCUS_GAIN://获得焦点 | ||
| 57 | + case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT://暂时获得焦点 | ||
| 58 | + if (mStartRequested || mPausedForLoss) { | ||
| 59 | + videoView.start(); | ||
| 60 | + mStartRequested = false; | ||
| 61 | + mPausedForLoss = false; | ||
| 62 | + } | ||
| 63 | + if (!videoView.isMute())//恢复音量 | ||
| 64 | + videoView.setVolume(1.0f, 1.0f); | ||
| 65 | + break; | ||
| 66 | + case AudioManager.AUDIOFOCUS_LOSS://焦点丢失 | ||
| 67 | + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT://焦点暂时丢失 | ||
| 68 | + if (videoView.isPlaying()) { | ||
| 69 | + mPausedForLoss = true; | ||
| 70 | + videoView.pause(); | ||
| 71 | + } | ||
| 72 | + break; | ||
| 73 | + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK://此时需降低音量 | ||
| 74 | + if (videoView.isPlaying() && !videoView.isMute()) { | ||
| 75 | + videoView.setVolume(0.1f, 0.1f); | ||
| 76 | + } | ||
| 77 | + break; | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * Requests to obtain the audio focus | ||
| 83 | + */ | ||
| 84 | + void requestFocus() { | ||
| 85 | + if (mCurrentFocus == AudioManager.AUDIOFOCUS_GAIN) { | ||
| 86 | + return; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + if (mAudioManager == null) { | ||
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + int status = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); | ||
| 94 | + if (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == status) { | ||
| 95 | + mCurrentFocus = AudioManager.AUDIOFOCUS_GAIN; | ||
| 96 | + return; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + mStartRequested = true; | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + /** | ||
| 103 | + * Requests the system to drop the audio focus | ||
| 104 | + */ | ||
| 105 | + void abandonFocus() { | ||
| 106 | + | ||
| 107 | + if (mAudioManager == null) { | ||
| 108 | + return; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + mStartRequested = false; | ||
| 112 | + mAudioManager.abandonAudioFocus(this); | ||
| 113 | + } | ||
| 114 | +} | ||
| 0 | \ No newline at end of file | 115 | \ No newline at end of file |