初始化橘视厅酒店版Android项目

This commit is contained in:
freeboss
2026-07-26 12:43:59 +08:00
commit e1a088ef8e
442 changed files with 68672 additions and 0 deletions

View 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();
}
}

View 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();
}
}

View 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();
}
}
}

View 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";
}
}
}

View 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;
}
}

View 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();
}

View 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;
}

View 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);
}

View 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();
}

View 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);
}
}

View File

@@ -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);
}

View 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);
}

View 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();
}

View 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;
}
}

View 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;
}

View 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();
}

View 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);
}
});
}
}

View 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());
}
}
}

File diff suppressed because it is too large Load Diff

View 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();
}
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
// }
//
//
//}