Commit d6adfcf66da54db5238dfc075bd4d03d2c2e50d8
1 parent
14ed85cd
1 封装视频库
Showing
11 changed files
with
408 additions
and
352 deletions
app/strike/src/main/AndroidManifest.xml
| ... | ... | @@ -9,7 +9,7 @@ |
| 9 | 9 | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> |
| 10 | 10 | |
| 11 | 11 | <application |
| 12 | - android:name="com.cnlive.strike.MyApp" | |
| 12 | + android:name="com.cnlive.strike.demo.exoplayer.demo.DemoApplication" | |
| 13 | 13 | android:allowBackup="true" |
| 14 | 14 | android:icon="@mipmap/ic_launcher" |
| 15 | 15 | android:label="@string/app_name" | ... | ... |
cloud/video/src/main/java/com/libs/video/video/exoplayer/CNLiveBaseVideoControlView.java
| ... | ... | @@ -9,15 +9,17 @@ import android.content.res.TypedArray; |
| 9 | 9 | import android.os.Looper; |
| 10 | 10 | import android.util.AttributeSet; |
| 11 | 11 | import android.util.Log; |
| 12 | +import android.view.Gravity; | |
| 12 | 13 | import android.view.KeyEvent; |
| 13 | 14 | import android.view.LayoutInflater; |
| 14 | 15 | import android.view.Surface; |
| 15 | 16 | import android.view.View; |
| 16 | -import android.view.WindowManager; | |
| 17 | +import android.view.ViewGroup; | |
| 17 | 18 | import android.widget.FrameLayout; |
| 18 | -import android.widget.TextView; | |
| 19 | +import android.widget.LinearLayout; | |
| 19 | 20 | |
| 20 | 21 | import androidx.annotation.Nullable; |
| 22 | +import androidx.core.content.ContextCompat; | |
| 21 | 23 | |
| 22 | 24 | import com.cnlive.cloud.video.R; |
| 23 | 25 | import com.google.android.exoplayer2.C; |
| ... | ... | @@ -25,7 +27,6 @@ import com.google.android.exoplayer2.ExoPlaybackException; |
| 25 | 27 | import com.google.android.exoplayer2.ExoPlayerLibraryInfo; |
| 26 | 28 | import com.google.android.exoplayer2.Format; |
| 27 | 29 | import com.google.android.exoplayer2.PlaybackParameters; |
| 28 | -import com.google.android.exoplayer2.PlaybackPreparer; | |
| 29 | 30 | import com.google.android.exoplayer2.Player; |
| 30 | 31 | import com.google.android.exoplayer2.Timeline; |
| 31 | 32 | import com.google.android.exoplayer2.analytics.AnalyticsListener; |
| ... | ... | @@ -44,6 +45,9 @@ import java.util.Formatter; |
| 44 | 45 | import java.util.Locale; |
| 45 | 46 | import java.util.concurrent.CopyOnWriteArrayList; |
| 46 | 47 | |
| 48 | +/** | |
| 49 | + * 视频控制视图基类 | |
| 50 | + */ | |
| 47 | 51 | public abstract class CNLiveBaseVideoControlView extends FrameLayout implements AnalyticsListener { |
| 48 | 52 | |
| 49 | 53 | static { |
| ... | ... | @@ -135,7 +139,10 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 135 | 139 | private FrameLayout fl_root;//根布局 |
| 136 | 140 | @Nullable |
| 137 | 141 | public Context context; |
| 142 | + //用于获取当前屏幕朝向 | |
| 138 | 143 | private Configuration newConfig; |
| 144 | + //延时隐藏当前播放视图 | |
| 145 | + private Runnable delayHideViewAction; | |
| 139 | 146 | |
| 140 | 147 | public CNLiveBaseVideoControlView(Context context) { |
| 141 | 148 | this(context, /* attrs= */ null, 0, null); |
| ... | ... | @@ -143,14 +150,29 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 143 | 150 | visibilityListeners = new CopyOnWriteArrayList<>(); |
| 144 | 151 | } |
| 145 | 152 | |
| 153 | + /** | |
| 154 | + * 获取竖屏ui | |
| 155 | + * | |
| 156 | + * @return | |
| 157 | + */ | |
| 146 | 158 | protected int getPortraitControllViewId() { |
| 147 | 159 | return -1; |
| 148 | 160 | } |
| 149 | 161 | |
| 162 | + /** | |
| 163 | + * 获取横屏UI | |
| 164 | + * | |
| 165 | + * @return | |
| 166 | + */ | |
| 150 | 167 | protected int getLandscapeControllViewId() { |
| 151 | 168 | return -1; |
| 152 | 169 | } |
| 153 | 170 | |
| 171 | + /** | |
| 172 | + * 按鈕点击事件 | |
| 173 | + * | |
| 174 | + * @param view | |
| 175 | + */ | |
| 154 | 176 | protected abstract void onViewClick(View view); |
| 155 | 177 | |
| 156 | 178 | @SuppressWarnings({ |
| ... | ... | @@ -168,6 +190,7 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 168 | 190 | if (context instanceof Activity) { |
| 169 | 191 | this.activity = (Activity) context; |
| 170 | 192 | } |
| 193 | + | |
| 171 | 194 | formatBuilder = new StringBuilder(); |
| 172 | 195 | formatter = new Formatter(formatBuilder, Locale.getDefault()); |
| 173 | 196 | period = new Timeline.Period(); |
| ... | ... | @@ -177,147 +200,9 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 177 | 200 | componentListener = new CNLiveBaseVideoControlView.ComponentListener(); |
| 178 | 201 | // controlDispatcher = new CNLiveDefaultControlDispatcher(); |
| 179 | 202 | controllerLayoutId = R.layout.layout_cnlive_control_view; |
| 180 | -// if (null != playbackAttrs && -1 != getPortraitControllViewId()) { | |
| 181 | -// rewindMs = DEFAULT_REWIND_MS; | |
| 182 | -// fastForwardMs = DEFAULT_FAST_FORWARD_MS; | |
| 183 | -// showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS; | |
| 184 | -// repeatToggleModes = DEFAULT_REPEAT_TOGGLE_MODES; | |
| 185 | -// timeBarMinUpdateIntervalMs = DEFAULT_TIME_BAR_MIN_UPDATE_INTERVAL_MS; | |
| 186 | -// hideAtMs = C.TIME_UNSET; | |
| 187 | -// showShuffleButton = false; | |
| 188 | -// if (playbackAttrs != null) { | |
| 189 | -// TypedArray a = | |
| 190 | -// context | |
| 191 | -// .getTheme() | |
| 192 | -// .obtainStyledAttributes(playbackAttrs, R.styleable.PlayerControlView, 0, 0); | |
| 193 | -// try { | |
| 194 | -// rewindMs = a.getInt(R.styleable.PlayerControlView_rewind_increment, rewindMs); | |
| 195 | -// fastForwardMs = | |
| 196 | -// a.getInt(R.styleable.PlayerControlView_fastforward_increment, fastForwardMs); | |
| 197 | -// showTimeoutMs = a.getInt(R.styleable.PlayerControlView_show_timeout, showTimeoutMs); | |
| 198 | -// controllerLayoutId = | |
| 199 | -// a.getResourceId(R.styleable.PlayerControlView_controller_layout_id, controllerLayoutId); | |
| 200 | -// repeatToggleModes = getRepeatToggleModes(a, repeatToggleModes); | |
| 201 | -// showShuffleButton = | |
| 202 | -// a.getBoolean(R.styleable.PlayerControlView_show_shuffle_button, showShuffleButton); | |
| 203 | -// setTimeBarMinUpdateInterval( | |
| 204 | -// a.getInt( | |
| 205 | -// R.styleable.PlayerControlView_time_bar_min_update_interval, | |
| 206 | -// timeBarMinUpdateIntervalMs)); | |
| 207 | -// } finally { | |
| 208 | -// a.recycle(); | |
| 209 | -// } | |
| 210 | -// } | |
| 211 | -// | |
| 212 | 203 | |
| 213 | -// | |
| 214 | -// adGroupTimesMs = new long[0]; | |
| 215 | -// playedAdGroups = new boolean[0]; | |
| 216 | -// extraAdGroupTimesMs = new long[0]; | |
| 217 | -// extraPlayedAdGroups = new boolean[0]; | |
| 218 | 204 | updateProgressAction = this::updateProgress; |
| 219 | -// hideAction = this::hide; | |
| 220 | -// | |
| 221 | -// basePortraitView = LayoutInflater.from(context).inflate(controllerLayoutId, /* root= */ this); | |
| 222 | -// setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); | |
| 223 | -// | |
| 224 | -// TimeBar customTimeBar = findViewById(R.id.cnlive_progress); | |
| 225 | -// View timeBarPlaceholder = findViewById(R.id.cnlive_progress_placeholder); | |
| 226 | -// if (customTimeBar != null) { | |
| 227 | -// timeBar = customTimeBar; | |
| 228 | -// } else if (timeBarPlaceholder != null) { | |
| 229 | -// // Propagate attrs as timebarAttrs so that DefaultTimeBar's custom attributes are transferred, | |
| 230 | -// // but standard attributes (e.g. background) are not. | |
| 231 | -// BaseTimerBar defaultTimeBar = new BaseTimerBar(context, null, 0, playbackAttrs); | |
| 232 | -// //todo 设置播放的颜色 | |
| 233 | -//// defaultTimeBar.setPlayedColor(ContextCompat.getColor(context,R.color.qmui_config_color_red)); | |
| 234 | -// //todo 设置缓冲颜色 | |
| 235 | -//// defaultTimeBar.setBufferedColor(ContextCompat.getColor(context,R.color.qmui_config_color_red)); | |
| 236 | -// defaultTimeBar.setId(R.id.cnlive_progress); | |
| 237 | -// defaultTimeBar.setLayoutParams(timeBarPlaceholder.getLayoutParams()); | |
| 238 | -// ViewGroup parent = ((ViewGroup) timeBarPlaceholder.getParent()); | |
| 239 | -// int timeBarIndex = parent.indexOfChild(timeBarPlaceholder); | |
| 240 | -// parent.removeView(timeBarPlaceholder); | |
| 241 | -// parent.addView(defaultTimeBar, timeBarIndex); | |
| 242 | -// timeBar = defaultTimeBar; | |
| 243 | -// } else { | |
| 244 | -// timeBar = null; | |
| 245 | -// } | |
| 246 | -// durationView = findViewById(R.id.cnlive_duration); | |
| 247 | -// positionView = findViewById(R.id.cnlive_position); | |
| 248 | -// //初始化顶部视图 | |
| 249 | -// //顶部标题 | |
| 250 | -// tvTopViewTitle = findViewById(R.id.cnlive_top_title); | |
| 251 | -// //设置标题名称 | |
| 252 | -// if (null != tvTopViewTitle) { | |
| 253 | -// tvTopViewTitle.setText("测试"); | |
| 254 | -// } | |
| 255 | -// if (timeBar != null) { | |
| 256 | -// timeBar.addListener(componentListener); | |
| 257 | -// } | |
| 258 | -// playButton = findViewById(R.id.cnlive_play); | |
| 259 | -// if (playButton != null) { | |
| 260 | -// playButton.setOnClickListener(componentListener); | |
| 261 | -// } | |
| 262 | -// pauseButton = findViewById(R.id.cnlive_pause); | |
| 263 | -// if (pauseButton != null) { | |
| 264 | -// pauseButton.setOnClickListener(componentListener); | |
| 265 | -// } | |
| 266 | -// previousButton = findViewById(R.id.cnlive_prev); | |
| 267 | -// if (previousButton != null) { | |
| 268 | -// previousButton.setOnClickListener(componentListener); | |
| 269 | -// } | |
| 270 | -// nextButton = findViewById(R.id.cnlive_next); | |
| 271 | -// if (nextButton != null) { | |
| 272 | -// nextButton.setOnClickListener(componentListener); | |
| 273 | -// } | |
| 274 | -// rewindButton = findViewById(R.id.cnlive_rew); | |
| 275 | -// if (rewindButton != null) { | |
| 276 | -// rewindButton.setOnClickListener(componentListener); | |
| 277 | -// } | |
| 278 | -// fastForwardButton = findViewById(R.id.cnlive_ffwd); | |
| 279 | -// if (fastForwardButton != null) { | |
| 280 | -// fastForwardButton.setOnClickListener(componentListener); | |
| 281 | -// } | |
| 282 | -// repeatToggleButton = findViewById(R.id.cnlive_repeat_toggle); | |
| 283 | -// if (repeatToggleButton != null) { | |
| 284 | -// repeatToggleButton.setOnClickListener(componentListener); | |
| 285 | -// } | |
| 286 | -// repeatToggleButton.setVisibility(GONE); | |
| 287 | -// shuffleButton = findViewById(R.id.cnlive_shuffle); | |
| 288 | -// if (shuffleButton != null) { | |
| 289 | -// shuffleButton.setOnClickListener(componentListener); | |
| 290 | -// } | |
| 291 | -// //初始化横竖屏切换 | |
| 292 | -// switchScreenButton = findViewById(R.id.cnlive_switch_screen); | |
| 293 | -// if (switchScreenButton != null) { | |
| 294 | -// switchScreenButton.setOnClickListener(componentListener); | |
| 295 | -// } | |
| 296 | -// vrButton = findViewById(R.id.cnlive_vr); | |
| 297 | -// setShowVrButton(false); | |
| 298 | 205 | // |
| 299 | -// Resources resources = context.getResources(); | |
| 300 | -// | |
| 301 | -// buttonAlphaEnabled = | |
| 302 | -// (float) resources.getInteger(R.integer.exo_media_button_opacity_percentage_enabled) / 100; | |
| 303 | -// buttonAlphaDisabled = | |
| 304 | -// (float) resources.getInteger(R.integer.exo_media_button_opacity_percentage_disabled) / 100; | |
| 305 | -// | |
| 306 | -// repeatOffButtonDrawable = resources.getDrawable(R.drawable.exo_controls_repeat_off); | |
| 307 | -// repeatOneButtonDrawable = resources.getDrawable(R.drawable.exo_controls_repeat_one); | |
| 308 | -// repeatAllButtonDrawable = resources.getDrawable(R.drawable.exo_controls_repeat_all); | |
| 309 | -// shuffleOnButtonDrawable = resources.getDrawable(R.drawable.exo_controls_shuffle_on); | |
| 310 | -// shuffleOffButtonDrawable = resources.getDrawable(R.drawable.exo_controls_shuffle_off); | |
| 311 | -// repeatOffButtonContentDescription = | |
| 312 | -// resources.getString(R.string.exo_controls_repeat_off_description); | |
| 313 | -// repeatOneButtonContentDescription = | |
| 314 | -// resources.getString(R.string.exo_controls_repeat_one_description); | |
| 315 | -// repeatAllButtonContentDescription = | |
| 316 | -// resources.getString(R.string.exo_controls_repeat_all_description); | |
| 317 | -// shuffleOnContentDescription = resources.getString(R.string.exo_controls_shuffle_on_description); | |
| 318 | -// shuffleOffContentDescription = | |
| 319 | -// resources.getString(R.string.exo_controls_shuffle_off_description); | |
| 320 | -// } else { | |
| 321 | 206 | //设置底层视图 |
| 322 | 207 | baseRootView = LayoutInflater.from(context).inflate(R.layout.layout_cnlive_control_view, /* root= */ this); |
| 323 | 208 | if (null != baseRootView) { |
| ... | ... | @@ -326,21 +211,8 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 326 | 211 | baseRootView.post(new Runnable() { |
| 327 | 212 | @Override |
| 328 | 213 | public void run() { |
| 329 | - if (-1 != getPortraitControllViewId()) { | |
| 330 | - basePortraitView = LayoutInflater.from(context).inflate(getPortraitControllViewId(), /* root= */ null); | |
| 331 | - if (null != fl_root && null != basePortraitView) { | |
| 332 | - fl_root.addView(basePortraitView); | |
| 333 | - } | |
| 334 | - } | |
| 335 | - if (-1 != getLandscapeControllViewId()) { | |
| 336 | - baseLandscapeView = LayoutInflater.from(context).inflate(getLandscapeControllViewId(), /* root= */ null); | |
| 337 | - if (null != fl_root && null != baseLandscapeView) { | |
| 338 | - fl_root.addView(baseLandscapeView); | |
| 339 | - } | |
| 340 | - } | |
| 341 | - basePortraitView.setVisibility(VISIBLE); | |
| 342 | - baseLandscapeView.setVisibility(GONE); | |
| 343 | - | |
| 214 | + //初始化视图UI | |
| 215 | + initView(); | |
| 344 | 216 | if (null != componentListener && null != basePortraitView) { |
| 345 | 217 | initPortraitView(basePortraitView, componentListener); |
| 346 | 218 | } |
| ... | ... | @@ -348,27 +220,98 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 348 | 220 | baseRootView.setOnClickListener(new OnClickListener() { |
| 349 | 221 | @Override |
| 350 | 222 | public void onClick(View v) { |
| 351 | - //获取当前屏幕朝向 | |
| 352 | - if (null == newConfig) { | |
| 353 | - showPortraitView(); | |
| 354 | - } else { | |
| 355 | - if (null != basePortraitView && null != baseLandscapeView) { | |
| 356 | - //当前是竖屏 | |
| 357 | - if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { | |
| 358 | - showPortraitView(); | |
| 359 | - } else { | |
| 360 | - showLandscapeView(); | |
| 361 | - } | |
| 362 | - } else { | |
| 363 | - showPortraitView(); | |
| 364 | - } | |
| 365 | - } | |
| 223 | + showControlView(); | |
| 366 | 224 | } |
| 367 | 225 | }); |
| 368 | 226 | } |
| 369 | 227 | |
| 370 | 228 | }); |
| 229 | +// post(new Runnable() { | |
| 230 | +// @Override | |
| 231 | +// public void run() { | |
| 232 | +// initView(); | |
| 233 | +// if (null != componentListener && null != basePortraitView) { | |
| 234 | +// initPortraitView(basePortraitView, componentListener); | |
| 235 | +// } | |
| 236 | +// LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT); | |
| 237 | +// LinearLayout lineLayout = new LinearLayout(context); | |
| 238 | +// lineLayout.setOrientation(LinearLayout.VERTICAL); | |
| 239 | +// lineLayout.setLayoutParams(params); | |
| 240 | +// lineLayout.setGravity(Gravity.CENTER ); | |
| 241 | +// lineLayout.setClickable(true); | |
| 242 | +// lineLayout.setBackgroundColor(ContextCompat.getColor(context,R.color.black)); | |
| 243 | +// | |
| 244 | +// addView(lineLayout); | |
| 245 | +// } | |
| 246 | +// }); | |
| 247 | + } | |
| 248 | + | |
| 249 | + /** | |
| 250 | + * 显示控制UI | |
| 251 | + */ | |
| 252 | + private void showControlView() { | |
| 253 | + //获取当前屏幕朝向 | |
| 254 | + if (null == newConfig) { | |
| 255 | + showPortraitView(); | |
| 256 | + } else { | |
| 257 | + if (null != basePortraitView && null != baseLandscapeView) { | |
| 258 | + //当前是竖屏 | |
| 259 | + if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { | |
| 260 | + showPortraitView(); | |
| 261 | + } else { | |
| 262 | + showLandscapeView(); | |
| 263 | + } | |
| 264 | + } else { | |
| 265 | + showPortraitView(); | |
| 266 | + } | |
| 267 | + } | |
| 268 | + if (null != delayHideViewAction) { | |
| 269 | + removeCallbacks(delayHideViewAction); | |
| 270 | + postDelayed(delayHideViewAction, 10 * 1000); | |
| 271 | + } | |
| 272 | + | |
| 273 | + } | |
| 274 | + | |
| 275 | + /** | |
| 276 | + * 初始化视图 | |
| 277 | + */ | |
| 278 | + private void initView() { | |
| 279 | + //隐藏视图 | |
| 280 | + delayHideViewAction = new Runnable() { | |
| 281 | + @Override | |
| 282 | + public void run() { | |
| 283 | + if (null != basePortraitView) { | |
| 284 | + basePortraitView.setVisibility(GONE); | |
| 285 | + } | |
| 286 | + if (null != baseLandscapeView) { | |
| 287 | + baseLandscapeView.setVisibility(GONE); | |
| 288 | + } | |
| 289 | + } | |
| 290 | + }; | |
| 291 | + | |
| 292 | + if (-1 != getPortraitControllViewId()) { | |
| 293 | + basePortraitView = LayoutInflater.from(context).inflate(getPortraitControllViewId(), /* root= */ null); | |
| 294 | +// if (null != fl_root && null != basePortraitView) { | |
| 295 | +// fl_root.addView(basePortraitView); | |
| 296 | +// } | |
| 297 | + addView(basePortraitView); | |
| 298 | + } | |
| 299 | + if (-1 != getLandscapeControllViewId()) { | |
| 300 | + baseLandscapeView = LayoutInflater.from(context).inflate(getLandscapeControllViewId(), /* root= */ null); | |
| 301 | +// if (null != fl_root && null != baseLandscapeView) { | |
| 302 | +// fl_root.addView(baseLandscapeView); | |
| 303 | +// } | |
| 304 | + addView(baseLandscapeView); | |
| 305 | + } | |
| 371 | 306 | |
| 307 | + basePortraitView.setVisibility(VISIBLE); | |
| 308 | + if (null != baseLandscapeView) { | |
| 309 | + baseLandscapeView.setVisibility(GONE); | |
| 310 | + } | |
| 311 | + if (null != delayHideViewAction) { | |
| 312 | + removeCallbacks(delayHideViewAction); | |
| 313 | + postDelayed(delayHideViewAction, 10 * 1000); | |
| 314 | + } | |
| 372 | 315 | } |
| 373 | 316 | |
| 374 | 317 | /** |
| ... | ... | @@ -385,19 +328,20 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 385 | 328 | * 显示横屏 |
| 386 | 329 | */ |
| 387 | 330 | private void showLandscapeView() { |
| 388 | - if (null == baseLandscapeView) { | |
| 389 | - return; | |
| 390 | - } | |
| 391 | - if (baseLandscapeView.getVisibility() == View.VISIBLE) { | |
| 392 | - baseLandscapeView.setVisibility(View.GONE); | |
| 393 | - } else { | |
| 394 | - baseLandscapeView.setVisibility(View.VISIBLE); | |
| 395 | - } | |
| 331 | + //当前有横屏UI | |
| 332 | + if (null != baseLandscapeView) { | |
| 333 | + if (baseLandscapeView.getVisibility() == View.VISIBLE) { | |
| 334 | + baseLandscapeView.setVisibility(View.GONE); | |
| 335 | + } else { | |
| 336 | + baseLandscapeView.setVisibility(View.VISIBLE); | |
| 337 | + } | |
| 396 | 338 | |
| 397 | - if (null != basePortraitView) { | |
| 398 | - basePortraitView.setVisibility(View.GONE); | |
| 339 | + if (null != basePortraitView) { | |
| 340 | + basePortraitView.setVisibility(View.GONE); | |
| 341 | + } | |
| 342 | + } else { | |
| 343 | + basePortraitView.setVisibility(View.VISIBLE); | |
| 399 | 344 | } |
| 400 | - | |
| 401 | 345 | } |
| 402 | 346 | |
| 403 | 347 | /** |
| ... | ... | @@ -407,11 +351,7 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 407 | 351 | if (null == basePortraitView) { |
| 408 | 352 | return; |
| 409 | 353 | } |
| 410 | - if (basePortraitView.getVisibility() == View.VISIBLE) { | |
| 411 | - basePortraitView.setVisibility(View.GONE); | |
| 412 | - } else { | |
| 413 | - basePortraitView.setVisibility(View.VISIBLE); | |
| 414 | - } | |
| 354 | + basePortraitView.setVisibility(View.VISIBLE); | |
| 415 | 355 | if (null != baseLandscapeView) { |
| 416 | 356 | baseLandscapeView.setVisibility(GONE); |
| 417 | 357 | } |
| ... | ... | @@ -554,35 +494,6 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 554 | 494 | // updateShuffleButton(); |
| 555 | 495 | // } |
| 556 | 496 | |
| 557 | - /** | |
| 558 | - * Returns whether the VR button is shown. | |
| 559 | - */ | |
| 560 | -// public boolean getShowVrButton() { | |
| 561 | -// return vrButton != null && vrButton.getVisibility() == VISIBLE; | |
| 562 | -// } | |
| 563 | - | |
| 564 | - /** | |
| 565 | - * Sets whether the VR button is shown. | |
| 566 | - * | |
| 567 | - * @param showVrButton Whether the VR button is shown. | |
| 568 | - */ | |
| 569 | -// public void setShowVrButton(boolean showVrButton) { | |
| 570 | -// if (vrButton != null) { | |
| 571 | -// vrButton.setVisibility(showVrButton ? VISIBLE : GONE); | |
| 572 | -// } | |
| 573 | -// } | |
| 574 | - | |
| 575 | - /** | |
| 576 | - * Sets listener for the VR button. | |
| 577 | - * | |
| 578 | - * @param onClickListener Listener for the VR button, or null to clear the listener. | |
| 579 | - */ | |
| 580 | -// public void setVrButtonListener(@Nullable OnClickListener onClickListener) { | |
| 581 | -// if (vrButton != null) { | |
| 582 | -// vrButton.setOnClickListener(onClickListener); | |
| 583 | -// } | |
| 584 | -// } | |
| 585 | - | |
| 586 | 497 | |
| 587 | 498 | /** |
| 588 | 499 | * Shows the playback controls. If {@link #getShowTimeoutMs()} is positive then the controls will |
| ... | ... | @@ -775,7 +686,7 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 775 | 686 | * 更新进度条 |
| 776 | 687 | */ |
| 777 | 688 | private void updateProgress() { |
| 778 | - if (!isVisible() || !isAttachedToWindow) { | |
| 689 | + if (!isVisible() || !isAttachedToWindow || null == basePortraitView) { | |
| 779 | 690 | return; |
| 780 | 691 | } |
| 781 | 692 | long position = 0; |
| ... | ... | @@ -783,23 +694,18 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 783 | 694 | position = player.getContentPosition(); |
| 784 | 695 | } |
| 785 | 696 | Log.e(TAG, "updateProgress: " + position); |
| 786 | -// if (positionView != null && !scrubbing) { | |
| 787 | -// positionView.setText(Util.getStringForTime(formatBuilder, formatter, position)); | |
| 788 | -// } | |
| 789 | -// if (progressUpdateListener != null) { | |
| 790 | -// progressUpdateListener.onProgressUpdate(position, bufferedPosition); | |
| 791 | -// } | |
| 792 | 697 | //先移除之前的动作 |
| 793 | 698 | removeCallbacks(updateProgressAction); |
| 794 | 699 | int playbackState = player == null ? Player.STATE_IDLE : player.getPlaybackState(); |
| 795 | - onProgressChange(position, Util.getStringForTime(formatBuilder, formatter, position)); | |
| 796 | 700 | if (player != null && player.isPlaying()) { |
| 797 | 701 | postDelayed(updateProgressAction, MAX_UPDATE_INTERVAL_MS); |
| 702 | + onProgressChange(position, Util.getStringForTime(formatBuilder, formatter, position)); | |
| 798 | 703 | } else if (playbackState != Player.STATE_ENDED && playbackState != Player.STATE_IDLE) { |
| 799 | 704 | postDelayed(updateProgressAction, MAX_UPDATE_INTERVAL_MS); |
| 800 | 705 | } |
| 801 | 706 | } |
| 802 | 707 | |
| 708 | + | |
| 803 | 709 | // private void requestPlayPauseFocus() { |
| 804 | 710 | // boolean shouldShowPauseButton = shouldShowPauseButton(); |
| 805 | 711 | // if (!shouldShowPauseButton && playButton != null) { |
| ... | ... | @@ -818,7 +724,7 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 818 | 724 | // view.setVisibility(VISIBLE); |
| 819 | 725 | // } |
| 820 | 726 | |
| 821 | - private void previous(VideoPlayer player) { | |
| 727 | + private void previous() { | |
| 822 | 728 | Timeline timeline = player.getCurrentTimeline(); |
| 823 | 729 | if (timeline.isEmpty() || player.isPlayingAd()) { |
| 824 | 730 | return; |
| ... | ... | @@ -837,10 +743,8 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 837 | 743 | |
| 838 | 744 | /** |
| 839 | 745 | * 播放下一个视频 |
| 840 | - * | |
| 841 | - * @param player | |
| 842 | 746 | */ |
| 843 | - public void next(VideoPlayer player) { | |
| 747 | + public void next() { | |
| 844 | 748 | Timeline timeline = player.getCurrentTimeline(); |
| 845 | 749 | if (timeline.isEmpty() || player.isPlayingAd()) { |
| 846 | 750 | return; |
| ... | ... | @@ -942,7 +846,12 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 942 | 846 | public void onDetachedFromWindow() { |
| 943 | 847 | super.onDetachedFromWindow(); |
| 944 | 848 | isAttachedToWindow = false; |
| 945 | - removeCallbacks(updateProgressAction); | |
| 849 | + if (null != delayHideViewAction) { | |
| 850 | + removeCallbacks(delayHideViewAction); | |
| 851 | + } | |
| 852 | + if (null != updateProgressAction) { | |
| 853 | + removeCallbacks(updateProgressAction); | |
| 854 | + } | |
| 946 | 855 | // removeCallbacks(hideAction); |
| 947 | 856 | } |
| 948 | 857 | |
| ... | ... | @@ -994,10 +903,10 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 994 | 903 | pauseVideo(); |
| 995 | 904 | break; |
| 996 | 905 | case KeyEvent.KEYCODE_MEDIA_NEXT: |
| 997 | - next(player); | |
| 906 | +// next(player); | |
| 998 | 907 | break; |
| 999 | 908 | case KeyEvent.KEYCODE_MEDIA_PREVIOUS: |
| 1000 | - previous(player); | |
| 909 | +// previous(player); | |
| 1001 | 910 | break; |
| 1002 | 911 | default: |
| 1003 | 912 | break; |
| ... | ... | @@ -1087,13 +996,6 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 1087 | 996 | } |
| 1088 | 997 | } |
| 1089 | 998 | |
| 1090 | - public void setShuffleModeEnabled(boolean isShuffleModeEnabled) { | |
| 1091 | - if (null == player) { | |
| 1092 | - return; | |
| 1093 | - } | |
| 1094 | - player.setShuffleModeEnabled(isShuffleModeEnabled); | |
| 1095 | -// controlDispatcher.dispatchSetShuffleModeEnabled(player, !player.getShuffleModeEnabled()); | |
| 1096 | - } | |
| 1097 | 999 | |
| 1098 | 1000 | /** |
| 1099 | 1001 | * 设置播放模式 |
| ... | ... | @@ -1117,12 +1019,19 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 1117 | 1019 | //获取当前屏幕朝向 |
| 1118 | 1020 | Configuration mConfiguration = activity.getResources().getConfiguration(); //获取设置的配置信息 |
| 1119 | 1021 | int ori = mConfiguration.orientation; //获取屏幕方向 |
| 1120 | - if (ori == mConfiguration.ORIENTATION_LANDSCAPE) { | |
| 1022 | + if (ori == Configuration.ORIENTATION_LANDSCAPE) { | |
| 1121 | 1023 | //强制为竖屏 |
| 1122 | 1024 | activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); |
| 1123 | - } else if (ori == mConfiguration.ORIENTATION_PORTRAIT) { | |
| 1025 | + } else if (ori == Configuration.ORIENTATION_PORTRAIT) { | |
| 1124 | 1026 | //强制为横屏 |
| 1125 | 1027 | activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); |
| 1028 | + //修改当前UI宽高 | |
| 1029 | + LayoutParams lp = (LayoutParams) getLayoutParams(); | |
| 1030 | + lp.setMargins(0, 0, 0, 0); | |
| 1031 | + lp.height = ViewGroup.LayoutParams.MATCH_PARENT; | |
| 1032 | + lp.width = ViewGroup.LayoutParams.MATCH_PARENT; | |
| 1033 | + lp.gravity = Gravity.CENTER; | |
| 1034 | + setLayoutParams(lp); | |
| 1126 | 1035 | } |
| 1127 | 1036 | } |
| 1128 | 1037 | } |
| ... | ... | @@ -1131,21 +1040,7 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 1131 | 1040 | protected void onConfigurationChanged(Configuration newConfig) { |
| 1132 | 1041 | super.onConfigurationChanged(newConfig); |
| 1133 | 1042 | this.newConfig = newConfig; |
| 1134 | - //竖屏 | |
| 1135 | - if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { | |
| 1136 | - showPortraitView(); | |
| 1137 | -// if (null != baseLandscapeView) { | |
| 1138 | -// baseLandscapeView.setVisibility(GONE); | |
| 1139 | -// } | |
| 1140 | -// if (null != basePortraitView) { | |
| 1141 | -// basePortraitView.setVisibility(VISIBLE); | |
| 1142 | -// } | |
| 1143 | - } | |
| 1144 | - //横屏 | |
| 1145 | - else { | |
| 1146 | - //隐藏状态栏 | |
| 1147 | - showLandscapeView(); | |
| 1148 | - } | |
| 1043 | + showControlView(); | |
| 1149 | 1044 | } |
| 1150 | 1045 | |
| 1151 | 1046 | /** |
| ... | ... | @@ -1243,7 +1138,9 @@ public abstract class CNLiveBaseVideoControlView extends FrameLayout implements |
| 1243 | 1138 | } |
| 1244 | 1139 | } |
| 1245 | 1140 | |
| 1246 | - /**获取格式化后的时间 | |
| 1141 | + /** | |
| 1142 | + * 获取格式化后的时间 | |
| 1143 | + * | |
| 1247 | 1144 | * @param duration |
| 1248 | 1145 | * @return |
| 1249 | 1146 | */ | ... | ... |
cloud/video/src/main/java/com/libs/video/video/exoplayer/CNLiveVideoView.java
| ... | ... | @@ -29,6 +29,7 @@ import android.widget.FrameLayout; |
| 29 | 29 | import android.widget.ImageView; |
| 30 | 30 | import android.widget.LinearLayout; |
| 31 | 31 | import android.widget.TextView; |
| 32 | +import android.widget.Toast; | |
| 32 | 33 | |
| 33 | 34 | import androidx.annotation.ColorInt; |
| 34 | 35 | import androidx.annotation.IntDef; |
| ... | ... | @@ -42,9 +43,11 @@ import com.google.android.exoplayer2.DefaultControlDispatcher; |
| 42 | 43 | import com.google.android.exoplayer2.ExoPlaybackException; |
| 43 | 44 | import com.google.android.exoplayer2.PlaybackPreparer; |
| 44 | 45 | import com.google.android.exoplayer2.Player; |
| 46 | +import com.google.android.exoplayer2.analytics.AnalyticsListener; | |
| 45 | 47 | import com.google.android.exoplayer2.metadata.Metadata; |
| 46 | 48 | import com.google.android.exoplayer2.metadata.flac.PictureFrame; |
| 47 | 49 | import com.google.android.exoplayer2.metadata.id3.ApicFrame; |
| 50 | +import com.google.android.exoplayer2.source.MediaSourceEventListener; | |
| 48 | 51 | import com.google.android.exoplayer2.source.TrackGroupArray; |
| 49 | 52 | import com.google.android.exoplayer2.source.ads.AdsLoader; |
| 50 | 53 | import com.google.android.exoplayer2.text.Cue; |
| ... | ... | @@ -63,13 +66,18 @@ import com.google.android.exoplayer2.util.Util; |
| 63 | 66 | import com.google.android.exoplayer2.video.VideoDecoderGLSurfaceView; |
| 64 | 67 | import com.google.android.exoplayer2.video.VideoListener; |
| 65 | 68 | |
| 69 | +import java.io.IOException; | |
| 66 | 70 | import java.lang.annotation.Documented; |
| 67 | 71 | import java.lang.annotation.Retention; |
| 68 | 72 | import java.lang.annotation.RetentionPolicy; |
| 69 | 73 | import java.util.ArrayList; |
| 70 | 74 | import java.util.List; |
| 71 | 75 | |
| 72 | -public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProvider { | |
| 76 | +public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProvider, AnalyticsListener { | |
| 77 | + | |
| 78 | + | |
| 79 | + private String TAG = this.getClass().getName(); | |
| 80 | + | |
| 73 | 81 | /** |
| 74 | 82 | * Determines when the buffering view is shown. One of {@link #SHOW_BUFFERING_NEVER}, {@link |
| 75 | 83 | * #SHOW_BUFFERING_WHEN_PLAYING} or {@link #SHOW_BUFFERING_ALWAYS}. |
| ... | ... | @@ -118,7 +126,7 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 118 | 126 | @Nullable |
| 119 | 127 | private final View bufferingView; |
| 120 | 128 | @Nullable |
| 121 | - private final TextView errorMessageView; | |
| 129 | + private final LinearLayout errorMessageView; | |
| 122 | 130 | @Nullable |
| 123 | 131 | private CNLiveBaseVideoControlView controller; |
| 124 | 132 | @Nullable |
| ... | ... | @@ -177,6 +185,8 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 177 | 185 | componentListener = new CNLiveVideoView.ComponentListener(); |
| 178 | 186 | //初始化videoplayer |
| 179 | 187 | player = new VideoPlayer(context); |
| 188 | + //添加事件监听 | |
| 189 | + player.addAnalyticsListener(this); | |
| 180 | 190 | if (isInEditMode()) { |
| 181 | 191 | contentFrame = null; |
| 182 | 192 | shutterView = null; |
| ... | ... | @@ -313,7 +323,13 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 313 | 323 | if (errorMessageView != null) { |
| 314 | 324 | errorMessageView.setVisibility(View.GONE); |
| 315 | 325 | } |
| 316 | - | |
| 326 | + errorMessageView.setOnClickListener(new OnClickListener() { | |
| 327 | + @Override | |
| 328 | + public void onClick(View view) { | |
| 329 | + errorMessageView.setVisibility(GONE); | |
| 330 | + player.retry(); | |
| 331 | + } | |
| 332 | + }); | |
| 317 | 333 | // Playback control view. |
| 318 | 334 | CNLiveBaseVideoControlView customController = findViewById(R.id.cnlive_controller); |
| 319 | 335 | controllerPlaceholder = findViewById(R.id.cnlive_controller_placeholder); |
| ... | ... | @@ -399,8 +415,14 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 399 | 415 | controller.addVisibilityListener(/* listener= */ componentListener); |
| 400 | 416 | } |
| 401 | 417 | Uri mp4VideoUri = Uri.parse("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8"); |
| 402 | - player.setDataSource(mp4VideoUri); | |
| 403 | - player.prepare(false); | |
| 418 | + List<String> list = new ArrayList<>(); | |
| 419 | + list.add("https://qq.com-ok-qq.com/20191005/24249_f5e36323/index.m3u8"); | |
| 420 | + list.add("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8"); | |
| 421 | + list.add("http://ffyl.vods2.cnlive.com/223/vod/2018/1120/223_88c98ab1395f4762b703fbdd9d59d2e5/ff80808166c2a45201672f14a9933131_800.m3u8"); | |
| 422 | + | |
| 423 | + | |
| 424 | + player.setDataSource(list); | |
| 425 | + player.prepare(); | |
| 404 | 426 | setPlayer(player); |
| 405 | 427 | controllerPlaceholder.setOnClickListener(new OnClickListener() { |
| 406 | 428 | @Override |
| ... | ... | @@ -763,16 +785,6 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 763 | 785 | return controller != null && controller.isVisible(); |
| 764 | 786 | } |
| 765 | 787 | |
| 766 | - /** | |
| 767 | - * Shows the playback controls. Does nothing if playback controls are disabled. | |
| 768 | - * | |
| 769 | - * <p>The playback controls are automatically hidden during playback after {{@link | |
| 770 | - * #getControllerShowTimeoutMs()}}. They are shown indefinitely when playback has not started yet, | |
| 771 | - * is paused, has ended or failed. | |
| 772 | - */ | |
| 773 | - public void showController() { | |
| 774 | - showController(shouldShowControllerIndefinitely()); | |
| 775 | - } | |
| 776 | 788 | |
| 777 | 789 | /** |
| 778 | 790 | * Hides the playback controls. Does nothing if playback controls are disabled. |
| ... | ... | @@ -807,7 +819,7 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 807 | 819 | this.controllerShowTimeoutMs = controllerShowTimeoutMs; |
| 808 | 820 | if (controller.isVisible()) { |
| 809 | 821 | // Update the controller's timeout if necessary. |
| 810 | - showController(); | |
| 822 | +// showController(); | |
| 811 | 823 | } |
| 812 | 824 | } |
| 813 | 825 | |
| ... | ... | @@ -838,16 +850,6 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 838 | 850 | return controllerAutoShow; |
| 839 | 851 | } |
| 840 | 852 | |
| 841 | - /** | |
| 842 | - * Sets whether the playback controls are automatically shown when playback starts, pauses, ends, | |
| 843 | - * or fails. If set to false, the playback controls can be manually operated with {@link | |
| 844 | - * #showController()} and {@link #hideController()}. | |
| 845 | - * | |
| 846 | - * @param controllerAutoShow Whether the playback controls are allowed to show automatically. | |
| 847 | - */ | |
| 848 | - public void setControllerAutoShow(boolean controllerAutoShow) { | |
| 849 | - this.controllerAutoShow = controllerAutoShow; | |
| 850 | - } | |
| 851 | 853 | |
| 852 | 854 | /** |
| 853 | 855 | * Sets whether the playback controls are hidden when ads are playing. Controls are always shown |
| ... | ... | @@ -1166,13 +1168,6 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 1166 | 1168 | || !player.getPlayWhenReady()); |
| 1167 | 1169 | } |
| 1168 | 1170 | |
| 1169 | - private void showController(boolean showIndefinitely) { | |
| 1170 | - if (!useController()) { | |
| 1171 | - return; | |
| 1172 | - } | |
| 1173 | -// controller.setShowTimeoutMs(showIndefinitely ? 0 : controllerShowTimeoutMs); | |
| 1174 | - controller.show(); | |
| 1175 | - } | |
| 1176 | 1171 | |
| 1177 | 1172 | private boolean isPlayingAd() { |
| 1178 | 1173 | return player != null && player.isPlayingAd() && player.getPlayWhenReady(); |
| ... | ... | @@ -1295,21 +1290,21 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 1295 | 1290 | } |
| 1296 | 1291 | |
| 1297 | 1292 | private void updateErrorMessage() { |
| 1298 | - if (errorMessageView != null) { | |
| 1299 | - if (customErrorMessage != null) { | |
| 1300 | - errorMessageView.setText(customErrorMessage); | |
| 1301 | - errorMessageView.setVisibility(View.VISIBLE); | |
| 1302 | - return; | |
| 1303 | - } | |
| 1304 | - @Nullable ExoPlaybackException error = player != null ? player.getPlaybackError() : null; | |
| 1305 | - if (error != null && errorMessageProvider != null) { | |
| 1306 | - CharSequence errorMessage = errorMessageProvider.getErrorMessage(error).second; | |
| 1307 | - errorMessageView.setText(errorMessage); | |
| 1308 | - errorMessageView.setVisibility(View.VISIBLE); | |
| 1309 | - } else { | |
| 1310 | - errorMessageView.setVisibility(View.GONE); | |
| 1311 | - } | |
| 1312 | - } | |
| 1293 | +// if (errorMessageView != null) { | |
| 1294 | +// if (customErrorMessage != null) { | |
| 1295 | +// errorMessageView.setText(customErrorMessage); | |
| 1296 | +// errorMessageView.setVisibility(View.VISIBLE); | |
| 1297 | +// return; | |
| 1298 | +// } | |
| 1299 | +// @Nullable ExoPlaybackException error = player != null ? player.getPlaybackError() : null; | |
| 1300 | +// if (error != null && errorMessageProvider != null) { | |
| 1301 | +// CharSequence errorMessage = errorMessageProvider.getErrorMessage(error).second; | |
| 1302 | +// errorMessageView.setText(errorMessage); | |
| 1303 | +// errorMessageView.setVisibility(View.VISIBLE); | |
| 1304 | +// } else { | |
| 1305 | +// errorMessageView.setVisibility(View.GONE); | |
| 1306 | +// } | |
| 1307 | +// } | |
| 1313 | 1308 | } |
| 1314 | 1309 | |
| 1315 | 1310 | private void updateContentDescription() { |
| ... | ... | @@ -1491,17 +1486,6 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 1491 | 1486 | } |
| 1492 | 1487 | |
| 1493 | 1488 | |
| 1494 | - /** | |
| 1495 | - * 设置播放进度条的颜色 | |
| 1496 | - * | |
| 1497 | - * @param color | |
| 1498 | - */ | |
| 1499 | -// public void setPlayedProgressColor(@ColorInt int color) { | |
| 1500 | -// if (null != controller && controller instanceof CNLiveBaseVideoControlView) { | |
| 1501 | -// CNLiveBaseVideoControlView controlView = controller; | |
| 1502 | -// controller.setPlayedColor(color); | |
| 1503 | -// } | |
| 1504 | -// } | |
| 1505 | 1489 | public void onBackPress() { |
| 1506 | 1490 | if (null != controller) { |
| 1507 | 1491 | controller.onBackPress(); |
| ... | ... | @@ -1539,5 +1523,86 @@ public class CNLiveVideoView extends FrameLayout implements AdsLoader.AdViewProv |
| 1539 | 1523 | } |
| 1540 | 1524 | } |
| 1541 | 1525 | } |
| 1526 | + | |
| 1527 | + @Override | |
| 1528 | + public void onLoadingChanged(EventTime eventTime, boolean isLoading) { | |
| 1529 | + if (null == bufferingView) { | |
| 1530 | + return; | |
| 1531 | + } | |
| 1532 | +// if (isLoading) { | |
| 1533 | +// bufferingView.setVisibility(VISIBLE); | |
| 1534 | +// } else { | |
| 1535 | +// bufferingView.setVisibility(GONE); | |
| 1536 | +// } | |
| 1537 | + Log.e(TAG, "onLoadingChanged: "); | |
| 1538 | + } | |
| 1539 | + | |
| 1540 | + @Override | |
| 1541 | + public void onLoadCanceled(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { | |
| 1542 | + if (null == bufferingView) { | |
| 1543 | + return; | |
| 1544 | + } | |
| 1545 | + bufferingView.setVisibility(GONE); | |
| 1546 | + Log.e(TAG, "onLoadCanceled: "); | |
| 1547 | + } | |
| 1548 | + | |
| 1549 | + @Override | |
| 1550 | + public void onLoadStarted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { | |
| 1551 | + Log.e(TAG, "onLoadStarted: "); | |
| 1552 | + bufferingView.setVisibility(VISIBLE); | |
| 1553 | + } | |
| 1554 | + | |
| 1555 | + @Override | |
| 1556 | + public void onPlayerError(EventTime eventTime, ExoPlaybackException error) { | |
| 1557 | + if (null != errorMessageView) { | |
| 1558 | + errorMessageView.setVisibility(VISIBLE); | |
| 1559 | + } | |
| 1560 | + // 报错 | |
| 1561 | + switch (error.type) { | |
| 1562 | + case ExoPlaybackException.TYPE_SOURCE: | |
| 1563 | + // 加载资源时出错 | |
| 1564 | + break; | |
| 1565 | + case ExoPlaybackException.TYPE_RENDERER: | |
| 1566 | + // 渲染时出错 | |
| 1567 | + break; | |
| 1568 | + case ExoPlaybackException.TYPE_UNEXPECTED: | |
| 1569 | + // 意外的错误 | |
| 1570 | + break; | |
| 1571 | + } | |
| 1572 | + } | |
| 1573 | + | |
| 1574 | + @Override | |
| 1575 | + public void onLoadCompleted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { | |
| 1576 | + bufferingView.setVisibility(GONE); | |
| 1577 | + } | |
| 1578 | + | |
| 1579 | + @Override | |
| 1580 | + public void onLoadError(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData, IOException error, boolean wasCanceled) { | |
| 1581 | + bufferingView.setVisibility(GONE); | |
| 1582 | + } | |
| 1583 | + | |
| 1584 | + @Override | |
| 1585 | + public void onPlayerStateChanged(EventTime eventTime, boolean playWhenReady, int playbackState) { | |
| 1586 | + switch (playbackState) { | |
| 1587 | + case Player.STATE_IDLE: | |
| 1588 | + Log.e(TAG, "空闲 "); | |
| 1589 | + // 空闲 | |
| 1590 | + break; | |
| 1591 | + case Player.STATE_BUFFERING: | |
| 1592 | + // 缓冲中 | |
| 1593 | + Log.e(TAG, "缓冲中 "); | |
| 1594 | + break; | |
| 1595 | + case Player.STATE_READY: | |
| 1596 | + Log.e(TAG, "准备好 "); | |
| 1597 | + | |
| 1598 | + // 准备好 | |
| 1599 | + break; | |
| 1600 | + case Player.STATE_ENDED: | |
| 1601 | + // 结束 | |
| 1602 | + break; | |
| 1603 | + default: | |
| 1604 | + break; | |
| 1605 | + } | |
| 1606 | + } | |
| 1542 | 1607 | } |
| 1543 | 1608 | ... | ... |
cloud/video/src/main/java/com/libs/video/video/exoplayer/IVideoMultDataSource.java
0 → 100644
| 1 | +package com.libs.video.video.exoplayer; | |
| 2 | + | |
| 3 | +import android.net.Uri; | |
| 4 | + | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 多资源播放 | |
| 9 | + */ | |
| 10 | +public interface IVideoMultDataSource { | |
| 11 | + List<String> getDataSourceId(); | |
| 12 | + | |
| 13 | + List<String> getDataSourceType(); | |
| 14 | + | |
| 15 | + List<String> getPlayUri(); | |
| 16 | +} | ... | ... |
cloud/video/src/main/java/com/libs/video/video/exoplayer/IVideoPlayer.java
| ... | ... | @@ -19,16 +19,17 @@ import com.google.android.exoplayer2.ui.PlayerView; |
| 19 | 19 | import com.libs.video.video.base.IDataSource; |
| 20 | 20 | import com.libs.video.video.base.IMediaPlayer; |
| 21 | 21 | |
| 22 | +import java.util.List; | |
| 23 | + | |
| 22 | 24 | public interface IVideoPlayer { |
| 23 | - // /** | |
| 24 | -// * 设置播放资源 | |
| 25 | -// * | |
| 26 | -// * @param data | |
| 27 | -// */ | |
| 25 | + void setDataSource(IVideoMultDataSource data); | |
| 26 | + | |
| 28 | 27 | void setDataSource(IVideoDataSource data); |
| 29 | 28 | |
| 30 | 29 | void setDataSource(Uri data); |
| 31 | 30 | |
| 31 | + void setDataSource(List<String> data); | |
| 32 | + | |
| 32 | 33 | void setPlayView(CNLiveVideoView playView); |
| 33 | 34 | |
| 34 | 35 | void play() throws IllegalStateException; |
| ... | ... | @@ -39,7 +40,11 @@ public interface IVideoPlayer { |
| 39 | 40 | |
| 40 | 41 | void prepare(); |
| 41 | 42 | |
| 42 | - void prepare(boolean isPlay); | |
| 43 | + void retry(); | |
| 44 | + | |
| 45 | + void onPause(); | |
| 46 | + | |
| 47 | + void onDestroy(); | |
| 43 | 48 | |
| 44 | 49 | void setForegroundMode(boolean foregroundMode); |
| 45 | 50 | ... | ... |
cloud/video/src/main/java/com/libs/video/video/exoplayer/MyTestControlView.java
| ... | ... | @@ -13,6 +13,7 @@ import androidx.appcompat.widget.AppCompatSeekBar; |
| 13 | 13 | import androidx.core.content.ContextCompat; |
| 14 | 14 | |
| 15 | 15 | import com.cnlive.cloud.video.R; |
| 16 | +import com.google.android.exoplayer2.source.MediaSourceEventListener; | |
| 16 | 17 | |
| 17 | 18 | public class MyTestControlView extends CNLiveBaseVideoControlView { |
| 18 | 19 | private View playButton; |
| ... | ... | @@ -70,11 +71,14 @@ public class MyTestControlView extends CNLiveBaseVideoControlView { |
| 70 | 71 | } |
| 71 | 72 | |
| 72 | 73 | @Override |
| 74 | +// protected int getLandscapeControllViewId() { | |
| 75 | +// return R.layout.layout_landscape_control_view; | |
| 76 | +// } | |
| 77 | + | |
| 73 | 78 | protected int getLandscapeControllViewId() { |
| 74 | - return R.layout.layout_landscape_control_view; | |
| 79 | + return -1; | |
| 75 | 80 | } |
| 76 | 81 | |
| 77 | - | |
| 78 | 82 | @Override |
| 79 | 83 | protected void onViewClick(View view) { |
| 80 | 84 | if (view.getId() == R.id.btn_start) { |
| ... | ... | @@ -109,4 +113,13 @@ public class MyTestControlView extends CNLiveBaseVideoControlView { |
| 109 | 113 | seekBar.setMax((int) duration); |
| 110 | 114 | } |
| 111 | 115 | |
| 112 | -} | |
| 116 | + @Override | |
| 117 | + public void onLoadStarted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { | |
| 118 | + super.onLoadStarted(eventTime, loadEventInfo, mediaLoadData); | |
| 119 | + } | |
| 120 | + | |
| 121 | + @Override | |
| 122 | + public void onLoadCompleted(EventTime eventTime, MediaSourceEventListener.LoadEventInfo loadEventInfo, MediaSourceEventListener.MediaLoadData mediaLoadData) { | |
| 123 | + super.onLoadCompleted(eventTime, loadEventInfo, mediaLoadData); | |
| 124 | + } | |
| 125 | +} | |
| 113 | 126 | \ No newline at end of file | ... | ... |
cloud/video/src/main/java/com/libs/video/video/exoplayer/VideoPlayer.java
| ... | ... | @@ -15,6 +15,8 @@ import com.google.android.exoplayer2.SimpleExoPlayer; |
| 15 | 15 | import com.google.android.exoplayer2.Timeline; |
| 16 | 16 | import com.google.android.exoplayer2.analytics.AnalyticsListener; |
| 17 | 17 | import com.google.android.exoplayer2.ext.rtmp.RtmpDataSourceFactory; |
| 18 | +import com.google.android.exoplayer2.source.ConcatenatingMediaSource; | |
| 19 | +import com.google.android.exoplayer2.source.ExtractorMediaSource; | |
| 18 | 20 | import com.google.android.exoplayer2.source.MediaSource; |
| 19 | 21 | import com.google.android.exoplayer2.source.ProgressiveMediaSource; |
| 20 | 22 | import com.google.android.exoplayer2.source.TrackGroupArray; |
| ... | ... | @@ -208,8 +210,31 @@ public class VideoPlayer implements IVideoPlayer { |
| 208 | 210 | return buffer.toString(); |
| 209 | 211 | } |
| 210 | 212 | |
| 213 | + /** | |
| 214 | + * 多资源播放 | |
| 215 | + * | |
| 216 | + * @param data | |
| 217 | + */ | |
| 218 | + @Override | |
| 219 | + public void setDataSource(IVideoMultDataSource data) { | |
| 220 | + if (null != data && null != data.getPlayUri()) { | |
| 221 | + MediaSource[] mediaSources = new MediaSource[data.getPlayUri().size()]; | |
| 222 | + for (int i = 0; i < data.getPlayUri().size(); i++) { | |
| 223 | + mediaSources[i] = getPlayerMediaSource(data.getPlayUri().get(i)); | |
| 224 | + } | |
| 225 | + currentMediaSource = | |
| 226 | + new ConcatenatingMediaSource(mediaSources); | |
| 227 | + } | |
| 228 | + | |
| 229 | +// LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2); | |
| 230 | + | |
| 231 | + } | |
| 232 | + | |
| 211 | 233 | @Override |
| 212 | 234 | public void setDataSource(IVideoDataSource data) { |
| 235 | + ConcatenatingMediaSource concatenatedSource = | |
| 236 | + new ConcatenatingMediaSource(); | |
| 237 | + | |
| 213 | 238 | mDataSource = null; |
| 214 | 239 | mDataSource = data; |
| 215 | 240 | Map<String, String> probeData = new HashMap<>(); |
| ... | ... | @@ -270,6 +295,18 @@ public class VideoPlayer implements IVideoPlayer { |
| 270 | 295 | } |
| 271 | 296 | |
| 272 | 297 | @Override |
| 298 | + public void setDataSource(List<String> data) { | |
| 299 | + if (null != data) { | |
| 300 | + MediaSource[] mediaSources = new MediaSource[data.size()]; | |
| 301 | + for (int i = 0; i < data.size(); i++) { | |
| 302 | + mediaSources[i] = getPlayerMediaSource(data.get(i)); | |
| 303 | + } | |
| 304 | + currentMediaSource = | |
| 305 | + new ConcatenatingMediaSource(mediaSources); | |
| 306 | + } | |
| 307 | + } | |
| 308 | + | |
| 309 | + @Override | |
| 273 | 310 | public void setPlayView(CNLiveVideoView playView) { |
| 274 | 311 | } |
| 275 | 312 | |
| ... | ... | @@ -350,13 +387,29 @@ public class VideoPlayer implements IVideoPlayer { |
| 350 | 387 | } |
| 351 | 388 | |
| 352 | 389 | @Override |
| 353 | - public void prepare(boolean isPlay) { | |
| 354 | - if (null != currentMediaSource && null != player) { | |
| 355 | - player.prepare(currentMediaSource); | |
| 356 | - player.setPlayWhenReady(isPlay); | |
| 390 | + public void retry() { | |
| 391 | + if (null != player) { | |
| 392 | + player.retry(); | |
| 393 | + } | |
| 394 | + } | |
| 395 | + | |
| 396 | + @Override | |
| 397 | + public void onPause() { | |
| 398 | + if (player != null) { | |
| 399 | + player.setPlayWhenReady(false); | |
| 357 | 400 | } |
| 358 | 401 | } |
| 359 | 402 | |
| 403 | + @Override | |
| 404 | + public void onDestroy() { | |
| 405 | + if (player != null) { | |
| 406 | + player.stop(); | |
| 407 | + player.release(); | |
| 408 | + player = null; | |
| 409 | + } | |
| 410 | + } | |
| 411 | + | |
| 412 | + | |
| 360 | 413 | private MediaSource getPlayerMediaSource(String playerUrl) { |
| 361 | 414 | if (TextUtils.isEmpty(playerUrl)) { |
| 362 | 415 | return null; |
| ... | ... | @@ -536,18 +589,18 @@ public class VideoPlayer implements IVideoPlayer { |
| 536 | 589 | */ |
| 537 | 590 | @Override |
| 538 | 591 | public int getCurrentWindowIndex() { |
| 539 | - int result=0; | |
| 592 | + int result = 0; | |
| 540 | 593 | if (null != player) { |
| 541 | - result=player.getCurrentWindowIndex(); | |
| 594 | + result = player.getCurrentWindowIndex(); | |
| 542 | 595 | } |
| 543 | 596 | return result; |
| 544 | 597 | } |
| 545 | 598 | |
| 546 | 599 | @Override |
| 547 | 600 | public long getDuration() { |
| 548 | - long result=0; | |
| 601 | + long result = 0; | |
| 549 | 602 | if (null != player) { |
| 550 | - result=player.getDuration(); | |
| 603 | + result = player.getDuration(); | |
| 551 | 604 | } |
| 552 | 605 | return result; |
| 553 | 606 | } | ... | ... |
cloud/video/src/main/res/drawable-hdpi/cover_youku_icon.png
0 → 100644
1.29 KB
cloud/video/src/main/res/drawable/video_seekbar_progress.xml
cloud/video/src/main/res/layout/layout_base_video_view.xml
| ... | ... | @@ -25,21 +25,6 @@ |
| 25 | 25 | android:layout_width="match_parent" |
| 26 | 26 | android:layout_height="match_parent" /> |
| 27 | 27 | |
| 28 | - <ProgressBar | |
| 29 | - android:id="@id/cnlive_buffering" | |
| 30 | - android:layout_width="wrap_content" | |
| 31 | - android:layout_height="wrap_content" | |
| 32 | - android:layout_gravity="center" | |
| 33 | - android:indeterminate="true" /> | |
| 34 | - | |
| 35 | - <TextView | |
| 36 | - android:id="@id/cnlive_error_message" | |
| 37 | - android:layout_width="match_parent" | |
| 38 | - android:layout_height="match_parent" | |
| 39 | - android:layout_gravity="center" | |
| 40 | - android:background="@color/exo_error_message_background_color" | |
| 41 | - android:gravity="center" | |
| 42 | - android:padding="16dp" /> | |
| 43 | 28 | |
| 44 | 29 | </com.google.android.exoplayer2.ui.AspectRatioFrameLayout> |
| 45 | 30 | |
| ... | ... | @@ -58,4 +43,25 @@ |
| 58 | 43 | android:layout_width="match_parent" |
| 59 | 44 | android:layout_height="match_parent" /> |
| 60 | 45 | |
| 46 | + <ProgressBar | |
| 47 | + android:id="@id/cnlive_buffering" | |
| 48 | + android:layout_width="wrap_content" | |
| 49 | + android:layout_height="wrap_content" | |
| 50 | + android:layout_gravity="center" | |
| 51 | + android:indeterminate="true" /> | |
| 52 | + | |
| 53 | + <LinearLayout | |
| 54 | + android:id="@id/cnlive_error_message" | |
| 55 | + android:layout_width="match_parent" | |
| 56 | + android:layout_height="match_parent" | |
| 57 | + android:background="@color/black" | |
| 58 | + android:clickable="true" | |
| 59 | + android:gravity="center" | |
| 60 | + android:orientation="vertical"> | |
| 61 | + | |
| 62 | + <Button | |
| 63 | + android:layout_width="wrap_content" | |
| 64 | + android:layout_height="wrap_content" | |
| 65 | + android:text="刷新" /> | |
| 66 | + </LinearLayout> | |
| 61 | 67 | </merge> |
| 62 | 68 | \ No newline at end of file | ... | ... |
cloud/video/src/main/res/layout/layout_protrait_control_view.xml
| 1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
| 2 | +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
| 3 | 3 | android:id="@+id/rl_protrait_root" |
| 4 | 4 | android:layout_width="match_parent" |
| 5 | 5 | android:layout_height="match_parent" |
| 6 | + android:background="@color/transparent" | |
| 6 | 7 | android:orientation="vertical"> |
| 7 | 8 | |
| 8 | 9 | <LinearLayout |
| ... | ... | @@ -15,7 +16,7 @@ |
| 15 | 16 | <LinearLayout |
| 16 | 17 | android:layout_width="match_parent" |
| 17 | 18 | android:layout_height="40dp" |
| 18 | - android:layout_alignParentBottom="true" | |
| 19 | + android:layout_gravity="bottom" | |
| 19 | 20 | android:background="@drawable/bg_cnvideo_bottom" |
| 20 | 21 | android:gravity="center_vertical"> |
| 21 | 22 | |
| ... | ... | @@ -63,4 +64,4 @@ |
| 63 | 64 | |
| 64 | 65 | </LinearLayout> |
| 65 | 66 | |
| 66 | -</RelativeLayout> | |
| 67 | 67 | \ No newline at end of file |
| 68 | +</FrameLayout> | |
| 68 | 69 | \ No newline at end of file | ... | ... |