基线备份:thirdparty 变体完成 + Android 4.2 兼容改造前
包含内容: - thirdparty flavor 独立 applicationId + Intent 入口(ThirdPartyEntryActivity) - 58 后端接口适配(单 /api 路径、code 0/200、camelCase 字段、UNBOUND 轮询) - MAC 硬件锚定校验(防一凭证多设备共用) - EPG mac 覆盖 SDK init(sMacOverride)+ ChannelManger 初始化对齐 - 提示文案优化(设备未注册/权益未绑定)
@@ -0,0 +1,26 @@
|
||||
package com.pukka.mediaplayertv;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.migu.mediaplayertv.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
8
mediaplayertv/src/main/AndroidManifest.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.pukka.mediaplayertv">
|
||||
<!-- <application android:allowBackup="false" android:label="${app_name}"-->
|
||||
<!-- android:supportsRtl="true">-->
|
||||
|
||||
<!-- </application>-->
|
||||
</manifest>
|
||||
96
mediaplayertv/src/main/java/com/pukka/videoplayertv/AbstractMediaPlayer.java
Executable file
@@ -0,0 +1,96 @@
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
/**
|
||||
* Author: wxf
|
||||
* Date: 2025/1/6 13:44
|
||||
* Description:播放器的抽象类封装
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public abstract class AbstractMediaPlayer implements IMediaPlayer {
|
||||
private OnPreparedListener mOnPreparedListener;
|
||||
private OnCompletionListener mOnCompletionListener;
|
||||
private OnBufferingUpdateListener mOnBufferingUpdateListener;
|
||||
private OnSeekCompleteListener mOnSeekCompleteListener;
|
||||
private OnVideoSizeChangedListener mOnVideoSizeChangedListener;
|
||||
private OnErrorListener mOnErrorListener;
|
||||
private OnInfoListener mOnInfoListener;
|
||||
|
||||
public final void setOnPreparedListener(OnPreparedListener listener) {
|
||||
mOnPreparedListener = listener;
|
||||
}
|
||||
|
||||
public final void setOnCompletionListener(OnCompletionListener listener) {
|
||||
mOnCompletionListener = listener;
|
||||
}
|
||||
|
||||
public final void setOnBufferingUpdateListener(
|
||||
OnBufferingUpdateListener listener) {
|
||||
mOnBufferingUpdateListener = listener;
|
||||
}
|
||||
|
||||
public final void setOnSeekCompleteListener(OnSeekCompleteListener listener) {
|
||||
mOnSeekCompleteListener = listener;
|
||||
}
|
||||
|
||||
public final void setOnVideoSizeChangedListener(
|
||||
OnVideoSizeChangedListener listener) {
|
||||
mOnVideoSizeChangedListener = listener;
|
||||
}
|
||||
|
||||
public final void setOnErrorListener(OnErrorListener listener) {
|
||||
mOnErrorListener = listener;
|
||||
}
|
||||
|
||||
public final void setOnInfoListener(OnInfoListener listener) {
|
||||
mOnInfoListener = listener;
|
||||
}
|
||||
|
||||
public void resetListeners() {
|
||||
mOnPreparedListener = null;
|
||||
mOnBufferingUpdateListener = null;
|
||||
mOnCompletionListener = null;
|
||||
mOnSeekCompleteListener = null;
|
||||
mOnVideoSizeChangedListener = null;
|
||||
mOnErrorListener = null;
|
||||
mOnInfoListener = null;
|
||||
}
|
||||
|
||||
protected final void notifyOnPrepared() {
|
||||
if (mOnPreparedListener != null)
|
||||
mOnPreparedListener.onPrepared(this);
|
||||
}
|
||||
|
||||
protected final void notifyOnCompletion() {
|
||||
if (mOnCompletionListener != null)
|
||||
mOnCompletionListener.onCompletion(this);
|
||||
}
|
||||
|
||||
protected final void notifyOnBufferingUpdate(int percent) {
|
||||
if (mOnBufferingUpdateListener != null)
|
||||
mOnBufferingUpdateListener.onBufferingUpdate(this, percent);
|
||||
}
|
||||
|
||||
protected final void notifyOnSeekComplete() {
|
||||
if (mOnSeekCompleteListener != null)
|
||||
mOnSeekCompleteListener.onSeekComplete(this);
|
||||
}
|
||||
|
||||
protected final void notifyOnVideoSizeChanged(int width, int height,
|
||||
int sarNum, int sarDen) {
|
||||
if (mOnVideoSizeChangedListener != null)
|
||||
mOnVideoSizeChangedListener.onVideoSizeChanged(this, width, height,
|
||||
sarNum, sarDen);
|
||||
}
|
||||
|
||||
protected final boolean notifyOnError(int what, int extra) {
|
||||
return mOnErrorListener != null && mOnErrorListener.onError(this, what, extra);
|
||||
}
|
||||
|
||||
protected final boolean notifyOnInfo(int what, int extra) {
|
||||
return mOnInfoListener != null && mOnInfoListener.onInfo(this, what, extra);
|
||||
}
|
||||
|
||||
public void setDataSource(IMediaDataSource mediaDataSource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
44
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidMediaFormat.java
Executable file
@@ -0,0 +1,44 @@
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.media.MediaFormat;
|
||||
|
||||
public class AndroidMediaFormat implements IMediaFormat {
|
||||
private final MediaFormat mMediaFormat;
|
||||
|
||||
public AndroidMediaFormat(MediaFormat mediaFormat) {
|
||||
mMediaFormat = mediaFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInteger(String name) {
|
||||
if (mMediaFormat == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mMediaFormat.getInteger(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String name) {
|
||||
if (mMediaFormat == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mMediaFormat.getString(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder out = new StringBuilder(128);
|
||||
out.append(getClass().getName());
|
||||
out.append('{');
|
||||
if (mMediaFormat != null) {
|
||||
out.append(mMediaFormat.toString());
|
||||
} else {
|
||||
out.append("null");
|
||||
}
|
||||
out.append('}');
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
521
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidMediaPlayer.java
Executable file
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
* Copyright (C) 2013 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaDataSource;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
|
||||
import com.pukka.libcore.threading.DispatchQueue;
|
||||
import com.pukka.libcore.utils.LogUtils;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Author: wxf
|
||||
* Date: 2025/1/6 13:44
|
||||
* Description:原生播放器的封装
|
||||
*/
|
||||
public class AndroidMediaPlayer extends AbstractMediaPlayer {
|
||||
private static final String TAG = "VideoViewTv";
|
||||
private final MediaPlayer mInternalMediaPlayer;
|
||||
private final AndroidMediaPlayerListenerHolder mInternalListenerAdapter;
|
||||
private String mDataSource;
|
||||
private MediaDataSource mMediaDataSource;
|
||||
|
||||
private final Object mInitLock = new Object();
|
||||
private boolean mIsReleased;
|
||||
|
||||
private static MediaInfo sMediaInfo;
|
||||
|
||||
public AndroidMediaPlayer() {
|
||||
synchronized (mInitLock) {
|
||||
mInternalMediaPlayer = new MediaPlayer();
|
||||
}
|
||||
mInternalMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mInternalListenerAdapter = new AndroidMediaPlayerListenerHolder(this);
|
||||
attachInternalListeners();
|
||||
}
|
||||
|
||||
public MediaPlayer getInternalMediaPlayer() {
|
||||
return mInternalMediaPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplay(SurfaceHolder sh) {
|
||||
synchronized (mInitLock) {
|
||||
if (!mIsReleased) {
|
||||
LogUtils.d("setDisplay ---xx : " + sh);
|
||||
try {
|
||||
mInternalMediaPlayer.setDisplay(sh);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
@Override
|
||||
public void setSurface(Surface surface) {
|
||||
LogUtils.d("setSurface ---xx : " + surface);
|
||||
mInternalMediaPlayer.setSurface(surface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(Context context, Uri uri)
|
||||
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
try {
|
||||
LogUtils.d("setDataSource ---xx : ");
|
||||
mInternalMediaPlayer.setDataSource(context, uri);
|
||||
} catch (IOException | IllegalArgumentException | SecurityException |
|
||||
IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
@Override
|
||||
public void setDataSource(Context context, Uri uri, Map<String, String> headers)
|
||||
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
try {
|
||||
LogUtils.d("setDataSource ---yy : ");
|
||||
mInternalMediaPlayer.setDataSource(context, uri, headers);
|
||||
} catch (IOException | IllegalArgumentException | SecurityException |
|
||||
IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(FileDescriptor fd)
|
||||
throws IOException, IllegalArgumentException, IllegalStateException {
|
||||
try {
|
||||
mInternalMediaPlayer.setDataSource(fd);
|
||||
} catch (IOException | IllegalArgumentException | SecurityException |
|
||||
IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(String path) throws IOException,
|
||||
IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
mDataSource = path;
|
||||
|
||||
Uri uri = Uri.parse(path);
|
||||
String scheme = uri.getScheme();
|
||||
if (!TextUtils.isEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
|
||||
mInternalMediaPlayer.setDataSource(uri.getPath());
|
||||
} else {
|
||||
mInternalMediaPlayer.setDataSource(path);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void setDataSource(IMediaDataSource mediaDataSource) {
|
||||
LogUtils.d("setDataSource ---zz : ");
|
||||
|
||||
releaseMediaDataSource();
|
||||
mMediaDataSource = new MediaDataSourceProxy(mediaDataSource);
|
||||
mInternalMediaPlayer.setDataSource(mMediaDataSource);
|
||||
LogUtils.d("setDataSource ---end : ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferPercentage() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
private static class MediaDataSourceProxy extends MediaDataSource {
|
||||
private final IMediaDataSource mMediaDataSource;
|
||||
|
||||
public MediaDataSourceProxy(IMediaDataSource mediaDataSource) {
|
||||
LogUtils.d("mMediaDataSource ---MediaDataSourceProxy : ");
|
||||
mMediaDataSource = mediaDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
|
||||
LogUtils.d("mMediaDataSource ---readAt : ");
|
||||
return mMediaDataSource.readAt(position, buffer, offset, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() throws IOException {
|
||||
LogUtils.d("mMediaDataSource ---getSize : ");
|
||||
return mMediaDataSource.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
LogUtils.d("mMediaDataSource ---close : ");
|
||||
mMediaDataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataSource() {
|
||||
return mDataSource;
|
||||
}
|
||||
|
||||
private void releaseMediaDataSource() {
|
||||
if (mMediaDataSource != null) {
|
||||
LogUtils.d("releaseMediaDataSource --- : ");
|
||||
try {
|
||||
mMediaDataSource.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mMediaDataSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareAsync() throws IllegalStateException {
|
||||
LogUtils.d("prepareAsync ---xx : ");
|
||||
try {
|
||||
mInternalMediaPlayer.prepareAsync();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws IllegalStateException {
|
||||
LogUtils.d("start ---xx : ");
|
||||
try {
|
||||
mInternalMediaPlayer.start();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws IllegalStateException {
|
||||
LogUtils.d("stop ---xx : ");
|
||||
try {
|
||||
mInternalMediaPlayer.stop();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() throws IllegalStateException {
|
||||
LogUtils.d("pause ---xx : ");
|
||||
try {
|
||||
mInternalMediaPlayer.pause();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScreenOnWhilePlaying(boolean screenOn) {
|
||||
mInternalMediaPlayer.setScreenOnWhilePlaying(screenOn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITrackInfo[] getTrackInfo() {
|
||||
return AndroidTrackInfo.fromMediaPlayer(mInternalMediaPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoWidth() {
|
||||
return mInternalMediaPlayer.getVideoWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoHeight() {
|
||||
return mInternalMediaPlayer.getVideoHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoSarNum() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoSarDen() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
try {
|
||||
return mInternalMediaPlayer.isPlaying();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(long msec) throws IllegalStateException {
|
||||
try {
|
||||
mInternalMediaPlayer.seekTo((int) msec);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCurrentPosition() {
|
||||
try {
|
||||
return mInternalMediaPlayer.getCurrentPosition();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
try {
|
||||
return mInternalMediaPlayer.getDuration();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
Log.d(TAG, "Android MediaPlayer -- 进入真正release播放器");
|
||||
try {
|
||||
mIsReleased = true;
|
||||
|
||||
mInternalMediaPlayer.release();
|
||||
releaseMediaDataSource();
|
||||
resetListeners();
|
||||
attachInternalListeners();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
try {
|
||||
mInternalMediaPlayer.reset();
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
releaseMediaDataSource();
|
||||
resetListeners();
|
||||
attachInternalListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSpeed() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
float currentSpeed = mInternalMediaPlayer.getPlaybackParams().getSpeed();
|
||||
if(currentSpeed==1.0){
|
||||
currentSpeed =1;
|
||||
}
|
||||
Log.d("PlaybackSpeed", "当前播放速度: " + currentSpeed + "x");
|
||||
return currentSpeed;
|
||||
} else {
|
||||
Log.d("PlaybackSpeed", "API 23 以下不支持获取播放速度");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLooping(boolean looping) {
|
||||
mInternalMediaPlayer.setLooping(looping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLooping() {
|
||||
return mInternalMediaPlayer.isLooping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(float leftVolume, float rightVolume) {
|
||||
mInternalMediaPlayer.setVolume(leftVolume, rightVolume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAudioSessionId() {
|
||||
return mInternalMediaPlayer.getAudioSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaInfo getMediaInfo() {
|
||||
if (sMediaInfo == null) {
|
||||
MediaInfo module = new MediaInfo();
|
||||
|
||||
module.mVideoDecoder = "android";
|
||||
module.mVideoDecoderImpl = "HW";
|
||||
|
||||
module.mAudioDecoder = "android";
|
||||
module.mAudioDecoderImpl = "HW";
|
||||
|
||||
sMediaInfo = module;
|
||||
}
|
||||
|
||||
return sMediaInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--------------------
|
||||
* misc
|
||||
*/
|
||||
@Override
|
||||
public void setWakeMode(Context context, int mode) {
|
||||
mInternalMediaPlayer.setWakeMode(context, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioStreamType(int streamtype) {
|
||||
mInternalMediaPlayer.setAudioStreamType(streamtype);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepInBackground(boolean keepInBackground) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 倍速播放
|
||||
*
|
||||
* @param speed 倍速 2f
|
||||
*/
|
||||
@Override
|
||||
public void setSpeed(float speed) {
|
||||
LogUtils.d(TAG, "setSpeed: " + speed);
|
||||
// 设置播放速度 (0.5x-2.0x 范围,具体取决于设备支持) Android 6.0 (API 23)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mInternalMediaPlayer.setPlaybackParams(mInternalMediaPlayer.getPlaybackParams()
|
||||
.setSpeed(1.5f)); // 1.5倍速
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------
|
||||
* Listeners adapter
|
||||
*/
|
||||
private void attachInternalListeners() {
|
||||
mInternalMediaPlayer.setOnPreparedListener(mInternalListenerAdapter);
|
||||
mInternalMediaPlayer
|
||||
.setOnBufferingUpdateListener(mInternalListenerAdapter);
|
||||
mInternalMediaPlayer.setOnCompletionListener(mInternalListenerAdapter);
|
||||
mInternalMediaPlayer
|
||||
.setOnSeekCompleteListener(mInternalListenerAdapter);
|
||||
mInternalMediaPlayer
|
||||
.setOnVideoSizeChangedListener(mInternalListenerAdapter);
|
||||
mInternalMediaPlayer.setOnErrorListener(mInternalListenerAdapter);
|
||||
mInternalMediaPlayer.setOnInfoListener(mInternalListenerAdapter);
|
||||
}
|
||||
|
||||
private class AndroidMediaPlayerListenerHolder implements
|
||||
MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener,
|
||||
MediaPlayer.OnBufferingUpdateListener,
|
||||
MediaPlayer.OnSeekCompleteListener,
|
||||
MediaPlayer.OnVideoSizeChangedListener,
|
||||
MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener {
|
||||
public final WeakReference<AndroidMediaPlayer> mWeakMediaPlayer;
|
||||
|
||||
public AndroidMediaPlayerListenerHolder(AndroidMediaPlayer mp) {
|
||||
mWeakMediaPlayer = new WeakReference<AndroidMediaPlayer>(mp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInfo(MediaPlayer mp, int what, int extra) {
|
||||
AndroidMediaPlayer self = mWeakMediaPlayer.get();
|
||||
return self != null && notifyOnInfo(what, extra);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
AndroidMediaPlayer self = mWeakMediaPlayer.get();
|
||||
LogUtils.d("onError ---xx --what : " + what + "--extra -- : " + extra);
|
||||
return self != null && notifyOnError(what, extra);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
|
||||
AndroidMediaPlayer self = mWeakMediaPlayer.get();
|
||||
if (self == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyOnVideoSizeChanged(width, height, 1, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSeekComplete(MediaPlayer mp) {
|
||||
AndroidMediaPlayer self = mWeakMediaPlayer.get();
|
||||
if (self == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyOnSeekComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBufferingUpdate(MediaPlayer mp, int percent) {
|
||||
AndroidMediaPlayer self = mWeakMediaPlayer.get();
|
||||
if (self == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyOnBufferingUpdate(percent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
AndroidMediaPlayer self = mWeakMediaPlayer.get();
|
||||
if (self == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyOnCompletion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
AndroidMediaPlayer self = mWeakMediaPlayer.get();
|
||||
if (self == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyOnPrepared();
|
||||
}
|
||||
}
|
||||
}
|
||||
115
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidTrackInfo.java
Executable file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaFormat;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Build;
|
||||
|
||||
public class AndroidTrackInfo implements ITrackInfo {
|
||||
private final MediaPlayer.TrackInfo mTrackInfo;
|
||||
|
||||
public static AndroidTrackInfo[] fromMediaPlayer(MediaPlayer mp) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return fromTrackInfo(mp.getTrackInfo());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AndroidTrackInfo[] fromTrackInfo(MediaPlayer.TrackInfo[] trackInfos) {
|
||||
if (trackInfos == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AndroidTrackInfo[] androidTrackInfo = new AndroidTrackInfo[trackInfos.length];
|
||||
for (int i = 0; i < trackInfos.length; ++i) {
|
||||
androidTrackInfo[i] = new AndroidTrackInfo(trackInfos[i]);
|
||||
}
|
||||
|
||||
return androidTrackInfo;
|
||||
}
|
||||
|
||||
private AndroidTrackInfo(MediaPlayer.TrackInfo trackInfo) {
|
||||
mTrackInfo = trackInfo;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
public IMediaFormat getFormat() {
|
||||
if (mTrackInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MediaFormat mediaFormat = mTrackInfo.getFormat();
|
||||
if (mediaFormat == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AndroidMediaFormat(mediaFormat);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
if (mTrackInfo == null) {
|
||||
return "und";
|
||||
}
|
||||
|
||||
return mTrackInfo.getLanguage();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
@Override
|
||||
public int getTrackType() {
|
||||
if (mTrackInfo == null) {
|
||||
return MEDIA_TRACK_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return mTrackInfo.getTrackType();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder out = new StringBuilder(128);
|
||||
out.append(getClass().getSimpleName());
|
||||
out.append('{');
|
||||
if (mTrackInfo != null) {
|
||||
out.append(mTrackInfo.toString());
|
||||
} else {
|
||||
out.append("null");
|
||||
}
|
||||
out.append('}');
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
@Override
|
||||
public String getInfoInline() {
|
||||
if (mTrackInfo != null) {
|
||||
return mTrackInfo.toString();
|
||||
} else {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
}
|
||||
66
mediaplayertv/src/main/java/com/pukka/videoplayertv/ConfigPlayer.java
Executable file
@@ -0,0 +1,66 @@
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import com.pukka.libcore.boxShared.BoxSharedParamsUtils;
|
||||
import com.pukka.libcore.utils.LogUtils;
|
||||
import com.pukka.libcore.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* Author: wxf
|
||||
* Date: 2023/8/11 13:44
|
||||
* Description:盒子播放器没有第一帧回调(info=3)的兼容处理
|
||||
*/
|
||||
public class ConfigPlayer {
|
||||
|
||||
private static ConfigPlayer configPlayer;
|
||||
|
||||
public static ConfigPlayer getInstance() {
|
||||
if (configPlayer == null) {
|
||||
synchronized (ConfigPlayer.class) {
|
||||
if (configPlayer == null) {
|
||||
configPlayer = new ConfigPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
return configPlayer;
|
||||
}
|
||||
|
||||
//适配的渠道名单 : 青海
|
||||
public String[] channelIds = {"400500000010315","400500000010321","400500000010319" };
|
||||
//适配的盒子型号
|
||||
public String[] models = {"b860av2.1" };
|
||||
//当前渠道号
|
||||
//private String channelId = BoxSharedParameters.getInstance().getChannelId();
|
||||
private String channelId;
|
||||
//当前的盒子型号
|
||||
private String model = BoxSharedParamsUtils.getInstance().getModel();
|
||||
|
||||
/**
|
||||
* 判断是否兼容盒子未返回第一帧(info=3)
|
||||
* @return true 兼容;false 不兼容
|
||||
**/
|
||||
public boolean isNeedStartFirstFrame() {
|
||||
boolean isNeed = false;
|
||||
if (!StringUtils.isEmpty(channelId) && !StringUtils.isEmpty(model)) {
|
||||
boolean isHaveChannel = false;
|
||||
for (int i=0;i<channelIds.length;i++){
|
||||
if(channelIds[i].equals(channelId)){
|
||||
isHaveChannel = true;
|
||||
}
|
||||
}
|
||||
boolean isHaveModel = false;
|
||||
for (int j=0;j<models.length;j++){
|
||||
if(model.toLowerCase().contains(models[j])){
|
||||
isHaveModel = true;
|
||||
}
|
||||
}
|
||||
if (isHaveChannel && isHaveModel){
|
||||
isNeed = true;
|
||||
}
|
||||
}
|
||||
LogUtils.d("channelId:"+channelId+" model:"+model);
|
||||
LogUtils.d("isNeed:"+isNeed);
|
||||
|
||||
return isNeed;
|
||||
}
|
||||
|
||||
}
|
||||
49
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaController.java
Executable file
@@ -0,0 +1,49 @@
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* 播控相关接口
|
||||
* */
|
||||
public interface IMediaController {
|
||||
|
||||
/**
|
||||
* 隐藏播控
|
||||
* */
|
||||
void hide();
|
||||
/**
|
||||
* 播控是否显示
|
||||
* */
|
||||
boolean isShowing();
|
||||
/**
|
||||
* 播控绑定到VideoViewTv控件上
|
||||
* */
|
||||
void setAnchorView(ViewGroup view);
|
||||
/**
|
||||
* 设置播控状态
|
||||
* */
|
||||
void setEnabled(boolean enabled);
|
||||
/**
|
||||
* 控制设置到VideoView对象上,VideoViewTv实现了MediaPlayerControl接口
|
||||
* */
|
||||
void setMediaPlayer(MediaPlayerControl player);
|
||||
/**
|
||||
* 在屏幕上显示这个控制器。它将在闲置’超时 (timeout)’毫秒到达后自动消失。
|
||||
* */
|
||||
void show(int timeout);
|
||||
/**
|
||||
* 在屏幕上显示这个控制器。它将在3秒以后自动消失。
|
||||
*/
|
||||
void show();
|
||||
/**
|
||||
* @param isffwd true 快进 false 快退
|
||||
* 快进
|
||||
* */
|
||||
void ffwd(boolean isffwd);
|
||||
/**
|
||||
* 快进和快退的定位播放
|
||||
* */
|
||||
void seekToTime();
|
||||
|
||||
}
|
||||
28
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaDataSource.java
Executable file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
public interface IMediaDataSource {
|
||||
int readAt(long position, byte[] buffer, int offset, int size) throws IOException;
|
||||
|
||||
long getSize() throws IOException;
|
||||
|
||||
void close() throws IOException;
|
||||
}
|
||||
30
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaFormat.java
Executable file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
public interface IMediaFormat {
|
||||
// Common keys
|
||||
String KEY_MIME = "mime";
|
||||
|
||||
// Video Keys
|
||||
String KEY_WIDTH = "width";
|
||||
String KEY_HEIGHT = "height";
|
||||
|
||||
String getString(String name);
|
||||
|
||||
int getInteger(String name);
|
||||
}
|
||||
229
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaPlayer.java
Executable file
@@ -0,0 +1,229 @@
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* 播放器相关接口定义和播放器相关常量
|
||||
* */
|
||||
public interface IMediaPlayer {
|
||||
|
||||
int MEDIA_INFO_UNKNOWN = 1;
|
||||
int MEDIA_INFO_STARTED_AS_NEXT = 2;
|
||||
/**第一帧 3*/
|
||||
int MEDIA_INFO_VIDEO_RENDERING_START = 3;
|
||||
int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
|
||||
/**开始缓冲 701*/
|
||||
int MEDIA_INFO_BUFFERING_START = 701;
|
||||
/**结束缓冲 702*/
|
||||
int MEDIA_INFO_BUFFERING_END = 702;
|
||||
int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
|
||||
int MEDIA_INFO_BAD_INTERLEAVING = 800;
|
||||
int MEDIA_INFO_NOT_SEEKABLE = 801;
|
||||
int MEDIA_INFO_METADATA_UPDATE = 802;
|
||||
int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
|
||||
int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
|
||||
int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
|
||||
|
||||
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;
|
||||
int MEDIA_INFO_AUDIO_RENDERING_START = 10002;
|
||||
|
||||
int MEDIA_ERROR_UNKNOWN = 1;
|
||||
int MEDIA_ERROR_SERVER_DIED = 100;
|
||||
int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
|
||||
/**IO异常 -1004*/
|
||||
int MEDIA_ERROR_IO = -1004;
|
||||
int MEDIA_ERROR_MALFORMED = -1007;
|
||||
/**IO异常 -1010*/
|
||||
int MEDIA_ERROR_UNSUPPORTED = -1010;
|
||||
/**超时异常 -110*/
|
||||
int MEDIA_ERROR_TIMED_OUT = -110;
|
||||
|
||||
void setDisplay(SurfaceHolder sh);
|
||||
|
||||
void setDataSource(Context context, Uri uri)
|
||||
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
void setDataSource(Context context, Uri uri, Map<String, String> headers)
|
||||
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
|
||||
|
||||
void setDataSource(FileDescriptor fd)
|
||||
throws IOException, IllegalArgumentException, IllegalStateException;
|
||||
/**
|
||||
* 设置媒体资源
|
||||
* */
|
||||
void setDataSource(String path)
|
||||
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
|
||||
/**
|
||||
* 获取媒体资源
|
||||
* */
|
||||
String getDataSource();
|
||||
/**
|
||||
* 异步准备媒体资源
|
||||
* */
|
||||
void prepareAsync() throws IllegalStateException;
|
||||
/**
|
||||
* 开始播放
|
||||
* */
|
||||
void start() throws IllegalStateException;
|
||||
/**
|
||||
* 停止
|
||||
* */
|
||||
void stop() throws IllegalStateException;
|
||||
/**
|
||||
* 暂停
|
||||
* */
|
||||
void pause() throws IllegalStateException;
|
||||
|
||||
void setScreenOnWhilePlaying(boolean screenOn);
|
||||
|
||||
int getVideoWidth();
|
||||
|
||||
int getVideoHeight();
|
||||
/**
|
||||
* 是否在播放
|
||||
* */
|
||||
boolean isPlaying();
|
||||
/**
|
||||
* 定位播放
|
||||
* */
|
||||
void seekTo(long msec) throws IllegalStateException;
|
||||
/**
|
||||
* 获取当前播放时长
|
||||
* */
|
||||
long getCurrentPosition();
|
||||
/**
|
||||
* 获取播放总时长
|
||||
* */
|
||||
long getDuration();
|
||||
/**
|
||||
* 销毁播放器
|
||||
* */
|
||||
void release();
|
||||
/**
|
||||
* 复位播放器
|
||||
* */
|
||||
void reset();
|
||||
/**
|
||||
* 倍数播
|
||||
* */
|
||||
void setSpeed(float speed);
|
||||
|
||||
float getSpeed();
|
||||
|
||||
void setVolume(float leftVolume, float rightVolume);
|
||||
|
||||
int getAudioSessionId();
|
||||
|
||||
MediaInfo getMediaInfo();
|
||||
|
||||
@Deprecated
|
||||
boolean isPlayable();
|
||||
/**
|
||||
* 播放准备就绪回调
|
||||
* */
|
||||
void setOnPreparedListener(OnPreparedListener listener);
|
||||
/**
|
||||
* 播放结束回调
|
||||
* */
|
||||
void setOnCompletionListener(OnCompletionListener listener);
|
||||
/**
|
||||
* 播放缓冲进度回调
|
||||
* */
|
||||
void setOnBufferingUpdateListener(
|
||||
OnBufferingUpdateListener listener);
|
||||
/**
|
||||
* 定位播放结束回调
|
||||
* */
|
||||
void setOnSeekCompleteListener(
|
||||
OnSeekCompleteListener listener);
|
||||
|
||||
void setOnVideoSizeChangedListener(
|
||||
OnVideoSizeChangedListener listener);
|
||||
/**
|
||||
* 播放错误回调
|
||||
* */
|
||||
void setOnErrorListener(OnErrorListener listener);
|
||||
/**
|
||||
* 播放消息回调
|
||||
* */
|
||||
void setOnInfoListener(OnInfoListener listener);
|
||||
|
||||
/*--------------------
|
||||
* Listeners
|
||||
*/
|
||||
interface OnPreparedListener {
|
||||
void onPrepared(IMediaPlayer mp);
|
||||
}
|
||||
|
||||
interface OnCompletionListener {
|
||||
void onCompletion(IMediaPlayer mp);
|
||||
}
|
||||
|
||||
interface OnBufferingUpdateListener {
|
||||
void onBufferingUpdate(IMediaPlayer mp, int percent);
|
||||
}
|
||||
|
||||
interface OnSeekCompleteListener {
|
||||
void onSeekComplete(IMediaPlayer mp);
|
||||
}
|
||||
|
||||
interface OnVideoSizeChangedListener {
|
||||
void onVideoSizeChanged(IMediaPlayer mp, int width, int height,
|
||||
int sar_num, int sar_den);
|
||||
}
|
||||
|
||||
interface OnErrorListener {
|
||||
boolean onError(IMediaPlayer mp, int what, int extra);
|
||||
}
|
||||
|
||||
interface OnInfoListener {
|
||||
boolean onInfo(IMediaPlayer mp, int what, int extra);
|
||||
}
|
||||
|
||||
/*--------------------
|
||||
* Optional
|
||||
*/
|
||||
void setAudioStreamType(int streamtype);
|
||||
|
||||
@Deprecated
|
||||
void setKeepInBackground(boolean keepInBackground);
|
||||
|
||||
int getVideoSarNum();
|
||||
|
||||
int getVideoSarDen();
|
||||
|
||||
@Deprecated
|
||||
void setWakeMode(Context context, int mode);
|
||||
|
||||
void setLooping(boolean looping);
|
||||
|
||||
boolean isLooping();
|
||||
|
||||
/*--------------------
|
||||
* AndroidMediaPlayer: JELLY_BEAN
|
||||
*/
|
||||
ITrackInfo[] getTrackInfo();
|
||||
|
||||
/*--------------------
|
||||
* AndroidMediaPlayer: ICE_CREAM_SANDWICH:
|
||||
*/
|
||||
void setSurface(Surface surface);
|
||||
|
||||
/*--------------------
|
||||
* AndroidMediaPlayer: M:
|
||||
*/
|
||||
void setDataSource(IMediaDataSource mediaDataSource);
|
||||
/**
|
||||
* exoPlayer getBufferPercentage
|
||||
* */
|
||||
int getBufferPercentage();
|
||||
}
|
||||
93
mediaplayertv/src/main/java/com/pukka/videoplayertv/IRenderView.java
Executable file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* 画布相关接口
|
||||
* */
|
||||
public interface IRenderView {
|
||||
int AR_ASPECT_FIT_PARENT = 0; // without clip 没有剪切 0视频分辨率小于盒子分辨率会自动拉伸铺满全屏 对于竖屏视频 只高度铺满 宽度不拉伸(一边铺满)
|
||||
int AR_ASPECT_FILL_PARENT = 1; // may clip 剪切 强制全屏 所有类型的视频均会拉伸铺满
|
||||
int AR_ASPECT_WRAP_CONTENT = 2; //原画幅,对于视频分辨率小于盒子分辨率的 会只展示中间一块内容 四周黑窗
|
||||
int AR_MATCH_PARENT = 3;//铺满,
|
||||
int AR_16_9_FIT_PARENT = 4;//拉伸。
|
||||
int AR_4_3_FIT_PARENT = 5;//按比例缩放
|
||||
|
||||
/**
|
||||
* 获得view
|
||||
* */
|
||||
View getView();
|
||||
|
||||
boolean shouldWaitForResize();
|
||||
|
||||
void setVideoSize(int videoWidth, int videoHeight);
|
||||
|
||||
void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
|
||||
|
||||
void setVideoRotation(int degree);
|
||||
|
||||
void setAspectRatio(int aspectRatio);
|
||||
|
||||
//添加回调
|
||||
void addRenderCallback(@NonNull IRenderCallback callback);
|
||||
//取消回调
|
||||
void removeRenderCallback(@NonNull IRenderCallback callback);
|
||||
|
||||
interface ISurfaceHolder {
|
||||
//绑定到视频播放器
|
||||
void bindToMediaPlayer(IMediaPlayer mp);
|
||||
|
||||
@NonNull
|
||||
IRenderView getRenderView();
|
||||
|
||||
@Nullable
|
||||
SurfaceHolder getSurfaceHolder();
|
||||
|
||||
@Nullable
|
||||
Surface openSurface();
|
||||
|
||||
@Nullable
|
||||
SurfaceTexture getSurfaceTexture();
|
||||
}
|
||||
|
||||
interface IRenderCallback {
|
||||
/**
|
||||
* @param holder
|
||||
* @param width could be 0
|
||||
* @param height could be 0
|
||||
*/
|
||||
void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);
|
||||
|
||||
/**
|
||||
* @param holder
|
||||
* @param format could be 0
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);
|
||||
|
||||
void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
|
||||
public interface ISurfaceTextureHolder {
|
||||
void setSurfaceTexture(SurfaceTexture surfaceTexture);
|
||||
|
||||
SurfaceTexture getSurfaceTexture();
|
||||
|
||||
void setSurfaceTextureHost(ISurfaceTextureHost surfaceTextureHost);
|
||||
}
|
||||
23
mediaplayertv/src/main/java/com/pukka/videoplayertv/ISurfaceTextureHost.java
Executable file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
|
||||
public interface ISurfaceTextureHost {
|
||||
void releaseSurfaceTexture(SurfaceTexture surfaceTexture);
|
||||
}
|
||||
34
mediaplayertv/src/main/java/com/pukka/videoplayertv/ITrackInfo.java
Executable file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
public interface ITrackInfo {
|
||||
int MEDIA_TRACK_TYPE_AUDIO = 2;
|
||||
int MEDIA_TRACK_TYPE_METADATA = 5;
|
||||
int MEDIA_TRACK_TYPE_SUBTITLE = 4;
|
||||
int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
|
||||
int MEDIA_TRACK_TYPE_UNKNOWN = 0;
|
||||
int MEDIA_TRACK_TYPE_VIDEO = 1;
|
||||
|
||||
IMediaFormat getFormat();
|
||||
|
||||
String getLanguage();
|
||||
|
||||
int getTrackType();
|
||||
|
||||
String getInfoInline();
|
||||
}
|
||||
235
mediaplayertv/src/main/java/com/pukka/videoplayertv/MeasureHelper.java
Executable file
@@ -0,0 +1,235 @@
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.pukka.libcore.utils.LogUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/***
|
||||
* 绘图测量帮助类
|
||||
*/
|
||||
public final class MeasureHelper {
|
||||
private static final String TAG = "VideoViewTv";
|
||||
private WeakReference<View> mWeakView;
|
||||
//视频宽
|
||||
private int mVideoWidth;
|
||||
//视频高
|
||||
private int mVideoHeight;
|
||||
private int mVideoSarNum;
|
||||
private int mVideoSarDen;
|
||||
|
||||
//视频旋转度
|
||||
private int mVideoRotationDegree;
|
||||
//测量宽度
|
||||
private int mMeasuredWidth;
|
||||
//测量高度
|
||||
private int mMeasuredHeight;
|
||||
//视频显示方式
|
||||
private int mCurrentAspectRatio = IRenderView.AR_16_9_FIT_PARENT;
|
||||
|
||||
public MeasureHelper(View view) {
|
||||
mWeakView = new WeakReference<View>(view);
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
if (mWeakView == null) {
|
||||
return null;
|
||||
}
|
||||
return mWeakView.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视频的现实大小
|
||||
*/
|
||||
public void setVideoSize(int videoWidth, int videoHeight) {
|
||||
mVideoWidth = videoWidth;
|
||||
mVideoHeight = videoHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置纵横比
|
||||
*/
|
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
|
||||
mVideoSarNum = videoSarNum;
|
||||
mVideoSarDen = videoSarDen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置旋转度
|
||||
*/
|
||||
public void setVideoRotation(int videoRotationDegree) {
|
||||
mVideoRotationDegree = videoRotationDegree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called by View.onMeasure(int, int)
|
||||
*
|
||||
* @param widthMeasureSpec
|
||||
* @param heightMeasureSpec
|
||||
*/
|
||||
public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// Log.i("wxf", "onMeasure(" + widthMeasureSpec + ", "
|
||||
// + heightMeasureSpec + ")");
|
||||
|
||||
LogUtils.d("doMeasure --start-- - width : " + widthMeasureSpec + " videoHeight: " + heightMeasureSpec);
|
||||
|
||||
//切屏
|
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
|
||||
int tempSpec = widthMeasureSpec;
|
||||
widthMeasureSpec = heightMeasureSpec;
|
||||
heightMeasureSpec = tempSpec;
|
||||
}
|
||||
|
||||
int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
|
||||
int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
|
||||
if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
|
||||
width = widthMeasureSpec;
|
||||
height = heightMeasureSpec;
|
||||
} else if (mVideoWidth > 0 && mVideoHeight > 0) {
|
||||
int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
|
||||
int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
|
||||
int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
|
||||
//MeasureSpec.AT_MOST (至多模式)
|
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
|
||||
float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
|
||||
float displayAspectRatio;
|
||||
Log.i("wxf", "onMeasure() mCurrentAspectRatio" + mCurrentAspectRatio);
|
||||
switch (mCurrentAspectRatio) {
|
||||
case IRenderView.AR_16_9_FIT_PARENT:
|
||||
displayAspectRatio = 16.0f / 9.0f;
|
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
|
||||
displayAspectRatio = 1.0f / displayAspectRatio;
|
||||
}
|
||||
break;
|
||||
case IRenderView.AR_4_3_FIT_PARENT:
|
||||
displayAspectRatio = 4.0f / 3.0f;
|
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
|
||||
displayAspectRatio = 1.0f / displayAspectRatio;
|
||||
}
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_FIT_PARENT:
|
||||
case IRenderView.AR_ASPECT_FILL_PARENT:
|
||||
case IRenderView.AR_ASPECT_WRAP_CONTENT:
|
||||
default:
|
||||
displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
|
||||
if (mVideoSarNum > 0 && mVideoSarDen > 0) {
|
||||
displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
|
||||
}
|
||||
break;
|
||||
}
|
||||
boolean shouldBeWider = displayAspectRatio > specAspectRatio;
|
||||
LogUtils.d("是否固定宽度 shouldBeWider: " + shouldBeWider + "实际视频宽高比 displayAspectRatio: " + displayAspectRatio+" specAspectRatio:"+specAspectRatio);
|
||||
switch (mCurrentAspectRatio) {
|
||||
case IRenderView.AR_ASPECT_FIT_PARENT:
|
||||
case IRenderView.AR_16_9_FIT_PARENT:
|
||||
case IRenderView.AR_4_3_FIT_PARENT:
|
||||
if (shouldBeWider) {
|
||||
// too wide, fix width 太宽,固定宽度
|
||||
LogUtils.d("固定宽度为屏幕宽度,高度保存比例缩放:"+mCurrentAspectRatio);
|
||||
width = widthSpecSize;
|
||||
height = (int) (width / displayAspectRatio);
|
||||
} else {
|
||||
// too high, fix height
|
||||
LogUtils.d("固定高度为屏幕高度,宽度保存比例缩放:"+mCurrentAspectRatio);
|
||||
height = heightSpecSize;
|
||||
width = (int) (height * displayAspectRatio);
|
||||
}
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_FILL_PARENT:
|
||||
if (shouldBeWider) {
|
||||
// not high enough, fix height
|
||||
LogUtils.d("AR_ASPECT_FILL_PARENT 固定高度,宽度保存比例缩放");
|
||||
height = heightSpecSize;
|
||||
width = (int) (height * displayAspectRatio);
|
||||
} else {
|
||||
// not wide enough, fix width
|
||||
LogUtils.d("AR_ASPECT_FILL_PARENT 固定宽度,高度保存比例缩放");
|
||||
width = widthSpecSize;
|
||||
height = (int) (width / displayAspectRatio);
|
||||
}
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_WRAP_CONTENT:
|
||||
default:
|
||||
if (shouldBeWider) {
|
||||
// too wide, fix width
|
||||
width = Math.min(mVideoWidth, widthSpecSize);
|
||||
height = (int) (width / displayAspectRatio);
|
||||
} else {
|
||||
// too high, fix height
|
||||
height = Math.min(mVideoHeight, heightSpecSize);
|
||||
width = (int) (height * displayAspectRatio);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
|
||||
// the size is fixed
|
||||
width = widthSpecSize;
|
||||
height = heightSpecSize;
|
||||
|
||||
// for compatibility, we adjust size based on aspect ratio
|
||||
if (mVideoWidth * height < width * mVideoHeight) {
|
||||
//Log.i("@@@", "image too wide, correcting");
|
||||
width = height * mVideoWidth / mVideoHeight;
|
||||
} else if (mVideoWidth * height > width * mVideoHeight) {
|
||||
//Log.i("@@@", "image too tall, correcting");
|
||||
height = width * mVideoHeight / mVideoWidth;
|
||||
}
|
||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
|
||||
// only the width is fixed, adjust the height to match aspect ratio if possible
|
||||
width = widthSpecSize;
|
||||
height = width * mVideoHeight / mVideoWidth;
|
||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
|
||||
// couldn't match aspect ratio within the constraints
|
||||
height = heightSpecSize;
|
||||
}
|
||||
} else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
|
||||
// only the height is fixed, adjust the width to match aspect ratio if possible
|
||||
height = heightSpecSize;
|
||||
width = height * mVideoWidth / mVideoHeight;
|
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
|
||||
// couldn't match aspect ratio within the constraints
|
||||
width = widthSpecSize;
|
||||
}
|
||||
} else {
|
||||
// neither the width nor the height are fixed, try to use actual video size
|
||||
width = mVideoWidth;
|
||||
height = mVideoHeight;
|
||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
|
||||
// too tall, decrease both width and height
|
||||
height = heightSpecSize;
|
||||
width = height * mVideoWidth / mVideoHeight;
|
||||
}
|
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
|
||||
// too wide, decrease both width and height
|
||||
width = widthSpecSize;
|
||||
height = width * mVideoHeight / mVideoWidth;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no size yet, just adopt the given spec sizes
|
||||
}
|
||||
LogUtils.d("doMeasure --end -- 视频宽videowidth : " + width + " 视频高videoHeight: " + height);
|
||||
mMeasuredWidth = width;
|
||||
mMeasuredHeight = height;
|
||||
}
|
||||
|
||||
public int getMeasuredWidth() {
|
||||
LogUtils.d("getMeasuredWidth : " + mMeasuredWidth);
|
||||
return mMeasuredWidth;
|
||||
}
|
||||
|
||||
public int getMeasuredHeight() {
|
||||
LogUtils.d("getMeasuredHeight : " + mMeasuredHeight);
|
||||
return mMeasuredHeight;
|
||||
}
|
||||
|
||||
public void setAspectRatio(int aspectRatio) {
|
||||
mCurrentAspectRatio = aspectRatio;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
14
mediaplayertv/src/main/java/com/pukka/videoplayertv/MediaInfo.java
Executable file
@@ -0,0 +1,14 @@
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
public class MediaInfo {
|
||||
public String mMediaPlayerName;
|
||||
|
||||
public String mVideoDecoder;
|
||||
public String mVideoDecoderImpl;
|
||||
|
||||
public String mAudioDecoder;
|
||||
public String mAudioDecoderImpl;
|
||||
|
||||
//public IjkMediaMeta mMeta;
|
||||
}
|
||||
18
mediaplayertv/src/main/java/com/pukka/videoplayertv/MediaPlayerControl.java
Executable file
@@ -0,0 +1,18 @@
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
/**
|
||||
* 媒体控制器控制具体操作接口
|
||||
* */
|
||||
public interface MediaPlayerControl {
|
||||
void start();
|
||||
void pause();
|
||||
int getDuration();
|
||||
int getCurrentPosition();
|
||||
void seekTo(int pos);
|
||||
boolean isPlaying();
|
||||
int getBufferPercentage();
|
||||
boolean canPause();
|
||||
boolean canSeekBackward();
|
||||
boolean canSeekForward();
|
||||
int getCurrentMode();
|
||||
}
|
||||
398
mediaplayertv/src/main/java/com/pukka/videoplayertv/SoftMediaPlayer.java
Executable file
@@ -0,0 +1,398 @@
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import tv.danmaku.ijk.media.bmiptvplayer.HyMediaPlayer;
|
||||
import tv.danmaku.ijk.media.bmiptvplayer.IMediaPlayer;
|
||||
|
||||
/**
|
||||
* Author: xjy
|
||||
* Date: 2022/11/23 14:12
|
||||
* Description: 软终端播放器
|
||||
*/
|
||||
public class SoftMediaPlayer extends AbstractMediaPlayer {
|
||||
private static final String TAG = "SoftMediaPlayer";
|
||||
//软终端播放器
|
||||
private final HyMediaPlayer mHyMediaPlayer;
|
||||
private final Object mInitLock = new Object();
|
||||
//是否销毁
|
||||
private boolean mIsReleased;
|
||||
/**
|
||||
*初始化
|
||||
* */
|
||||
public SoftMediaPlayer(Context context) {
|
||||
synchronized (mInitLock) {
|
||||
mHyMediaPlayer = new HyMediaPlayer(context);
|
||||
initLister();
|
||||
}
|
||||
}
|
||||
/**
|
||||
*初始化
|
||||
* */
|
||||
public SoftMediaPlayer(Context context, int playerType){
|
||||
synchronized (mInitLock) {
|
||||
mHyMediaPlayer = new HyMediaPlayer(context,playerType);
|
||||
initLister();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置SurfaceHolder
|
||||
* */
|
||||
@Override
|
||||
public void setDisplay(SurfaceHolder sh) {
|
||||
if(null !=mHyMediaPlayer) {
|
||||
if(!mIsReleased) {
|
||||
mHyMediaPlayer.setDisplay(sh);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置视频源
|
||||
* */
|
||||
@Override
|
||||
public void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setDataSource(context,uri);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置视频源
|
||||
* */
|
||||
@Override
|
||||
public void setDataSource(Context context, Uri uri, Map<String, String> headers) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setDataSource(context,uri,headers);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置视频源
|
||||
* */
|
||||
@Override
|
||||
public void setDataSource(FileDescriptor fd) throws IOException, IllegalArgumentException, IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setDataSource(fd);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置视频源
|
||||
* */
|
||||
@Override
|
||||
public void setDataSource(String path) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setDataSource(path);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取视频源
|
||||
* */
|
||||
@Override
|
||||
public String getDataSource() {
|
||||
if(null != mHyMediaPlayer){
|
||||
return mHyMediaPlayer.getDataSource();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 开始准备
|
||||
* */
|
||||
@Override
|
||||
public void prepareAsync() throws IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.prepareAsync();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 播放
|
||||
* */
|
||||
@Override
|
||||
public void start() throws IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.start();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 停止
|
||||
* */
|
||||
@Override
|
||||
public void stop() throws IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.stop();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 暂停
|
||||
* */
|
||||
@Override
|
||||
public void pause() throws IllegalStateException {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.pause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScreenOnWhilePlaying(boolean screenOn) {
|
||||
if(null!= mHyMediaPlayer){
|
||||
mHyMediaPlayer.setScreenOnWhilePlaying(screenOn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoWidth() {
|
||||
if(null != mHyMediaPlayer) {
|
||||
return mHyMediaPlayer.getVideoWidth();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoHeight() {
|
||||
if(null != mHyMediaPlayer) {
|
||||
return mHyMediaPlayer.getVideoHeight();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* 是否在播放
|
||||
* */
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
if(null != mHyMediaPlayer) {
|
||||
return mHyMediaPlayer.isPlaying();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 定点播放
|
||||
* */
|
||||
@Override
|
||||
public void seekTo(long msec) throws IllegalStateException {
|
||||
if(null != mHyMediaPlayer) {
|
||||
mHyMediaPlayer.seekTo(msec);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 当前Position
|
||||
* */
|
||||
@Override
|
||||
public long getCurrentPosition() {
|
||||
if(null != mHyMediaPlayer){
|
||||
return mHyMediaPlayer.getCurrentPosition();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* 获取Duration
|
||||
* */
|
||||
@Override
|
||||
public long getDuration() {
|
||||
if(null != mHyMediaPlayer){
|
||||
return mHyMediaPlayer.getDuration();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* 销毁播放器
|
||||
* */
|
||||
@Override
|
||||
public void release() {
|
||||
if(null != mHyMediaPlayer){
|
||||
mIsReleased = true;
|
||||
mHyMediaPlayer.release();
|
||||
resetListeners();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 复位
|
||||
* */
|
||||
@Override
|
||||
public void reset() {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.reset();
|
||||
resetListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpeed(float speed) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSpeed() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置声音
|
||||
* */
|
||||
@Override
|
||||
public void setVolume(float leftVolume, float rightVolume) {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setVolume(leftVolume, rightVolume);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAudioSessionId() {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.getAudioSessionId();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaInfo getMediaInfo() {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.getMediaInfo();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLogEnabled(boolean enable) {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setLogEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayable() {
|
||||
if(null != mHyMediaPlayer){
|
||||
return mHyMediaPlayer.isPlayable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioStreamType(int streamtype) {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setAudioStreamType(streamtype);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepInBackground(boolean keepInBackground) {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setKeepInBackground(keepInBackground);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoSarNum() {
|
||||
if(null != mHyMediaPlayer){
|
||||
return mHyMediaPlayer.getVideoSarNum();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoSarDen() {
|
||||
if(null != mHyMediaPlayer){
|
||||
return mHyMediaPlayer.getVideoSarDen();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWakeMode(Context context, int mode) {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setWakeMode(context,mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLooping(boolean looping) {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setLooping(looping);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLooping() {
|
||||
if(null != mHyMediaPlayer){
|
||||
return mHyMediaPlayer.isLooping();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITrackInfo[] getTrackInfo() {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 设置Surface
|
||||
* */
|
||||
@Override
|
||||
public void setSurface(Surface surface) {
|
||||
if(null != mHyMediaPlayer){
|
||||
mHyMediaPlayer.setSurface(surface);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferPercentage() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放器回调
|
||||
* onInfo onBufferingUpdate onCompletion onError onPrepared onSeekComplete onVideoSizeChanged
|
||||
* */
|
||||
private void initLister(){
|
||||
if(null == mHyMediaPlayer){
|
||||
return;
|
||||
}
|
||||
mHyMediaPlayer.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
|
||||
@Override
|
||||
public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
|
||||
// TODO: 2023/4/2 放开会报错。。不知道为什么
|
||||
notifyOnInfo(i,i1);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
mHyMediaPlayer.setOnBufferingUpdateListener(new IMediaPlayer.OnBufferingUpdateListener() {
|
||||
@Override
|
||||
public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int i) {
|
||||
notifyOnBufferingUpdate(i);
|
||||
}
|
||||
});
|
||||
mHyMediaPlayer.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(IMediaPlayer iMediaPlayer) {
|
||||
notifyOnCompletion();
|
||||
}
|
||||
});
|
||||
mHyMediaPlayer.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
|
||||
@Override
|
||||
public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {
|
||||
notifyOnError(i,i1);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
mHyMediaPlayer.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared(IMediaPlayer iMediaPlayer) {
|
||||
notifyOnPrepared();
|
||||
}
|
||||
});
|
||||
mHyMediaPlayer.setOnSeekCompleteListener(new IMediaPlayer.OnSeekCompleteListener() {
|
||||
@Override
|
||||
public void onSeekComplete(IMediaPlayer iMediaPlayer) {
|
||||
notifyOnSeekComplete();
|
||||
}
|
||||
});
|
||||
mHyMediaPlayer.setOnVideoSizeChangedListener(new IMediaPlayer.OnVideoSizeChangedListener() {
|
||||
@Override
|
||||
public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) {
|
||||
notifyOnVideoSizeChanged(i,i1,i2,i3);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
318
mediaplayertv/src/main/java/com/pukka/videoplayertv/SurfaceRenderView.java
Executable file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.pukka.libcore.utils.LogUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* 播放视频的SurfaceView
|
||||
*/
|
||||
public class SurfaceRenderView extends SurfaceView implements IRenderView {
|
||||
private static final String TAG = "VideoViewTv";
|
||||
//测量帮助类
|
||||
private MeasureHelper mMeasureHelper;
|
||||
//SurfaceCallback
|
||||
private SurfaceCallback mSurfaceCallback;
|
||||
int sWidth = 0;
|
||||
int sHeight = 0;
|
||||
|
||||
public SurfaceRenderView(Context context) {
|
||||
super(context);
|
||||
initView();
|
||||
}
|
||||
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView();
|
||||
}
|
||||
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initView();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
// 实例化measureHelper
|
||||
mMeasureHelper = new MeasureHelper(this);
|
||||
// 实例化surfacecallback
|
||||
mSurfaceCallback = new SurfaceCallback(this);
|
||||
getHolder().addCallback(mSurfaceCallback);
|
||||
// noinspection deprecation
|
||||
getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
|
||||
}
|
||||
|
||||
//IRenderView
|
||||
@Override
|
||||
public View getView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
//IRenderView
|
||||
@Override
|
||||
public boolean shouldWaitForResize() {
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// IRenderView
|
||||
// Layout & Measure
|
||||
//--------------------
|
||||
@Override
|
||||
public void setVideoSize(int videoWidth, int videoHeight) {
|
||||
if (videoWidth > 0 && videoHeight > 0) {
|
||||
LogUtils.d("setVideoSize -- width: " + videoWidth + "--videoHeight : " + videoHeight);
|
||||
mMeasureHelper.setVideoSize(videoWidth, videoHeight);
|
||||
getHolder().setFixedSize(videoWidth, videoHeight);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
|
||||
if (videoSarNum > 0 && videoSarDen > 0) {
|
||||
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
|
||||
LogUtils.d("setVideoSampleAspectRatio -- videoSarNum : " + videoSarNum);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoRotation(int degree) {
|
||||
Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAspectRatio(int aspectRatio) {
|
||||
Log.d("wxf", "setAspectRatio - " + aspectRatio);
|
||||
mMeasureHelper.setAspectRatio(aspectRatio);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
//设置控件的宽和高
|
||||
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// SurfaceViewHolder
|
||||
//--------------------
|
||||
|
||||
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
|
||||
|
||||
//播放视频的VIEW
|
||||
private SurfaceRenderView mSurfaceView;
|
||||
//播放视频的surfaceHolder
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
|
||||
public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView,
|
||||
@Nullable SurfaceHolder surfaceHolder) {
|
||||
mSurfaceView = surfaceView;
|
||||
mSurfaceHolder = surfaceHolder;
|
||||
LogUtils.d("InternalSurfaceHolder --mSurfaceHolder-- " + mSurfaceHolder + " -- mSurfaceView :" + mSurfaceView);
|
||||
}
|
||||
|
||||
//surfaceHolder 传递给IMediaPlayer
|
||||
@Override
|
||||
public void bindToMediaPlayer(IMediaPlayer mp) {
|
||||
if (mp != null) {
|
||||
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
|
||||
(mp instanceof ISurfaceTextureHolder)) {
|
||||
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
|
||||
textureHolder.setSurfaceTexture(null);
|
||||
}
|
||||
LogUtils.d("bindToMediaPlayer --mSurfaceHolder-- " + mSurfaceHolder);
|
||||
mp.setDisplay(mSurfaceHolder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public IRenderView getRenderView() {
|
||||
return mSurfaceView;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SurfaceHolder getSurfaceHolder() {
|
||||
return mSurfaceHolder;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SurfaceTexture getSurfaceTexture() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Surface openSurface() {
|
||||
if (mSurfaceHolder == null) {
|
||||
return null;
|
||||
}
|
||||
return mSurfaceHolder.getSurface();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// SurfaceHolder.Callback
|
||||
//-------------------------
|
||||
|
||||
@Override
|
||||
public void addRenderCallback(IRenderCallback callback) {
|
||||
mSurfaceCallback.addRenderCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRenderCallback(IRenderCallback callback) {
|
||||
mSurfaceCallback.removeRenderCallback(callback);
|
||||
}
|
||||
|
||||
//private SurfaceCallback mSurfaceCallback;
|
||||
|
||||
private static final class SurfaceCallback implements SurfaceHolder.Callback {
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
private boolean mIsFormatChanged;
|
||||
private int mFormat;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
//弱引用 SurfaceRenderView
|
||||
private WeakReference<SurfaceRenderView> mWeakSurfaceView;
|
||||
private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();
|
||||
|
||||
public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) {
|
||||
mWeakSurfaceView = new WeakReference<SurfaceRenderView>(surfaceView);
|
||||
}
|
||||
|
||||
public void addRenderCallback(@NonNull IRenderCallback callback) {
|
||||
mRenderCallbackMap.put(callback, callback);
|
||||
|
||||
ISurfaceHolder surfaceHolder = null;
|
||||
if (mSurfaceHolder != null) {
|
||||
if (surfaceHolder == null) {
|
||||
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
}
|
||||
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
|
||||
}
|
||||
|
||||
if (mIsFormatChanged) {
|
||||
if (surfaceHolder == null) {
|
||||
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
}
|
||||
callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeRenderCallback(@NonNull IRenderCallback callback) {
|
||||
mRenderCallbackMap.remove(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.d(TAG, "surfaceCreated: ");
|
||||
mSurfaceHolder = holder;
|
||||
mIsFormatChanged = false;
|
||||
mFormat = 0;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.d(TAG, "surfaceDestroyed: ");
|
||||
mSurfaceHolder = null;
|
||||
mIsFormatChanged = false;
|
||||
mFormat = 0;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceDestroyed(surfaceHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format,
|
||||
int width, int height) {
|
||||
Log.d(TAG, "surfaceChanged: ");
|
||||
mSurfaceHolder = holder;
|
||||
mIsFormatChanged = true;
|
||||
mFormat = format;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
// mMeasureHelper.setVideoSize(width, height);
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Accessibility
|
||||
//--------------------
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
event.setClassName(SurfaceRenderView.class.getName());
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
info.setClassName(SurfaceRenderView.class.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
1052
mediaplayertv/src/main/java/com/pukka/videoplayertv/TvBaseController.java
Executable file
170
mediaplayertv/src/main/java/com/pukka/videoplayertv/TvMediaController.java
Executable file
@@ -0,0 +1,170 @@
|
||||
|
||||
package com.pukka.videoplayertv;
|
||||
import android.app.ActionBar;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.pukka.mediaplayertv.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Author: wxf
|
||||
* Date: 2023/4/5 10:00
|
||||
* Description:点播播控
|
||||
* */
|
||||
public class TvMediaController extends TvBaseController implements IMediaController {
|
||||
|
||||
private ActionBar mActionBar;
|
||||
private RelativeLayout mTouchVolumePanel;
|
||||
private SeekBar mTouchVolumeSeekBar;
|
||||
private TextView mTouchVolumeValueTv;
|
||||
private AudioManager mAudioManager;
|
||||
private boolean mIsTouchMode;
|
||||
|
||||
public TvMediaController(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public TvMediaController(Context context, boolean useFastForward) {
|
||||
super(context, useFastForward);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public TvMediaController(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
private void initView(Context context) {
|
||||
mAudioManager = (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTouchModeEnabled(boolean isTouchMode) {
|
||||
mIsTouchMode = isTouchMode;
|
||||
updateTouchVolumeVisibility();
|
||||
if (mIsTouchMode) {
|
||||
syncSystemVolumeToUi();
|
||||
}
|
||||
super.setTouchModeEnabled(isTouchMode);
|
||||
}
|
||||
|
||||
private void bindTouchVolumeViews() {
|
||||
mTouchVolumePanel = findViewById(R.id.touch_volume_panel);
|
||||
mTouchVolumeSeekBar = findViewById(R.id.sb_touch_volume);
|
||||
mTouchVolumeValueTv = findViewById(R.id.tv_touch_volume_value);
|
||||
if (mTouchVolumeSeekBar != null) {
|
||||
mTouchVolumeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (!fromUser) {
|
||||
return;
|
||||
}
|
||||
if (mAudioManager != null) {
|
||||
int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
||||
int targetVolume = (int) ((progress / 100f) * maxVolume);
|
||||
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, targetVolume, 0);
|
||||
}
|
||||
if (mTouchVolumeValueTv != null) {
|
||||
mTouchVolumeValueTv.setText(String.valueOf(progress));
|
||||
}
|
||||
show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
show();
|
||||
}
|
||||
});
|
||||
}
|
||||
updateTouchVolumeVisibility();
|
||||
syncSystemVolumeToUi();
|
||||
}
|
||||
|
||||
private void updateTouchVolumeVisibility() {
|
||||
if (mTouchVolumePanel != null) {
|
||||
mTouchVolumePanel.setVisibility(mIsTouchMode ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void syncSystemVolumeToUi() {
|
||||
if (mAudioManager == null || mTouchVolumeSeekBar == null || mTouchVolumeValueTv == null) {
|
||||
return;
|
||||
}
|
||||
int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
||||
int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
|
||||
mTouchVolumeSeekBar.setMax(100);
|
||||
int progress = maxVolume > 0 ? (int) ((currentVolume * 100f) / maxVolume) : 0;
|
||||
mTouchVolumeSeekBar.setProgress(progress);
|
||||
mTouchVolumeValueTv.setText(String.valueOf(progress));
|
||||
}
|
||||
|
||||
public void setSupportActionBar(@Nullable ActionBar actionBar) {
|
||||
mActionBar = actionBar;
|
||||
if (isShowing()) {
|
||||
//媒体控制器显示,则actionBar显示
|
||||
actionBar.show();
|
||||
} else {
|
||||
actionBar.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
syncSystemVolumeToUi();
|
||||
super.show();
|
||||
//Log.e("wxf", "show 1111");
|
||||
//显示媒体控制器
|
||||
if (mActionBar != null)
|
||||
//显示actionBar
|
||||
//Log.e("wxf","show 2222");
|
||||
{
|
||||
mActionBar.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
super.hide();
|
||||
if (mActionBar != null) {
|
||||
mActionBar.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnchorView(ViewGroup view) {
|
||||
super.setAnchorView(view);
|
||||
bindTouchVolumeViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaPlayer(MediaPlayerControl player) {
|
||||
super.setMediaPlayer(player);
|
||||
}
|
||||
|
||||
//----------
|
||||
// Extends
|
||||
//----------
|
||||
private ArrayList<View> mShowOnceArray = new ArrayList<View>();
|
||||
|
||||
public void showOnce(@NonNull View view) {
|
||||
mShowOnceArray.add(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
show();
|
||||
}
|
||||
}
|
||||
1583
mediaplayertv/src/main/java/com/pukka/videoplayertv/VideoViewTv.java
Executable file
@@ -0,0 +1,169 @@
|
||||
package com.pukka.videoplayertv.exoplayer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
||||
import com.google.android.exoplayer2.source.rtsp.RtspMediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ExoMediaSourceHelper {
|
||||
|
||||
private static volatile ExoMediaSourceHelper sInstance;
|
||||
|
||||
private final String mUserAgent;
|
||||
private final Context mAppContext;
|
||||
private HttpDataSource.Factory mHttpDataSourceFactory;
|
||||
private Cache mCache;
|
||||
|
||||
private ExoMediaSourceHelper(Context context) {
|
||||
mAppContext = context.getApplicationContext();
|
||||
mUserAgent = Util.getUserAgent(mAppContext, mAppContext.getApplicationInfo().name);
|
||||
}
|
||||
|
||||
public static ExoMediaSourceHelper getInstance(Context context) {
|
||||
if (sInstance == null) {
|
||||
synchronized (ExoMediaSourceHelper.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ExoMediaSourceHelper(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public MediaSource getMediaSource(String uri) {
|
||||
return getMediaSource(uri, null, false);
|
||||
}
|
||||
|
||||
public MediaSource getMediaSource(String uri, Map<String, String> headers) {
|
||||
return getMediaSource(uri, headers, false);
|
||||
}
|
||||
|
||||
public MediaSource getMediaSource(String uri, boolean isCache) {
|
||||
return getMediaSource(uri, null, isCache);
|
||||
}
|
||||
|
||||
public MediaSource getMediaSource(String uri, Map<String, String> headers, boolean isCache) {
|
||||
Uri contentUri = Uri.parse(uri);
|
||||
// if ("rtmp".equals(contentUri.getScheme())) {
|
||||
// return new ProgressiveMediaSource.Factory(new RtmpDataSource.Factory())
|
||||
// .createMediaSource(MediaItem.fromUri(contentUri));
|
||||
// } else
|
||||
if ("rtsp".equals(contentUri.getScheme())) {
|
||||
return new RtspMediaSource.Factory().createMediaSource(MediaItem.fromUri(contentUri));
|
||||
}
|
||||
int contentType = inferContentType(uri);
|
||||
DataSource.Factory factory;
|
||||
if (isCache) {
|
||||
factory = getCacheDataSourceFactory();
|
||||
} else {
|
||||
factory = getDataSourceFactory();
|
||||
}
|
||||
if (mHttpDataSourceFactory != null) {
|
||||
setHeaders(headers);
|
||||
}
|
||||
switch (contentType) {
|
||||
case C.CONTENT_TYPE_DASH:
|
||||
return new DashMediaSource.Factory(factory).createMediaSource(MediaItem.fromUri(contentUri));
|
||||
case C.CONTENT_TYPE_HLS:
|
||||
return new HlsMediaSource.Factory(factory).createMediaSource(MediaItem.fromUri(contentUri));
|
||||
default:
|
||||
case C.CONTENT_TYPE_OTHER:
|
||||
return new ProgressiveMediaSource.Factory(factory).createMediaSource(MediaItem.fromUri(contentUri));
|
||||
}
|
||||
}
|
||||
|
||||
private int inferContentType(String fileName) {
|
||||
fileName = fileName.toLowerCase();
|
||||
if (fileName.contains(".mpd")) {
|
||||
return C.CONTENT_TYPE_DASH;
|
||||
} else if (fileName.contains(".m3u8")) {
|
||||
return C.CONTENT_TYPE_HLS;
|
||||
} else {
|
||||
return C.CONTENT_TYPE_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
private DataSource.Factory getCacheDataSourceFactory() {
|
||||
if (mCache == null) {
|
||||
mCache = newCache();
|
||||
}
|
||||
return new CacheDataSource.Factory()
|
||||
.setCache(mCache)
|
||||
.setUpstreamDataSourceFactory(getDataSourceFactory())
|
||||
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
|
||||
}
|
||||
|
||||
private Cache newCache() {
|
||||
return new SimpleCache(
|
||||
new File(mAppContext.getExternalCacheDir(), "exo-video-cache"),//缓存目录
|
||||
new LeastRecentlyUsedCacheEvictor(512 * 1024 * 1024),//缓存大小,默认512M,使用LRU算法实现
|
||||
new StandaloneDatabaseProvider(mAppContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new DataSource factory.
|
||||
*
|
||||
* @return A new DataSource factory.
|
||||
*/
|
||||
private DataSource.Factory getDataSourceFactory() {
|
||||
return new DefaultDataSource.Factory(mAppContext, getHttpDataSourceFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new HttpDataSource factory.
|
||||
*
|
||||
* @return A new HttpDataSource factory.
|
||||
*/
|
||||
private DataSource.Factory getHttpDataSourceFactory() {
|
||||
if (mHttpDataSourceFactory == null) {
|
||||
mHttpDataSourceFactory = new DefaultHttpDataSource.Factory()
|
||||
.setUserAgent(mUserAgent)
|
||||
.setAllowCrossProtocolRedirects(true);
|
||||
}
|
||||
return mHttpDataSourceFactory;
|
||||
}
|
||||
|
||||
private void setHeaders(Map<String, String> headers) {
|
||||
if (headers != null && headers.size() > 0) {
|
||||
//如果发现用户通过header传递了UA,则强行将HttpDataSourceFactory里面的userAgent字段替换成用户的
|
||||
if (headers.containsKey("User-Agent")) {
|
||||
String value = headers.remove("User-Agent");
|
||||
if (!TextUtils.isEmpty(value)) {
|
||||
try {
|
||||
Field userAgentField = mHttpDataSourceFactory.getClass().getDeclaredField("userAgent");
|
||||
userAgentField.setAccessible(true);
|
||||
userAgentField.set(mHttpDataSourceFactory, value);
|
||||
} catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
mHttpDataSourceFactory.setDefaultRequestProperties(headers);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
this.mCache = cache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
package com.pukka.videoplayertv.exoplayer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaFormat;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.PlaybackException;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.RenderersFactory;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.analytics.DefaultAnalyticsCollector;
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||
import com.google.android.exoplayer2.util.Clock;
|
||||
import com.google.android.exoplayer2.util.EventLogger;
|
||||
import com.google.android.exoplayer2.video.VideoFrameMetadataListener;
|
||||
import com.google.android.exoplayer2.video.VideoSize;
|
||||
import com.pukka.libcore.base.BuildConfig;
|
||||
import com.pukka.libcore.threading.DispatchQueue;
|
||||
import com.pukka.libcore.utils.LogUtils;
|
||||
import com.pukka.videoplayertv.AbstractMediaPlayer;
|
||||
import com.pukka.videoplayertv.IMediaDataSource;
|
||||
import com.pukka.videoplayertv.ITrackInfo;
|
||||
import com.pukka.videoplayertv.MediaInfo;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* PkExoMediaPlayer 播放器内核
|
||||
*/
|
||||
public final class PkExoMediaPlayer extends AbstractMediaPlayer implements Player.Listener {
|
||||
private final static String TAG = "PkExoMediaPlayer";
|
||||
private final static String TAG2 = "PkExoMediaPlayer|播放流程";
|
||||
/**
|
||||
* 视频/音频开始渲染
|
||||
*/
|
||||
public static final int MEDIA_INFO_RENDERING_START = 3;
|
||||
/**
|
||||
* 缓冲开始
|
||||
*/
|
||||
public static final int MEDIA_INFO_BUFFERING_START = 701;
|
||||
/**
|
||||
* 缓冲结束
|
||||
*/
|
||||
public static final int MEDIA_INFO_BUFFERING_END = 702;
|
||||
protected Context mAppContext;
|
||||
protected ExoPlayer mInternalPlayer;
|
||||
protected MediaSource mMediaSource;
|
||||
protected ExoMediaSourceHelper mMediaSourceHelper;
|
||||
private PlaybackParameters mSpeedPlaybackParameters;
|
||||
private boolean mIsPreparing;
|
||||
private RenderersFactory mRenderersFactory;
|
||||
private TrackSelector mTrackSelector;
|
||||
private String playUrl;
|
||||
private boolean isSeek = false;
|
||||
private volatile long duration = 0;
|
||||
private volatile long currentPosition = 0;
|
||||
private volatile int videoWidth = 0;
|
||||
private volatile int videoHeight = 0;
|
||||
private volatile boolean isPlaying = false;
|
||||
|
||||
public PkExoMediaPlayer(Context context) {
|
||||
LogUtils.d(TAG, "播放器版本:" + BuildConfig.VERSION_NAME);
|
||||
LogUtils.d(TAG2, "创建播放器");
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "starting:3");
|
||||
playUrl = "";
|
||||
isFirst_BUFFERING_START = true;
|
||||
mAppContext = context.getApplicationContext();
|
||||
mMediaSourceHelper = ExoMediaSourceHelper.getInstance(context);
|
||||
this.mInternalPlayer = initPlayer();
|
||||
mInternalPlayer.setWakeMode(C.WAKE_MODE_LOCAL); // 保持屏幕常亮
|
||||
setOptions();
|
||||
initMediaListener();
|
||||
});
|
||||
}
|
||||
|
||||
public ExoPlayer initPlayer() {
|
||||
|
||||
// // 创建 DataSource 工厂
|
||||
// DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
|
||||
//
|
||||
// // 创建 ExoPlayer 实例
|
||||
// ExoPlayer mInternalPlayer = new ExoPlayer.Builder(mAppContext)
|
||||
// .setMediaSourceFactory(new DefaultMediaSourceFactory(dataSourceFactory)) // 默认支持多种格式
|
||||
// .build();
|
||||
|
||||
// 增加缓冲区大小
|
||||
DefaultLoadControl mLoadControl = new DefaultLoadControl.Builder()
|
||||
.setBufferDurationsMs(50000, 50000, 1500, 2000)
|
||||
.build();
|
||||
ExoPlayer mInternalPlayer = new ExoPlayer.Builder(
|
||||
mAppContext,
|
||||
mRenderersFactory == null ? mRenderersFactory = new DefaultRenderersFactory(mAppContext).setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER) : mRenderersFactory,
|
||||
new DefaultMediaSourceFactory(mAppContext),
|
||||
mTrackSelector == null ? mTrackSelector = new DefaultTrackSelector(mAppContext) : mTrackSelector,
|
||||
mLoadControl,
|
||||
DefaultBandwidthMeter.getSingletonInstance(mAppContext),
|
||||
new DefaultAnalyticsCollector(Clock.DEFAULT))
|
||||
.build();
|
||||
mInternalPlayer.setVideoFrameMetadataListener(new VideoFrameMetadataListener() {
|
||||
@Override
|
||||
public void onVideoFrameAboutToBeRendered(long presentationTimeUs, long releaseTimeNs, Format format, MediaFormat mediaFormat) {
|
||||
// 每帧渲染前的回调
|
||||
// LogUtils.d("播放帧", "presentationTimeUs: " + presentationTimeUs + " releaseTimeNs:"+releaseTimeNs);
|
||||
if (isVideoFrameMetadata){
|
||||
isVideoFrameMetadata = false;
|
||||
LogUtils.d("播放帧", "实际播放第二帧");
|
||||
}
|
||||
}
|
||||
});
|
||||
mInternalPlayer.addAnalyticsListener(new EventLogger());
|
||||
return mInternalPlayer;
|
||||
}
|
||||
|
||||
public void setOptions() {
|
||||
LogUtils.d(TAG, "setOptions");
|
||||
//准备好了,先不播放,手动调才能播放
|
||||
if (null != mInternalPlayer) {
|
||||
mInternalPlayer.setPlayWhenReady(false);
|
||||
}
|
||||
// 检查 FFmpeg 扩展库是否加载
|
||||
// if (FfmpegLibrary.isAvailable()) {
|
||||
// LogUtils.d("FFmpeg extension is available");
|
||||
// } else {
|
||||
// LogUtils.d("FFmpeg extension is NOT available");
|
||||
// }
|
||||
//放着音乐去视频播放,两个声音
|
||||
LogUtils.d(TAG, "AudioManager init");
|
||||
AudioManager audioManager = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
|
||||
@Override
|
||||
public void onAudioFocusChange(int focusChange) {
|
||||
// 处理音频焦点变化
|
||||
switch (focusChange) {
|
||||
case AudioManager.AUDIOFOCUS_GAIN:
|
||||
LogUtils.d(TAG, "AudioManager AUDIOFOCUS_GAIN");
|
||||
// 重新获得焦点,恢复播放
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS:
|
||||
LogUtils.d(TAG, "AudioManager AUDIOFOCUS_LOSS");
|
||||
// 失去焦点,暂停播放
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
||||
// 暂时失去焦点,暂停播放
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
||||
// 暂时失去焦点,降低音量
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
LogUtils.d(TAG, "AudioManager requestAudioFocus()");
|
||||
int result = audioManager.requestAudioFocus(
|
||||
audioFocusChangeListener,
|
||||
AudioManager.STREAM_MUSIC,
|
||||
AudioManager.AUDIOFOCUS_GAIN
|
||||
);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||
// 成功获得焦点,开始播放
|
||||
LogUtils.d(TAG, "AudioManager get focus success");
|
||||
} else {
|
||||
// 未获得焦点,处理失败情况
|
||||
LogUtils.d(TAG, "AudioManager luss focus");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 倍速播放
|
||||
*
|
||||
* @param speed 倍速 2f
|
||||
*/
|
||||
@Override
|
||||
public void setSpeed(float speed) {
|
||||
LogUtils.d(TAG, "setSpeed: " + speed);
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "ExoPlayer == null");
|
||||
return;
|
||||
}
|
||||
PlaybackParameters playbackParameters = new PlaybackParameters(speed);
|
||||
mSpeedPlaybackParameters = playbackParameters;
|
||||
if (mInternalPlayer != null) {
|
||||
mInternalPlayer.setPlaybackParameters(playbackParameters);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSpeed() {
|
||||
if (mInternalPlayer==null){
|
||||
return 1;
|
||||
}
|
||||
return mInternalPlayer.getPlaybackParameters().speed;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setDisplay(SurfaceHolder holder) {
|
||||
LogUtils.d(TAG, "setDisplay");
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "statue setDisplay:" + holder);
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
if (holder == null)
|
||||
setSurface(null);
|
||||
else
|
||||
setSurface(holder.getSurface());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSurface(Surface surface) {
|
||||
LogUtils.d(TAG, "setSurface");
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
return;
|
||||
}
|
||||
LogUtils.d(TAG, "statue setSurface()");
|
||||
if (mInternalPlayer != null) {
|
||||
mInternalPlayer.setVideoSurface(surface);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(IMediaDataSource mediaDataSource) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(Context var1, Uri var2) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
LogUtils.d(TAG, "setDataSource 2:" + var2.getPath());
|
||||
LogUtils.d(TAG2, "设置播放地址:" + var2.getPath());
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "setDataSource Uri:" + var2);
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "ExoPlayer == null");
|
||||
return;
|
||||
}
|
||||
initSeekTime();
|
||||
if (null != var2) {
|
||||
String path = var2.toString();
|
||||
playUrl = path;
|
||||
mMediaSource = mMediaSourceHelper.getMediaSource(path, null);
|
||||
LogUtils.d("setDataSource () statue starting analysis playUrl:" + playUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setDataSource(Context var1, Uri var2, Map<String, String> var3) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
LogUtils.d(TAG, "setDataSource 3:" + var2.toString());
|
||||
LogUtils.d(TAG2, "设置播放地址:" + var2.toString());
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
initSeekTime();
|
||||
if (null != var2) {
|
||||
String path = var2.toString();
|
||||
playUrl = path;
|
||||
mMediaSource = mMediaSourceHelper.getMediaSource(path, var3);
|
||||
LogUtils.d("setDataSource () statue starting analysis play");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(FileDescriptor var1) throws IOException, IllegalArgumentException, IllegalStateException {
|
||||
//no support
|
||||
LogUtils.d(TAG, "setDataSource 4");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSource(String var1) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
LogUtils.d(TAG, "setDataSource 5:" + var1);
|
||||
LogUtils.d(TAG2, "设置播放地址:" + var1);
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
initSeekTime();
|
||||
playUrl = var1;
|
||||
mMediaSource = mMediaSourceHelper.getMediaSource(var1, null);
|
||||
LogUtils.d("statue setDataSource() starting analysis playUrl:" + playUrl);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataSource() {
|
||||
LogUtils.d(TAG, "getDataSource");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return null;
|
||||
}
|
||||
return playUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareAsync() throws IllegalStateException {
|
||||
LogUtils.d(TAG, "prepareAsync");
|
||||
LogUtils.d(TAG2, "开始解码准备播放");
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
if (mInternalPlayer == null)
|
||||
return;
|
||||
if (mMediaSource == null) return;
|
||||
if (mSpeedPlaybackParameters != null) {
|
||||
mInternalPlayer.setPlaybackParameters(mSpeedPlaybackParameters);
|
||||
}
|
||||
mIsPreparing = true;
|
||||
mInternalPlayer.setMediaSource(mMediaSource);
|
||||
mInternalPlayer.setPlayWhenReady(false);
|
||||
mInternalPlayer.prepare();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws IllegalStateException {
|
||||
LogUtils.d(TAG, "start");
|
||||
LogUtils.d(TAG2, "开始缓冲");
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "statue starting analysis start()");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
mInternalPlayer.setPlayWhenReady(true);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws IllegalStateException {
|
||||
LogUtils.d(TAG, "stop");
|
||||
LogUtils.d(TAG2, "停止播放");
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "statue stop:");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
mInternalPlayer.stop();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void pause() throws IllegalStateException {
|
||||
LogUtils.d(TAG, "pause");
|
||||
LogUtils.d(TAG2, "暂停播放");
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "statue pause:");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
mInternalPlayer.setPlayWhenReady(false);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScreenOnWhilePlaying(boolean var1) {
|
||||
LogUtils.d(TAG, "setScreenOnWhilePlaying");
|
||||
//no support
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoWidth() {
|
||||
LogUtils.d(TAG, "getVideoWidth");
|
||||
//no support
|
||||
//电视看点
|
||||
if (videoWidth == 0) {
|
||||
videoWidth = 1920;
|
||||
}
|
||||
LogUtils.d(TAG, "getVideoWidth videoWidth:" + videoWidth);
|
||||
return videoWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoHeight() {
|
||||
LogUtils.d(TAG, "getVideoHeight");
|
||||
//no support
|
||||
if (videoHeight == 0) {
|
||||
videoHeight = 1080;
|
||||
}
|
||||
LogUtils.d(TAG, "getVideoHeight videoHeight:" + videoHeight);
|
||||
return videoHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
// LogUtils.d(TAG, "isPlaying");
|
||||
if (isMainThread()) {
|
||||
if (mInternalPlayer == null) {
|
||||
return false;
|
||||
} else {
|
||||
int state = mInternalPlayer.getPlaybackState();
|
||||
if (state == Player.STATE_BUFFERING && isSeek) {
|
||||
LogUtils.d(TAG, "isPlaying seekTo: main thread isPlaying():=isSeek" + isSeek);
|
||||
return true;
|
||||
}
|
||||
isPlaying = mInternalPlayer.isPlaying();
|
||||
// LogUtils.d(TAG, "isPlaying main thread isPlaying():==" + isPlaying);
|
||||
return mInternalPlayer.isPlaying();
|
||||
}
|
||||
} else {
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "isPlaying mInternalPlayer == null");
|
||||
isPlaying = false;
|
||||
} else {
|
||||
isPlaying = mInternalPlayer.isPlaying();
|
||||
LogUtils.d(TAG, "isPlaying isPlaying:33 :" + isPlaying);
|
||||
}
|
||||
});
|
||||
try {
|
||||
//20比较合适
|
||||
Thread.sleep(20);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "isPlaying 4:" + isPlaying);
|
||||
return isPlaying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(long time) throws IllegalStateException {
|
||||
LogUtils.d(TAG, "seekTo:" + time);
|
||||
LogUtils.d(TAG2, "seekTo播放:" + time);
|
||||
isSeek = true;
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "seekTo statue starting analysis seekTo:" + time);
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
return;
|
||||
}
|
||||
if (time < 0) {
|
||||
LogUtils.d(TAG, "seekTo value =" + time);
|
||||
return;
|
||||
}
|
||||
mInternalPlayer.seekTo(time);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCurrentPosition() {
|
||||
LogUtils.d(TAG, "getCurrentPosition");
|
||||
if (isMainThread()) {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
currentPosition = 0;
|
||||
return 0;
|
||||
}
|
||||
currentPosition = mInternalPlayer.getCurrentPosition();
|
||||
LogUtils.d(TAG, "getCurrentPosition:" + mInternalPlayer.getCurrentPosition());
|
||||
return currentPosition;
|
||||
}
|
||||
return currentPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
LogUtils.d(TAG, "getDuration");
|
||||
if (isMainThread()) {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
duration = 0;
|
||||
} else {
|
||||
duration = mInternalPlayer.getDuration();
|
||||
LogUtils.d(TAG, "main Thread getDuration:11 :" + duration);
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
public boolean isMainThread() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setVolume(float leftVolume, float rightVolume) {
|
||||
LogUtils.d(TAG, "setVolume:" + leftVolume + " " + rightVolume);
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
LogUtils.d(TAG, "setVolume leftVolume:" + leftVolume + "|| rightVolume:" + rightVolume);
|
||||
if (mInternalPlayer != null)
|
||||
mInternalPlayer.setVolume((leftVolume + rightVolume) / 2);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAudioSessionId() {
|
||||
LogUtils.d(TAG, "getAudioSessionId");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
return 0;
|
||||
}
|
||||
return mInternalPlayer.getAudioSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaInfo getMediaInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayable() {
|
||||
LogUtils.d(TAG, "isPlayable");
|
||||
//no support
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isFirst_BUFFERING_START = true;
|
||||
private boolean isVideoFrameMetadata = false;
|
||||
|
||||
/***
|
||||
* ExoMediaPlayer播放相关回调
|
||||
* */
|
||||
@Override
|
||||
public void onPlaybackStateChanged(int playbackState) {
|
||||
LogUtils.d(TAG, "onPlaybackStateChanged");
|
||||
LogUtils.d(TAG, "onPlaybackStateChanged infoListener.onInfo mIsPreparing:" + mIsPreparing + " isSeek:" + isSeek);
|
||||
LogUtils.d(TAG, "onPlaybackStateChanged infoListener.onInfo:" + playbackState);
|
||||
if (mIsPreparing) {
|
||||
if (playbackState == Player.STATE_READY) {
|
||||
LogUtils.d(TAG, "onPlaybackStateChanged starting analysis exoPlayer statue: onPrepared");
|
||||
notifyOnPrepared();
|
||||
if (!isSeek) {
|
||||
notifyOnInfo(MEDIA_INFO_RENDERING_START, 0);
|
||||
}
|
||||
mIsPreparing = false;
|
||||
LogUtils.d(TAG2, "缓冲完毕开始展示播放画面1:" + isSeek);
|
||||
} else if (playbackState == Player.STATE_BUFFERING) {
|
||||
LogUtils.d(TAG2, "解码完毕准备缓冲");
|
||||
} else if (playbackState == Player.STATE_ENDED) {
|
||||
LogUtils.d(TAG2, "播放器缓冲结束");
|
||||
} else if (playbackState == Player.STATE_IDLE) {
|
||||
}
|
||||
} else {
|
||||
switch (playbackState) {
|
||||
case Player.STATE_BUFFERING:
|
||||
LogUtils.d(TAG2, "正在缓冲");
|
||||
LogUtils.d(TAG, "onPlaybackStateChanged starting analysis infoListener.onInfo:" + MEDIA_INFO_BUFFERING_START + " percentage:" + getBufferPercentage());
|
||||
notifyOnInfo(MEDIA_INFO_BUFFERING_START, 0);
|
||||
|
||||
break;
|
||||
case Player.STATE_READY:
|
||||
//缓冲结束 缓冲结束,可以开始播放
|
||||
if (isFirst_BUFFERING_START) {
|
||||
notifyOnInfo(MEDIA_INFO_BUFFERING_START, 0);
|
||||
}
|
||||
notifyOnInfo(MEDIA_INFO_BUFFERING_START, 0);
|
||||
isVideoFrameMetadata = true;
|
||||
notifyOnInfo(MEDIA_INFO_RENDERING_START, 0);
|
||||
LogUtils.d(TAG2, "缓冲完毕开始展示播放画面2:"+ isSeek);
|
||||
LogUtils.d(TAG, "onPlaybackStateChanged starting analysis infoListener.onInfo: " + MEDIA_INFO_BUFFERING_END);
|
||||
break;
|
||||
case Player.STATE_ENDED:
|
||||
LogUtils.d(TAG2, "播放器缓冲结束");
|
||||
notifyOnCompletion();
|
||||
LogUtils.d(TAG2, "播放器准备完毕");
|
||||
LogUtils.d(TAG, "onPlaybackStateChanged completionListener.onCompletion:");
|
||||
initSeekTime();
|
||||
break;
|
||||
case Player.STATE_IDLE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(PlaybackException error) {
|
||||
LogUtils.d(TAG, "onPlayerError");
|
||||
String errorMessage = "code:" + error.errorCode + " ||message:" + error.getMessage();
|
||||
LogUtils.d(TAG2, "播放器报错 " + errorMessage);
|
||||
initSeekTime();
|
||||
notifyOnError(error.errorCode, errorMessage.contains("Format")?264:9999);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderedFirstFrame() {
|
||||
LogUtils.d(TAG, "onRenderedFirstFrame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
|
||||
LogUtils.d(TAG, "onPositionDiscontinuity");
|
||||
isSeek = false;
|
||||
DispatchQueue.main.async(() -> {
|
||||
//doSeekComplete(mInternalPlayer);
|
||||
// Seek 操作完成
|
||||
// if (null != seekCompleteListener) {
|
||||
// LogUtils.d(TAG, "onPositionDiscontinuity seekCompleteListener() oldPosition:" + oldPosition.positionMs + " newPosition:" + newPosition.positionMs);
|
||||
// //seekCompleteListener.onSeekComplete(mInternalPlayer);
|
||||
// }
|
||||
LogUtils.d(TAG, "onPositionDiscontinuity seekCompleteListener() oldPosition:" + oldPosition.positionMs + " newPosition:" + newPosition.positionMs);
|
||||
notifyOnSeekComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(VideoSize videoSize) {
|
||||
LogUtils.d(TAG, "onVideoSizeChanged");
|
||||
videoWidth = videoSize.width;
|
||||
videoHeight = videoSize.height;
|
||||
LogUtils.d(TAG, "onVideoSizeChanged videoWidth:" + videoWidth + " videoHeight:" + videoHeight);
|
||||
notifyOnVideoSizeChanged(videoSize.width, videoSize.height, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
// int MEDIA_INFO_UNKNOWN = 1;//未知信息
|
||||
// int MEDIA_INFO_STARTED_AS_NEXT = 2;//播放下一条
|
||||
// int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频开始整备中,准备渲染
|
||||
// int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;//视频日志跟踪
|
||||
// int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲中 开始缓冲
|
||||
// int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
|
||||
// int MEDIA_INFO_NETWORK_BANDWIDTH = 703;//网络带宽,网速方面
|
||||
// int MEDIA_INFO_BAD_INTERLEAVING = 800;//
|
||||
// int MEDIA_INFO_NOT_SEEKABLE = 801;//不可设置播放位置,直播方面
|
||||
// int MEDIA_INFO_METADATA_UPDATE = 802;//
|
||||
// int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
|
||||
// int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;//不支持字幕
|
||||
// int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;//字幕超时
|
||||
// int MEDIA_INFO_VIDEO_INTERRUPT= -10000;//数据连接中断,一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
|
||||
// int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频方向改变,视频选择信息
|
||||
// int MEDIA_INFO_AUDIO_RENDERING_START = 10002;//音频开始整备中
|
||||
// int MEDIA_ERROR_SERVER_DIED = 100;//服务挂掉,视频中断,一般是视频源异常或者不支持的视频类型。
|
||||
// int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收
|
||||
// int MEDIA_ERROR_IO = -1004;//IO 错误
|
||||
// int MEDIA_ERROR_MALFORMED = -1007;
|
||||
// int MEDIA_ERROR_UNSUPPORTED = -1010;//数据不支持
|
||||
// int MEDIA_ERROR_TIMED_OUT = -110;//数据超时
|
||||
//
|
||||
// int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频准备渲染
|
||||
// int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收
|
||||
// int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲
|
||||
// int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
|
||||
// int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频选择信息
|
||||
// int MEDIA_ERROR_SERVER_DIED = 100;//视频中断,一般是视频源异常或者不支持的视频类型。
|
||||
// int MEDIA_ERROR_IJK_PLAYER = -10000,//一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
|
||||
// @Override
|
||||
// public void setOnErrorListener(OnErrorListener var1) {
|
||||
// LogUtils.d(TAG, "setOnErrorListener");
|
||||
// //错误信息不返回
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// errorListener = var1;
|
||||
// });
|
||||
// }
|
||||
|
||||
/**
|
||||
* int MEDIA INFO VIDEO RENDERING START = 3;//视频准备渲染
|
||||
* int MEDIA INFO BUFFERING START = 701;//开始缓冲
|
||||
* int MEDIA INFO BUFFERING END = 702;//缓冲结束
|
||||
* int MEDIA INFO VIDEO ROTATION CHANGED = 10001;//视频选择信息
|
||||
* int MEDIA ERROR SERVER DIED = 100;//视频中断,一般是视频源异常或者不支持的视频类型。
|
||||
* int MEDIA ERROR IJK PLAYER = -10800,//一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
|
||||
* int MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK = 200;//数据错误没有有效的回收
|
||||
*/
|
||||
// @Override
|
||||
// public void setOnInfoListener(OnInfoListener var1) {
|
||||
// LogUtils.d(TAG, "setOnInfoListener");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// infoListener = var1;
|
||||
// });
|
||||
// }
|
||||
@Override
|
||||
public void setAudioStreamType(int var1) {
|
||||
LogUtils.d(TAG, "setAudioStreamType");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
//mInternalPlayer.setAudioStreamType(var1);
|
||||
//no support
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepInBackground(boolean var1) {
|
||||
LogUtils.d(TAG, "setKeepInBackground");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
}
|
||||
//mInternalPlayer.set
|
||||
//no support
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoSarNum() {
|
||||
LogUtils.d(TAG, "getVideoSarNum");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return 0;
|
||||
}
|
||||
//no support
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoSarDen() {
|
||||
LogUtils.d(TAG, "getVideoSarDen");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return 0;
|
||||
}
|
||||
//no support
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferPercentage() {
|
||||
LogUtils.d(TAG, "getBufferPercentage:" + mInternalPlayer.getBufferedPercentage());
|
||||
return mInternalPlayer == null ? 0 : mInternalPlayer.getBufferedPercentage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWakeMode(Context var1, int var2) {
|
||||
LogUtils.d(TAG, "setWakeMode");
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
mInternalPlayer.setWakeMode(var2);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLooping(boolean isLooping) {
|
||||
LogUtils.d(TAG, "setLooping:" + isLooping);
|
||||
DispatchQueue.main.async(() -> {
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
if (mInternalPlayer != null)
|
||||
mInternalPlayer.setRepeatMode(isLooping ? Player.REPEAT_MODE_ALL : Player.REPEAT_MODE_OFF);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLooping() {
|
||||
LogUtils.d(TAG, "isLooping");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mInternalPlayer.getRepeatMode() == Player.REPEAT_MODE_ALL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITrackInfo[] getTrackInfo() {
|
||||
return new ITrackInfo[0];
|
||||
}
|
||||
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
LogUtils.d(TAG, "release:");
|
||||
LogUtils.d(TAG2, "释放播放器资源");
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LogUtils.d(TAG, "statue release");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
initSeekTime();
|
||||
if (mInternalPlayer != null) {
|
||||
mInternalPlayer.removeListener(PkExoMediaPlayer.this);
|
||||
mInternalPlayer.release();
|
||||
mInternalPlayer = null;
|
||||
}
|
||||
mIsPreparing = false;
|
||||
mSpeedPlaybackParameters = null;
|
||||
}
|
||||
});
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "statue release");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// addPlayRecord();
|
||||
// initSeekTime();
|
||||
// if (mInternalPlayer != null) {
|
||||
// mInternalPlayer.removeListener(this);
|
||||
// mInternalPlayer.release();
|
||||
// mInternalPlayer = null;
|
||||
// }
|
||||
// mIsPreparing = false;
|
||||
// mSpeedPlaybackParameters = null;
|
||||
// });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
LogUtils.d(TAG, "reset");
|
||||
LogUtils.d(TAG2, "重置播放器资源:");
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "reset start");
|
||||
if (mInternalPlayer == null) {
|
||||
LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
return;
|
||||
}
|
||||
initSeekTime();
|
||||
if (mInternalPlayer != null) {
|
||||
mInternalPlayer.stop();
|
||||
mInternalPlayer.clearMediaItems();
|
||||
mIsPreparing = false;
|
||||
playUrl = "";
|
||||
}
|
||||
LogUtils.d(TAG, "reset end");
|
||||
});
|
||||
}
|
||||
|
||||
private void initMediaListener() {
|
||||
LogUtils.d(TAG, "initMediaListener");
|
||||
mInternalPlayer.addListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复默认值
|
||||
*/
|
||||
private void initSeekTime() {
|
||||
LogUtils.d(TAG, "initSeekTime");
|
||||
DispatchQueue.main.async(() -> {
|
||||
isSeek = false;
|
||||
duration = 0;
|
||||
currentPosition = 0;
|
||||
videoWidth = 0;
|
||||
videoHeight = 0;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, int reason) {
|
||||
LogUtils.d(TAG, "onTimelineChanged");
|
||||
Player.Listener.super.onTimelineChanged(timeline, reason);
|
||||
}
|
||||
|
||||
public void resetListeners(){
|
||||
LogUtils.d(TAG,"resetListeners");
|
||||
super.resetListeners();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,889 @@
|
||||
//package com.pukka.videoplayertv.exoplayer;
|
||||
//
|
||||
//import android.content.Context;
|
||||
//import android.media.AudioManager;
|
||||
//import android.media.MediaFormat;
|
||||
//import android.net.Uri;
|
||||
//import android.os.Handler;
|
||||
//import android.os.Looper;
|
||||
//import android.view.Surface;
|
||||
//import android.view.SurfaceHolder;
|
||||
//
|
||||
//import androidx.media3.common.C;
|
||||
//import androidx.media3.common.Format;
|
||||
//import androidx.media3.common.MediaItem;
|
||||
//import androidx.media3.common.PlaybackException;
|
||||
//import androidx.media3.common.PlaybackParameters;
|
||||
//import androidx.media3.common.Player;
|
||||
//import androidx.media3.common.Timeline;
|
||||
//import androidx.media3.common.VideoSize;
|
||||
//import androidx.media3.common.util.UnstableApi;
|
||||
//import androidx.media3.datasource.DataSource;
|
||||
//import androidx.media3.datasource.DefaultHttpDataSource;
|
||||
//import androidx.media3.exoplayer.ExoPlayer;
|
||||
//import androidx.media3.exoplayer.RenderersFactory;
|
||||
//import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
//import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||
//import androidx.media3.exoplayer.util.EventLogger;
|
||||
//import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
||||
//
|
||||
//import com.pukka.libcore.base.BuildConfig;
|
||||
//import com.pukka.libcore.threading.DispatchQueue;
|
||||
//import com.pukka.libcore.utils.LogUtils;
|
||||
//import com.pukka.videoplayertv.AbstractMediaPlayer;
|
||||
//import com.pukka.videoplayertv.IMediaDataSource;
|
||||
//import com.pukka.videoplayertv.ITrackInfo;
|
||||
//import com.pukka.videoplayertv.MediaInfo;
|
||||
//
|
||||
//import java.io.FileDescriptor;
|
||||
//import java.io.IOException;
|
||||
//import java.util.Map;
|
||||
//
|
||||
//
|
||||
///**
|
||||
// * PkExoMediaPlayer 播放器内核
|
||||
// */
|
||||
//@UnstableApi
|
||||
//public final class PkHighExoMediaPlayer extends AbstractMediaPlayer implements Player.Listener {
|
||||
// private final static String TAG = "PkExoMediaPlayer";
|
||||
// private final static String TAG2 = "PkExoMediaPlayer|播放流程";
|
||||
// /**
|
||||
// * 视频/音频开始渲染
|
||||
// */
|
||||
// public static final int MEDIA_INFO_RENDERING_START = 3;
|
||||
// /**
|
||||
// * 缓冲开始
|
||||
// */
|
||||
// public static final int MEDIA_INFO_BUFFERING_START = 701;
|
||||
// /**
|
||||
// * 缓冲结束
|
||||
// */
|
||||
// public static final int MEDIA_INFO_BUFFERING_END = 702;
|
||||
// protected Context mAppContext;
|
||||
// protected ExoPlayer mInternalPlayer;
|
||||
// protected MediaItem mMediaItem;
|
||||
// private PlaybackParameters mSpeedPlaybackParameters;
|
||||
// private boolean mIsPreparing;
|
||||
// private RenderersFactory mRenderersFactory;
|
||||
// private TrackSelector mTrackSelector;
|
||||
// private String playUrl;
|
||||
// private boolean isSeek = false;
|
||||
// private volatile long duration = 0;
|
||||
// private volatile long currentPosition = 0;
|
||||
// private volatile int videoWidth = 0;
|
||||
// private volatile int videoHeight = 0;
|
||||
// private volatile boolean isPlaying = false;
|
||||
//
|
||||
// @UnstableApi
|
||||
// public PkHighExoMediaPlayer(Context context) {
|
||||
// LogUtils.d(TAG, "播放器版本:" + BuildConfig.VERSION_NAME);
|
||||
// LogUtils.d(TAG2, "创建播放器");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "starting:3");
|
||||
// playUrl = "";
|
||||
// isFirst_BUFFERING_START = true;
|
||||
// mAppContext = context.getApplicationContext();
|
||||
// this.mInternalPlayer = initPlayer();
|
||||
// mInternalPlayer.setWakeMode(C.WAKE_MODE_LOCAL); // 保持屏幕常亮
|
||||
// setOptions();
|
||||
// initMediaListener();
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// public ExoPlayer initPlayer() {
|
||||
//
|
||||
// // 创建 DataSource 工厂
|
||||
// DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
|
||||
//
|
||||
// // 创建 ExoPlayer 实例
|
||||
// ExoPlayer mInternalPlayer = new ExoPlayer.Builder(mAppContext)
|
||||
// .setMediaSourceFactory(new DefaultMediaSourceFactory(dataSourceFactory)) // 默认支持多种格式
|
||||
// .build();
|
||||
//
|
||||
// // 增加缓冲区大小
|
||||
//// DefaultLoadControl mLoadControl = new DefaultLoadControl.Builder()
|
||||
//// .setBufferDurationsMs(50000, 50000, 1500, 2000)
|
||||
//// .build();
|
||||
//// ExoPlayer mInternalPlayer = new ExoPlayer.Builder(
|
||||
//// mAppContext,
|
||||
//// mRenderersFactory == null ? mRenderersFactory = new DefaultRenderersFactory(mAppContext).setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER) : mRenderersFactory,
|
||||
//// new DefaultMediaSourceFactory(mAppContext),
|
||||
//// mTrackSelector == null ? mTrackSelector = new DefaultTrackSelector(mAppContext) : mTrackSelector,
|
||||
//// mLoadControl,
|
||||
//// DefaultBandwidthMeter.getSingletonInstance(mAppContext),
|
||||
//// new DefaultAnalyticsCollector(Clock.DEFAULT))
|
||||
//// .build();
|
||||
// mInternalPlayer.setVideoFrameMetadataListener(new VideoFrameMetadataListener() {
|
||||
// @Override
|
||||
// public void onVideoFrameAboutToBeRendered(long presentationTimeUs, long releaseTimeNs, Format format, MediaFormat mediaFormat) {
|
||||
// // 每帧渲染前的回调
|
||||
//// LogUtils.d("播放帧", "presentationTimeUs: " + presentationTimeUs + " releaseTimeNs:"+releaseTimeNs);
|
||||
// if (isVideoFrameMetadata) {
|
||||
// isVideoFrameMetadata = false;
|
||||
// LogUtils.d("播放帧", "实际播放第二帧");
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// mInternalPlayer.addAnalyticsListener(new EventLogger());
|
||||
// return mInternalPlayer;
|
||||
// }
|
||||
//
|
||||
// public void setOptions() {
|
||||
// LogUtils.d(TAG, "setOptions");
|
||||
// //准备好了,先不播放,手动调才能播放
|
||||
// if (null != mInternalPlayer) {
|
||||
// mInternalPlayer.setPlayWhenReady(false);
|
||||
// }
|
||||
// // 检查 FFmpeg 扩展库是否加载
|
||||
//// if (FfmpegLibrary.isAvailable()) {
|
||||
//// LogUtils.d("FFmpeg extension is available");
|
||||
//// } else {
|
||||
//// LogUtils.d("FFmpeg extension is NOT available");
|
||||
//// }
|
||||
// //放着音乐去视频播放,两个声音
|
||||
// LogUtils.d(TAG, "AudioManager init");
|
||||
// AudioManager audioManager = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
// AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
|
||||
// @Override
|
||||
// public void onAudioFocusChange(int focusChange) {
|
||||
// // 处理音频焦点变化
|
||||
// switch (focusChange) {
|
||||
// case AudioManager.AUDIOFOCUS_GAIN:
|
||||
// LogUtils.d(TAG, "AudioManager AUDIOFOCUS_GAIN");
|
||||
// // 重新获得焦点,恢复播放
|
||||
// break;
|
||||
// case AudioManager.AUDIOFOCUS_LOSS:
|
||||
// LogUtils.d(TAG, "AudioManager AUDIOFOCUS_LOSS");
|
||||
// // 失去焦点,暂停播放
|
||||
// break;
|
||||
// case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
||||
// // 暂时失去焦点,暂停播放
|
||||
// break;
|
||||
// case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
||||
// // 暂时失去焦点,降低音量
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// LogUtils.d(TAG, "AudioManager requestAudioFocus()");
|
||||
// int result = audioManager.requestAudioFocus(
|
||||
// audioFocusChangeListener,
|
||||
// AudioManager.STREAM_MUSIC,
|
||||
// AudioManager.AUDIOFOCUS_GAIN
|
||||
// );
|
||||
// if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||
// // 成功获得焦点,开始播放
|
||||
// LogUtils.d(TAG, "AudioManager get focus success");
|
||||
// } else {
|
||||
// // 未获得焦点,处理失败情况
|
||||
// LogUtils.d(TAG, "AudioManager luss focus");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 倍速播放
|
||||
// *
|
||||
// * @param speed 倍速 2f
|
||||
// */
|
||||
// @Override
|
||||
// public void setSpeed(float speed) {
|
||||
// LogUtils.d(TAG, "setSpeed: " + speed);
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "ExoPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// PlaybackParameters playbackParameters = new PlaybackParameters(speed);
|
||||
// mSpeedPlaybackParameters = playbackParameters;
|
||||
// if (mInternalPlayer != null) {
|
||||
// mInternalPlayer.setPlaybackParameters(playbackParameters);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public float getSpeed() {
|
||||
// if (mInternalPlayer==null){
|
||||
// return 1;
|
||||
// }
|
||||
// return mInternalPlayer.getPlaybackParameters().speed;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public void setDisplay(SurfaceHolder holder) {
|
||||
// LogUtils.d(TAG, "setDisplay");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "statue setDisplay:" + holder);
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// if (holder == null)
|
||||
// setSurface(null);
|
||||
// else
|
||||
// setSurface(holder.getSurface());
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setSurface(Surface surface) {
|
||||
// LogUtils.d(TAG, "setSurface");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// LogUtils.d(TAG, "statue setSurface()");
|
||||
// if (mInternalPlayer != null) {
|
||||
// mInternalPlayer.setVideoSurface(surface);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setDataSource(IMediaDataSource mediaDataSource) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setDataSource(Context var1, Uri var2) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
// LogUtils.d(TAG, "setDataSource 2:" + var2.getPath());
|
||||
// LogUtils.d(TAG2, "设置播放地址:" + var2.getPath());
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "setDataSource Uri:" + var2);
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "ExoPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// initSeekTime();
|
||||
// if (null != var2) {
|
||||
// String path = var2.toString();
|
||||
// playUrl = path;
|
||||
// mMediaItem = MediaItem.fromUri(var2);
|
||||
// LogUtils.d("setDataSource () statue starting analysis playUrl:" + playUrl);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public void setDataSource(Context var1, Uri var2, Map<String, String> var3) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
// LogUtils.d(TAG, "setDataSource 3:" + var2.toString());
|
||||
// LogUtils.d(TAG2, "设置播放地址:" + var2.toString());
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// initSeekTime();
|
||||
// if (null != var2) {
|
||||
// String path = var2.toString();
|
||||
// playUrl = path;
|
||||
// mMediaItem = MediaItem.fromUri(var2);
|
||||
// LogUtils.d("setDataSource () statue starting analysis play");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setDataSource(FileDescriptor var1) throws IOException, IllegalArgumentException, IllegalStateException {
|
||||
// //no support
|
||||
// LogUtils.d(TAG, "setDataSource 4");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setDataSource(String var1) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
// LogUtils.d(TAG, "setDataSource 5:" + var1);
|
||||
// LogUtils.d(TAG2, "设置播放地址:" + var1);
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// initSeekTime();
|
||||
// playUrl = var1;
|
||||
// mMediaItem = MediaItem.fromUri(var1);
|
||||
// LogUtils.d("statue setDataSource() starting analysis playUrl:" + playUrl);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getDataSource() {
|
||||
// LogUtils.d(TAG, "getDataSource");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return null;
|
||||
// }
|
||||
// return playUrl;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void prepareAsync() throws IllegalStateException {
|
||||
// LogUtils.d(TAG, "prepareAsync");
|
||||
// LogUtils.d(TAG2, "开始解码准备播放");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// if (mInternalPlayer == null)
|
||||
// return;
|
||||
// if (mMediaItem == null) return;
|
||||
// if (mSpeedPlaybackParameters != null) {
|
||||
// mInternalPlayer.setPlaybackParameters(mSpeedPlaybackParameters);
|
||||
// }
|
||||
// mIsPreparing = true;
|
||||
// mInternalPlayer.setMediaItem(mMediaItem);
|
||||
// mInternalPlayer.setPlayWhenReady(false);
|
||||
// mInternalPlayer.prepare();
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void start() throws IllegalStateException {
|
||||
// LogUtils.d(TAG, "start");
|
||||
// LogUtils.d(TAG2, "开始缓冲");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "statue starting analysis start()");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// mInternalPlayer.setPlayWhenReady(true);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void stop() throws IllegalStateException {
|
||||
// LogUtils.d(TAG, "stop");
|
||||
// LogUtils.d(TAG2, "停止播放");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "statue stop:");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// mInternalPlayer.stop();
|
||||
// });
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public void pause() throws IllegalStateException {
|
||||
// LogUtils.d(TAG, "pause");
|
||||
// LogUtils.d(TAG2, "暂停播放");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "statue pause:");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// mInternalPlayer.setPlayWhenReady(false);
|
||||
//
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setScreenOnWhilePlaying(boolean var1) {
|
||||
// LogUtils.d(TAG, "setScreenOnWhilePlaying");
|
||||
// //no support
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getVideoWidth() {
|
||||
// LogUtils.d(TAG, "getVideoWidth");
|
||||
// //no support
|
||||
// //电视看点
|
||||
// if (videoWidth == 0) {
|
||||
// videoWidth = 1920;
|
||||
// }
|
||||
// LogUtils.d(TAG, "getVideoWidth videoWidth:" + videoWidth);
|
||||
// return videoWidth;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getVideoHeight() {
|
||||
// LogUtils.d(TAG, "getVideoHeight");
|
||||
// //no support
|
||||
// if (videoHeight == 0) {
|
||||
// videoHeight = 1080;
|
||||
// }
|
||||
// LogUtils.d(TAG, "getVideoHeight videoHeight:" + videoHeight);
|
||||
// return videoHeight;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isPlaying() {
|
||||
// LogUtils.d(TAG, "isPlaying");
|
||||
// if (isMainThread()) {
|
||||
// if (mInternalPlayer == null) {
|
||||
// return false;
|
||||
// } else {
|
||||
// int state = mInternalPlayer.getPlaybackState();
|
||||
// if (state == Player.STATE_BUFFERING && isSeek) {
|
||||
// LogUtils.d(TAG, "isPlaying seekTo: main thread isPlaying():=isSeek" + isSeek);
|
||||
// return true;
|
||||
// }
|
||||
// isPlaying = mInternalPlayer.isPlaying();
|
||||
// LogUtils.d(TAG, "isPlaying main thread isPlaying():==" + isPlaying);
|
||||
// return mInternalPlayer.isPlaying();
|
||||
// }
|
||||
// } else {
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "isPlaying mInternalPlayer == null");
|
||||
// isPlaying = false;
|
||||
// } else {
|
||||
// isPlaying = mInternalPlayer.isPlaying();
|
||||
// LogUtils.d(TAG, "isPlaying isPlaying:33 :" + isPlaying);
|
||||
// }
|
||||
// });
|
||||
// try {
|
||||
// //20比较合适
|
||||
// Thread.sleep(20);
|
||||
// } catch (Exception ex) {
|
||||
// ex.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// LogUtils.d(TAG, "isPlaying 4:" + isPlaying);
|
||||
// return isPlaying;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void seekTo(long time) throws IllegalStateException {
|
||||
// LogUtils.d(TAG, "seekTo:" + time);
|
||||
// LogUtils.d(TAG2, "seekTo播放:" + time);
|
||||
// isSeek = true;
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "seekTo statue starting analysis seekTo:" + time);
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// if (time < 0) {
|
||||
// LogUtils.d(TAG, "seekTo value =" + time);
|
||||
// return;
|
||||
// }
|
||||
// mInternalPlayer.seekTo(time);
|
||||
// });
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public long getCurrentPosition() {
|
||||
// LogUtils.d(TAG, "getCurrentPosition");
|
||||
// if (isMainThread()) {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
// currentPosition = 0;
|
||||
// return 0;
|
||||
// }
|
||||
// currentPosition = mInternalPlayer.getCurrentPosition();
|
||||
// LogUtils.d(TAG, "getCurrentPosition:" + mInternalPlayer.getCurrentPosition());
|
||||
// return currentPosition;
|
||||
// }
|
||||
// return currentPosition;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public long getDuration() {
|
||||
// LogUtils.d(TAG, "getDuration");
|
||||
// if (isMainThread()) {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// duration = 0;
|
||||
// } else {
|
||||
// duration = mInternalPlayer.getDuration();
|
||||
// LogUtils.d(TAG, "main Thread getDuration:11 :" + duration);
|
||||
// return duration;
|
||||
// }
|
||||
// }
|
||||
// return duration;
|
||||
// }
|
||||
//
|
||||
// public boolean isMainThread() {
|
||||
// return Looper.myLooper() == Looper.getMainLooper();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public void setVolume(float leftVolume, float rightVolume) {
|
||||
// LogUtils.d(TAG, "setVolume:" + leftVolume + " " + rightVolume);
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// LogUtils.d(TAG, "setVolume leftVolume:" + leftVolume + "|| rightVolume:" + rightVolume);
|
||||
// if (mInternalPlayer != null)
|
||||
// mInternalPlayer.setVolume((leftVolume + rightVolume) / 2);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getAudioSessionId() {
|
||||
// LogUtils.d(TAG, "getAudioSessionId");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
// return 0;
|
||||
// }
|
||||
// return mInternalPlayer.getAudioSessionId();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public MediaInfo getMediaInfo() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isPlayable() {
|
||||
// LogUtils.d(TAG, "isPlayable");
|
||||
// //no support
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// private boolean isFirst_BUFFERING_START = true;
|
||||
// private boolean isVideoFrameMetadata = false;
|
||||
//
|
||||
// /***
|
||||
// * ExoMediaPlayer播放相关回调
|
||||
// * */
|
||||
// @Override
|
||||
// public void onPlaybackStateChanged(int playbackState) {
|
||||
// LogUtils.d(TAG, "onPlaybackStateChanged");
|
||||
// LogUtils.d(TAG, "onPlaybackStateChanged infoListener.onInfo mIsPreparing:" + mIsPreparing + " isSeek:" + isSeek);
|
||||
// LogUtils.d(TAG, "onPlaybackStateChanged infoListener.onInfo:" + playbackState);
|
||||
// if (mIsPreparing) {
|
||||
// if (playbackState == Player.STATE_READY) {
|
||||
// LogUtils.d(TAG, "onPlaybackStateChanged starting analysis exoPlayer statue: onPrepared");
|
||||
// notifyOnPrepared();
|
||||
// if (!isSeek) {
|
||||
// notifyOnInfo(MEDIA_INFO_RENDERING_START, 0);
|
||||
// }
|
||||
// mIsPreparing = false;
|
||||
// LogUtils.d(TAG2, "缓冲完毕开始展示播放画面1:" + isSeek);
|
||||
// } else if (playbackState == Player.STATE_BUFFERING) {
|
||||
// LogUtils.d(TAG2, "解码完毕准备缓冲");
|
||||
// } else if (playbackState == Player.STATE_ENDED) {
|
||||
// LogUtils.d(TAG2, "播放器缓冲结束");
|
||||
// } else if (playbackState == Player.STATE_IDLE) {
|
||||
// }
|
||||
// } else {
|
||||
// switch (playbackState) {
|
||||
// case Player.STATE_BUFFERING:
|
||||
// LogUtils.d(TAG2, "正在缓冲");
|
||||
// LogUtils.d(TAG, "onPlaybackStateChanged starting analysis infoListener.onInfo:" + MEDIA_INFO_BUFFERING_START + " percentage:" + getBufferPercentage());
|
||||
// notifyOnInfo(MEDIA_INFO_BUFFERING_START, 0);
|
||||
//
|
||||
// break;
|
||||
// case Player.STATE_READY:
|
||||
// //缓冲结束 缓冲结束,可以开始播放
|
||||
// if (isFirst_BUFFERING_START) {
|
||||
// notifyOnInfo(MEDIA_INFO_BUFFERING_START, 0);
|
||||
// }
|
||||
// notifyOnInfo(MEDIA_INFO_BUFFERING_START, 0);
|
||||
// isVideoFrameMetadata = true;
|
||||
// notifyOnInfo(MEDIA_INFO_RENDERING_START, 0);
|
||||
// LogUtils.d(TAG2, "缓冲完毕开始展示播放画面2:"+ isSeek);
|
||||
// LogUtils.d(TAG, "onPlaybackStateChanged starting analysis infoListener.onInfo: " + MEDIA_INFO_BUFFERING_END);
|
||||
// break;
|
||||
// case Player.STATE_ENDED:
|
||||
// LogUtils.d(TAG2, "播放器缓冲结束");
|
||||
// notifyOnCompletion();
|
||||
// LogUtils.d(TAG2, "播放器准备完毕");
|
||||
// LogUtils.d(TAG, "onPlaybackStateChanged completionListener.onCompletion:");
|
||||
// initSeekTime();
|
||||
// break;
|
||||
// case Player.STATE_IDLE:
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onPlayerError(PlaybackException error) {
|
||||
// LogUtils.d(TAG, "onPlayerError");
|
||||
// String errorMessage = "code:" + error.errorCode + " ||message:" + error.getMessage();
|
||||
// LogUtils.d(TAG2, "播放器报错 " + errorMessage);
|
||||
// initSeekTime();
|
||||
// notifyOnError(error.errorCode, errorMessage.contains("Format")?264:9999);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onRenderedFirstFrame() {
|
||||
// LogUtils.d(TAG, "onRenderedFirstFrame");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
|
||||
// LogUtils.d(TAG, "onPositionDiscontinuity");
|
||||
// isSeek = false;
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// //doSeekComplete(mInternalPlayer);
|
||||
// // Seek 操作完成
|
||||
//// if (null != seekCompleteListener) {
|
||||
//// LogUtils.d(TAG, "onPositionDiscontinuity seekCompleteListener() oldPosition:" + oldPosition.positionMs + " newPosition:" + newPosition.positionMs);
|
||||
//// //seekCompleteListener.onSeekComplete(mInternalPlayer);
|
||||
//// }
|
||||
// LogUtils.d(TAG, "onPositionDiscontinuity seekCompleteListener() oldPosition:" + oldPosition.positionMs + " newPosition:" + newPosition.positionMs);
|
||||
// notifyOnSeekComplete();
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onVideoSizeChanged(VideoSize videoSize) {
|
||||
// LogUtils.d(TAG, "onVideoSizeChanged");
|
||||
// videoWidth = videoSize.width;
|
||||
// videoHeight = videoSize.height;
|
||||
// LogUtils.d(TAG, "onVideoSizeChanged videoWidth:" + videoWidth + " videoHeight:" + videoHeight);
|
||||
// notifyOnVideoSizeChanged(videoSize.width, videoSize.height, 0, 0);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // int MEDIA_INFO_UNKNOWN = 1;//未知信息
|
||||
//// int MEDIA_INFO_STARTED_AS_NEXT = 2;//播放下一条
|
||||
//// int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频开始整备中,准备渲染
|
||||
//// int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;//视频日志跟踪
|
||||
//// int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲中 开始缓冲
|
||||
//// int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
|
||||
//// int MEDIA_INFO_NETWORK_BANDWIDTH = 703;//网络带宽,网速方面
|
||||
//// int MEDIA_INFO_BAD_INTERLEAVING = 800;//
|
||||
//// int MEDIA_INFO_NOT_SEEKABLE = 801;//不可设置播放位置,直播方面
|
||||
//// int MEDIA_INFO_METADATA_UPDATE = 802;//
|
||||
//// int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
|
||||
//// int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;//不支持字幕
|
||||
//// int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;//字幕超时
|
||||
//// int MEDIA_INFO_VIDEO_INTERRUPT= -10000;//数据连接中断,一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
|
||||
//// int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频方向改变,视频选择信息
|
||||
//// int MEDIA_INFO_AUDIO_RENDERING_START = 10002;//音频开始整备中
|
||||
//// int MEDIA_ERROR_SERVER_DIED = 100;//服务挂掉,视频中断,一般是视频源异常或者不支持的视频类型。
|
||||
//// int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收
|
||||
//// int MEDIA_ERROR_IO = -1004;//IO 错误
|
||||
//// int MEDIA_ERROR_MALFORMED = -1007;
|
||||
//// int MEDIA_ERROR_UNSUPPORTED = -1010;//数据不支持
|
||||
//// int MEDIA_ERROR_TIMED_OUT = -110;//数据超时
|
||||
////
|
||||
//// int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频准备渲染
|
||||
//// int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收
|
||||
//// int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲
|
||||
//// int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
|
||||
//// int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频选择信息
|
||||
//// int MEDIA_ERROR_SERVER_DIED = 100;//视频中断,一般是视频源异常或者不支持的视频类型。
|
||||
//// int MEDIA_ERROR_IJK_PLAYER = -10000,//一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
|
||||
//// @Override
|
||||
//// public void setOnErrorListener(OnErrorListener var1) {
|
||||
//// LogUtils.d(TAG, "setOnErrorListener");
|
||||
//// //错误信息不返回
|
||||
//// DispatchQueue.main.async(() -> {
|
||||
//// if (mInternalPlayer == null) {
|
||||
//// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
//// return;
|
||||
//// }
|
||||
//// errorListener = var1;
|
||||
//// });
|
||||
//// }
|
||||
//
|
||||
// /**
|
||||
// * int MEDIA INFO VIDEO RENDERING START = 3;//视频准备渲染
|
||||
// * int MEDIA INFO BUFFERING START = 701;//开始缓冲
|
||||
// * int MEDIA INFO BUFFERING END = 702;//缓冲结束
|
||||
// * int MEDIA INFO VIDEO ROTATION CHANGED = 10001;//视频选择信息
|
||||
// * int MEDIA ERROR SERVER DIED = 100;//视频中断,一般是视频源异常或者不支持的视频类型。
|
||||
// * int MEDIA ERROR IJK PLAYER = -10800,//一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
|
||||
// * int MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK = 200;//数据错误没有有效的回收
|
||||
// */
|
||||
//// @Override
|
||||
//// public void setOnInfoListener(OnInfoListener var1) {
|
||||
//// LogUtils.d(TAG, "setOnInfoListener");
|
||||
//// DispatchQueue.main.async(() -> {
|
||||
//// if (mInternalPlayer == null) {
|
||||
//// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
//// return;
|
||||
//// }
|
||||
//// infoListener = var1;
|
||||
//// });
|
||||
//// }
|
||||
// @Override
|
||||
// public void setAudioStreamType(int var1) {
|
||||
// LogUtils.d(TAG, "setAudioStreamType");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// //mInternalPlayer.setAudioStreamType(var1);
|
||||
// //no support
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setKeepInBackground(boolean var1) {
|
||||
// LogUtils.d(TAG, "setKeepInBackground");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// }
|
||||
// //mInternalPlayer.set
|
||||
// //no support
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getVideoSarNum() {
|
||||
// LogUtils.d(TAG, "getVideoSarNum");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return 0;
|
||||
// }
|
||||
// //no support
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getVideoSarDen() {
|
||||
// LogUtils.d(TAG, "getVideoSarDen");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return 0;
|
||||
// }
|
||||
// //no support
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getBufferPercentage() {
|
||||
// LogUtils.d(TAG, "getBufferPercentage:" + mInternalPlayer.getBufferedPercentage());
|
||||
// return mInternalPlayer == null ? 0 : mInternalPlayer.getBufferedPercentage();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setWakeMode(Context var1, int var2) {
|
||||
// LogUtils.d(TAG, "setWakeMode");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// mInternalPlayer.setWakeMode(var2);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void setLooping(boolean isLooping) {
|
||||
// LogUtils.d(TAG, "setLooping:" + isLooping);
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// if (mInternalPlayer != null)
|
||||
// mInternalPlayer.setRepeatMode(isLooping ? Player.REPEAT_MODE_ALL : Player.REPEAT_MODE_OFF);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isLooping() {
|
||||
// LogUtils.d(TAG, "isLooping");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "iMediaPlayer == null");
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// if (mInternalPlayer.getRepeatMode() == Player.REPEAT_MODE_ALL) {
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ITrackInfo[] getTrackInfo() {
|
||||
// return new ITrackInfo[0];
|
||||
// }
|
||||
//
|
||||
// private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
//
|
||||
// @Override
|
||||
// public void release() {
|
||||
// LogUtils.d(TAG, "release:");
|
||||
// LogUtils.d(TAG2, "释放播放器资源");
|
||||
// mainHandler.post(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// LogUtils.d(TAG, "statue release");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// initSeekTime();
|
||||
// if (mInternalPlayer != null) {
|
||||
// mInternalPlayer.removeListener(PkHighExoMediaPlayer.this);
|
||||
// mInternalPlayer.release();
|
||||
// mInternalPlayer = null;
|
||||
// }
|
||||
// mIsPreparing = false;
|
||||
// mSpeedPlaybackParameters = null;
|
||||
// }
|
||||
// });
|
||||
//// DispatchQueue.main.async(() -> {
|
||||
//// LogUtils.d(TAG, "statue release");
|
||||
//// if (mInternalPlayer == null) {
|
||||
//// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
//// return;
|
||||
//// }
|
||||
//// addPlayRecord();
|
||||
//// initSeekTime();
|
||||
//// if (mInternalPlayer != null) {
|
||||
//// mInternalPlayer.removeListener(this);
|
||||
//// mInternalPlayer.release();
|
||||
//// mInternalPlayer = null;
|
||||
//// }
|
||||
//// mIsPreparing = false;
|
||||
//// mSpeedPlaybackParameters = null;
|
||||
//// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void reset() {
|
||||
// LogUtils.d(TAG, "reset");
|
||||
// LogUtils.d(TAG2, "重置播放器资源:");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// LogUtils.d(TAG, "reset start");
|
||||
// if (mInternalPlayer == null) {
|
||||
// LogUtils.d(TAG, "mInternalPlayer == null");
|
||||
// return;
|
||||
// }
|
||||
// initSeekTime();
|
||||
// if (mInternalPlayer != null) {
|
||||
// mInternalPlayer.stop();
|
||||
// mInternalPlayer.clearMediaItems();
|
||||
// mIsPreparing = false;
|
||||
// playUrl = "";
|
||||
// }
|
||||
// LogUtils.d(TAG, "reset end");
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// private void initMediaListener() {
|
||||
// LogUtils.d(TAG, "initMediaListener");
|
||||
// mInternalPlayer.addListener(this);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 恢复默认值
|
||||
// */
|
||||
// private void initSeekTime() {
|
||||
// LogUtils.d(TAG, "initSeekTime");
|
||||
// DispatchQueue.main.async(() -> {
|
||||
// isSeek = false;
|
||||
// duration = 0;
|
||||
// currentPosition = 0;
|
||||
// videoWidth = 0;
|
||||
// videoHeight = 0;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onTimelineChanged(Timeline timeline, int reason) {
|
||||
// LogUtils.d(TAG, "onTimelineChanged");
|
||||
// Player.Listener.super.onTimelineChanged(timeline, reason);
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
BIN
mediaplayertv/src/main/res/drawable-hdpi/ic_media_pause.png
Executable file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
mediaplayertv/src/main/res/drawable-hdpi/player_load_bg.png
Executable file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-hdpi/player_load_logo.png
Executable file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading00.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading000.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading01.png
Executable file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading02.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading03.png
Executable file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading04.png
Executable file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading05.png
Executable file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading06.png
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading07.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading08.png
Executable file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading09.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading10.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading11.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading12.png
Executable file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading13.png
Executable file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading14.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading15.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading16.png
Executable file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading17.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading18.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading19.png
Executable file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading20.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading21.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/loading22.png
Executable file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/progress_loading.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
mediaplayertv/src/main/res/drawable-xxhdpi/switch_keyboard_bg.png
Executable file
|
After Width: | Height: | Size: 740 B |
24
mediaplayertv/src/main/res/drawable/bg_btn_video_selector.xml
Executable file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_focused="true">
|
||||
<shape android:shape="rectangle">
|
||||
<gradient android:type="linear" android:useLevel="true" android:startColor="#ff8e26" android:endColor="#ff7618" android:angle="90" />
|
||||
<corners android:radius="@dimen/qb_px_6" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<gradient android:type="linear" android:useLevel="true" android:startColor="#ff8e26" android:endColor="#ff7618" android:angle="90" />
|
||||
<corners android:radius="@dimen/qb_px_6" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#34FFFFFF"/>
|
||||
<corners android:radius="@dimen/qb_px_6" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
8
mediaplayertv/src/main/res/drawable/bg_control_bottom.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient android:type="linear"
|
||||
android:startColor="#00000000"
|
||||
android:endColor="#ff000000"
|
||||
android:angle="270" />
|
||||
</shape>
|
||||
9
mediaplayertv/src/main/res/drawable/bg_control_top.xml
Executable file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient android:type="linear"
|
||||
android:startColor="#ff000000"
|
||||
android:centerColor="#b0000000"
|
||||
android:endColor="#000f1923"
|
||||
android:angle="270" />
|
||||
</shape>
|
||||
10
mediaplayertv/src/main/res/drawable/bg_pause_video_selector.xml
Executable file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_focused="true">
|
||||
<shape android:shape="oval">
|
||||
<solid
|
||||
android:color="#ff8e26"
|
||||
/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
8
mediaplayertv/src/main/res/drawable/bg_white_round_border_normal.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#FFFFFF" />
|
||||
<solid android:color="#00000000" />
|
||||
</shape>
|
||||
8
mediaplayertv/src/main/res/drawable/bg_white_round_border_pressed.xml
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#FFFFFF" />
|
||||
<solid android:color="#4DFFFFFF" />
|
||||
</shape>
|
||||
6
mediaplayertv/src/main/res/drawable/bg_white_round_border_selector.xml
Executable file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/bg_white_round_border_pressed" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/bg_white_round_border_pressed" android:state_focused="true" />
|
||||
<item android:drawable="@drawable/bg_white_round_border_normal" />
|
||||
</selector>
|
||||
BIN
mediaplayertv/src/main/res/drawable/loading_icon.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
76
mediaplayertv/src/main/res/drawable/loading_player_animation.xml
Executable file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:oneshot="false">
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading000"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading00"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading01"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading02"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading03"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading04"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading05"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading06"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading07"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading08"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading09"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading10"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading11"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading12"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading13"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading14"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading15"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading16"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading17"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading18"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading19"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading20"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading21"/>
|
||||
<item
|
||||
android:duration="43"
|
||||
android:drawable="@drawable/loading22"/>
|
||||
</animation-list>
|
||||
24
mediaplayertv/src/main/res/drawable/ondemand_seekbar_style.xml
Executable file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<solid android:color="@color/white8" />
|
||||
<corners android:radius="@dimen/qb_px_2" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 第二进度图 -->
|
||||
<item android:id="@android:id/secondaryProgress">
|
||||
<shape>
|
||||
<solid android:color="@color/white_transparent_60"/>
|
||||
<corners android:radius="@dimen/qb_px_2" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<solid android:color="#FF7417" />
|
||||
<corners android:radius="@dimen/qb_px_2" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
</layer-list>
|
||||
5
mediaplayertv/src/main/res/drawable/player_seek_thumb.xml
Executable file
@@ -0,0 +1,5 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<size android:width="@dimen/qb_px_20" android:height="@dimen/qb_px_20"/>
|
||||
<solid android:color="#FFFFFFFF"/>
|
||||
<stroke android:width="@dimen/qb_px_2" android:color="#66000000"/>
|
||||
</shape>
|
||||
18
mediaplayertv/src/main/res/drawable/seekbar_focused_background.xml
Executable file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_focused="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#1AFFFFFF" />
|
||||
<corners android:radius="4dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#FF6B00" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
24
mediaplayertv/src/main/res/drawable/svg_media_pause_icon.xml
Executable file
@@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="68"
|
||||
android:viewportHeight="68">
|
||||
<path
|
||||
android:pathData="M34,34m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
|
||||
<path
|
||||
android:pathData="M28,24L30,24A2,2 0,0 1,32 26L32,42A2,2 0,0 1,30 44L28,44A2,2 0,0 1,26 42L26,26A2,2 0,0 1,28 24z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M38,24L40,24A2,2 0,0 1,42 26L42,42A2,2 0,0 1,40 44L38,44A2,2 0,0 1,36 42L36,26A2,2 0,0 1,38 24z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
||||
17
mediaplayertv/src/main/res/drawable/svg_media_play_icon.xml
Executable file
@@ -0,0 +1,17 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="68"
|
||||
android:viewportHeight="68">
|
||||
<path
|
||||
android:pathData="M34,34m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M28,24L28,44L44,34L28,24Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
||||
BIN
mediaplayertv/src/main/res/drawable/videotip1.png
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
mediaplayertv/src/main/res/drawable/videotip2.png
Executable file
|
After Width: | Height: | Size: 5.4 KiB |
19
mediaplayertv/src/main/res/drawable/volume_seek_progress.xml
Executable file
@@ -0,0 +1,19 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background">
|
||||
<shape android:shape="rectangle">
|
||||
<size android:height="@dimen/qb_px_12"/>
|
||||
<corners android:radius="@dimen/qb_px_12"/>
|
||||
<solid android:color="#50000000"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape android:shape="rectangle">
|
||||
<size android:height="@dimen/qb_px_12"/>
|
||||
<corners android:radius="@dimen/qb_px_12"/>
|
||||
<gradient android:startColor="@android:color/holo_orange_light"
|
||||
android:endColor="@android:color/holo_orange_dark"/>
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
</layer-list>
|
||||
321
mediaplayertv/src/main/res/layout/media_controller_tv.xml
Executable file
@@ -0,0 +1,321 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:background="#00000000"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/qb_px_240"
|
||||
android:background="@drawable/bg_control_top"
|
||||
android:focusable="false">
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginTop="@dimen/qb_px_28"
|
||||
android:layout_marginRight="@dimen/qb_px_40"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/play_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/qb_px_40"
|
||||
android:ellipsize="end"
|
||||
android:maxEms="20"
|
||||
android:singleLine="true"
|
||||
android:text="恐龙当家"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/mg_sp_21"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible" />
|
||||
|
||||
<TextClock
|
||||
android:id="@+id/id_tc_clock"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:focusable="false"
|
||||
android:format12Hour="HH:mm"
|
||||
android:format24Hour="HH:mm"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="@dimen/mg_sp_22"
|
||||
android:textStyle="normal"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="right"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/id_tip_img1"
|
||||
android:layout_width="@dimen/qb_px_69"
|
||||
android:layout_height="@dimen/qb_px_27"
|
||||
android:focusable="false"
|
||||
android:src="@drawable/videotip2"
|
||||
android:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/id_tip_img2"
|
||||
android:layout_width="@dimen/qb_px_233"
|
||||
android:layout_height="@dimen/qb_px_27"
|
||||
android:layout_marginLeft="@dimen/qb_px_12"
|
||||
android:focusable="false"
|
||||
android:src="@drawable/videotip1"
|
||||
android:visibility="visible" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/play_contro_rl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@drawable/bg_control_bottom"
|
||||
android:focusable="false"
|
||||
android:paddingLeft="@dimen/qb_px_40"
|
||||
android:paddingTop="@dimen/qb_px_24"
|
||||
android:paddingRight="@dimen/qb_px_40"
|
||||
android:paddingBottom="@dimen/qb_px_20">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_settings_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/jujitv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_btn_video_selector"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center"
|
||||
android:hint="三个字"
|
||||
android:nextFocusDown="@id/mediacontroller_progress"
|
||||
android:nextFocusLeft="@id/jujitv"
|
||||
android:nextFocusRight="@id/id_tv_resolution"
|
||||
android:nextFocusUp="@id/jujitv"
|
||||
android:paddingHorizontal="@dimen/qb_px_15"
|
||||
android:paddingVertical="@dimen/qb_px_7"
|
||||
android:text="选集"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/mg_sp_21" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/id_tv_resolution"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qb_px_8"
|
||||
android:background="@drawable/bg_btn_video_selector"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center"
|
||||
android:hint="三个字"
|
||||
android:nextFocusDown="@id/mediacontroller_progress"
|
||||
android:nextFocusLeft="@id/jujitv"
|
||||
android:nextFocusRight="@id/id_tv_speed"
|
||||
android:nextFocusUp="@id/id_tv_resolution"
|
||||
android:paddingHorizontal="@dimen/qb_px_15"
|
||||
android:paddingVertical="@dimen/qb_px_7"
|
||||
android:text="清晰度"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/mg_sp_21" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/id_tv_speed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qb_px_8"
|
||||
android:background="@drawable/bg_btn_video_selector"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center"
|
||||
android:hint="三个字"
|
||||
android:nextFocusDown="@id/mediacontroller_progress"
|
||||
android:nextFocusLeft="@id/id_tv_resolution"
|
||||
android:nextFocusRight="@id/pause"
|
||||
android:nextFocusUp="@id/id_tv_speed"
|
||||
android:paddingHorizontal="@dimen/qb_px_15"
|
||||
android:paddingVertical="@dimen/qb_px_7"
|
||||
android:text="倍速"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/mg_sp_21" />
|
||||
</LinearLayout>
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pause"
|
||||
android:layout_width="@dimen/qb_px_52"
|
||||
android:layout_height="@dimen/qb_px_52"
|
||||
android:background="@drawable/bg_pause_video_selector"
|
||||
android:focusable="true"
|
||||
android:nextFocusDown="@id/mediacontroller_progress"
|
||||
android:nextFocusLeft="@id/id_tv_speed"
|
||||
android:nextFocusRight="@id/id_back_btn"
|
||||
android:nextFocusUp="@id/pause"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/svg_media_play_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/id_back_btn"
|
||||
android:layout_width="@dimen/qb_px_50"
|
||||
android:layout_height="@dimen/qb_px_50"
|
||||
android:layout_marginStart="@dimen/qb_px_12"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/bg_pause_video_selector"
|
||||
android:nextFocusDown="@id/mediacontroller_progress"
|
||||
android:nextFocusLeft="@id/pause"
|
||||
android:nextFocusRight="@id/id_back_btn"
|
||||
android:nextFocusUp="@id/id_back_btn"
|
||||
android:padding="@dimen/qb_px_10"
|
||||
android:visibility="visible" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_progress_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/qb_px_20"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time_current"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/mg_sp_20" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/mediacontroller_progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qb_px_20"
|
||||
android:layout_marginEnd="@dimen/qb_px_20"
|
||||
android:layout_weight="1"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:max="1000"
|
||||
android:maxHeight="4dp"
|
||||
android:minHeight="4dp"
|
||||
android:nextFocusDown="@id/mediacontroller_progress"
|
||||
android:nextFocusLeft="@id/mediacontroller_progress"
|
||||
android:nextFocusRight="@id/mediacontroller_progress"
|
||||
android:nextFocusUp="@id/pause"
|
||||
android:paddingLeft="@dimen/qb_px_8"
|
||||
android:paddingTop="@dimen/qb_px_6"
|
||||
android:paddingRight="@dimen/qb_px_8"
|
||||
android:paddingBottom="@dimen/qb_px_6"
|
||||
android:progressDrawable="@drawable/ondemand_seekbar_style"
|
||||
android:thumb="@mipmap/dot" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/mg_sp_20" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/centerPause"
|
||||
android:visibility="gone"
|
||||
android:layout_width="@dimen/qb_px_160"
|
||||
android:layout_height="@dimen/qb_px_160"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginBottom="@dimen/qb_px_26"
|
||||
android:background="@drawable/ic_media_pause"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/touch_volume_panel"
|
||||
android:layout_width="@dimen/qb_px_72"
|
||||
android:layout_height="@dimen/qb_px_320"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="@dimen/qb_px_160"
|
||||
android:gravity="center_horizontal"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_touch_volume_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:text="0"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/mg_sp_20"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="@dimen/qb_px_40"
|
||||
android:layout_height="@dimen/qb_px_240"
|
||||
android:layout_below="@id/tv_touch_volume_value"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/qb_px_20">
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_touch_volume"
|
||||
android:layout_width="@dimen/qb_px_240"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:max="100"
|
||||
android:maxHeight="@dimen/qb_px_12"
|
||||
android:minHeight="@dimen/qb_px_12"
|
||||
android:rotation="270"
|
||||
android:progressDrawable="@drawable/volume_seek_progress"
|
||||
android:thumb="@mipmap/dot" />
|
||||
</FrameLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
20
mediaplayertv/src/main/res/layout/media_vod_menu_controller.xml
Executable file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:gravity="bottom"
|
||||
android:layout_height="@dimen/qb_px_420">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/id_rv_type_page"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/id_rv_type"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/id_rv_function"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
BIN
mediaplayertv/src/main/res/mipmap-hdpi/dot.png
Executable file
|
After Width: | Height: | Size: 909 B |
BIN
mediaplayertv/src/main/res/mipmap-hdpi/ic_media_play.png
Executable file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
mediaplayertv/src/main/res/mipmap-hdpi/icon_backward.png
Executable file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
mediaplayertv/src/main/res/mipmap-hdpi/icon_forward.png
Executable file
|
After Width: | Height: | Size: 4.7 KiB |
1979
mediaplayertv/src/main/res/values-sw1080dp-land/dimens.xml
Executable file
1980
mediaplayertv/src/main/res/values-sw360dp-land/dimens.xml
Executable file
1980
mediaplayertv/src/main/res/values-sw480dp-land/dimens.xml
Executable file
1980
mediaplayertv/src/main/res/values-sw540dp-land/dimens.xml
Executable file
1980
mediaplayertv/src/main/res/values-sw617dp-land/dimens.xml
Executable file
1980
mediaplayertv/src/main/res/values-sw720dp-land/dimens.xml
Executable file
21
mediaplayertv/src/main/res/values/colors.xml
Executable file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!--TEXT-->
|
||||
<!--ICON-->
|
||||
|
||||
<!--透明度20-->
|
||||
<!--透明度30-->
|
||||
<!--透明度40-->
|
||||
<!--透明度50-->
|
||||
<!--透明度60-->
|
||||
<!--透明度70-->
|
||||
<!--透明度80-->
|
||||
<!--透明度20-->
|
||||
<!--透明色 -->
|
||||
<!--透明色 -->
|
||||
<color name="white_transparent_60">#99FFFFFF</color><!--透明色 -->
|
||||
<color name="white">#FFFFFF</color>
|
||||
<!--透明度0.2白色-->
|
||||
<color name="white8">#CCFFFFFF</color><!--透明度0.8白色-->
|
||||
|
||||
</resources>
|
||||
43
mediaplayertv/src/main/res/values/dimens.xml
Executable file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!--PAGE TITLE-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--BODY-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Bold-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Segoe WP Regular-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Bold-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
<!--Light-->
|
||||
|
||||
<!--Light-->
|
||||
</resources>
|
||||
1980
mediaplayertv/src/main/res/values/dimens_sw.xml
Executable file
2
mediaplayertv/src/main/res/values/h_dimens.xml
Executable file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
1
mediaplayertv/src/main/res/values/strings.xml
Executable file
@@ -0,0 +1 @@
|
||||
<resources></resources>
|
||||
2
mediaplayertv/src/main/res/values/w_dimens.xml
Executable file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
17
mediaplayertv/src/test/java/com/pukka/mediaplayertv/ExampleUnitTest.java
Executable file
@@ -0,0 +1,17 @@
|
||||
package com.pukka.mediaplayertv;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||