feat: dev-5.1 分支基线(v1.3.35 发版后代码 + 5.1 声道改造文档)
- 以 main(ccc65af) 为基线提交当前工作区代码
- 新增 5.1 声道改造文档两份
- .gitignore 补充 .vscode/、.screenshots/
This commit is contained in:
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AbstractMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AbstractMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidMediaFormat.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidMediaFormat.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidTrackInfo.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/AndroidTrackInfo.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ConfigPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ConfigPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaController.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaController.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaDataSource.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaDataSource.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaFormat.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaFormat.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IRenderView.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/IRenderView.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ISurfaceTextureHolder.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ISurfaceTextureHolder.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ISurfaceTextureHost.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ISurfaceTextureHost.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ITrackInfo.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/ITrackInfo.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/MeasureHelper.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/MeasureHelper.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/MediaInfo.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/MediaInfo.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/MediaPlayerControl.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/MediaPlayerControl.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/SoftMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/SoftMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/SurfaceRenderView.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/SurfaceRenderView.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/TvBaseController.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/TvBaseController.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/TvMediaController.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/TvMediaController.java
Executable file → Normal file
12
mediaplayertv/src/main/java/com/pukka/videoplayertv/VideoViewTv.java
Executable file → Normal file
12
mediaplayertv/src/main/java/com/pukka/videoplayertv/VideoViewTv.java
Executable file → Normal file
@@ -328,11 +328,6 @@ public class VideoViewTv extends FrameLayout implements MediaPlayerControl {
|
||||
}
|
||||
|
||||
private void initVideoView() {
|
||||
// Android 4.2 兼容:ExoPlayer 2.19 官方支持 API 19+,低版本强制切系统 MediaPlayer 内核,
|
||||
// 避免 PkExoMediaPlayer/ExoPlayer 类在 Dalvik 上触发 VerifyError
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
terminalType = 0;
|
||||
}
|
||||
if (!checkAudioCapability()){
|
||||
terminalType = 0;
|
||||
}
|
||||
@@ -567,9 +562,7 @@ public class VideoViewTv extends FrameLayout implements MediaPlayerControl {
|
||||
reset();
|
||||
mMediaPlayer.release();
|
||||
if(mMediaPlayer != null){
|
||||
// terminalType 短路:非 Exo 内核时跳过 instanceof,避免 Dalvik 解析
|
||||
// PkExoMediaPlayer 引发的 ExoPlayer 类加载 VerifyError(Android 4.2)
|
||||
if(terminalType == 2 && mMediaPlayer instanceof PkExoMediaPlayer){
|
||||
if(mMediaPlayer instanceof PkExoMediaPlayer){
|
||||
PkExoMediaPlayer pkExoMediaPlayer = (PkExoMediaPlayer)mMediaPlayer;
|
||||
pkExoMediaPlayer.resetListeners();
|
||||
}
|
||||
@@ -1498,8 +1491,7 @@ public class VideoViewTv extends FrameLayout implements MediaPlayerControl {
|
||||
onPauseListener = null;
|
||||
onResumeListener = null;
|
||||
if(mMediaPlayer != null){
|
||||
// terminalType 短路:同上,避免非 Exo 内核时 Dalvik 触发 PkExoMediaPlayer 类解析
|
||||
if(terminalType == 2 && mMediaPlayer instanceof PkExoMediaPlayer){
|
||||
if(mMediaPlayer instanceof PkExoMediaPlayer){
|
||||
PkExoMediaPlayer pkExoMediaPlayer = (PkExoMediaPlayer)mMediaPlayer;
|
||||
pkExoMediaPlayer.resetListeners();
|
||||
}
|
||||
|
||||
8
mediaplayertv/src/main/java/com/pukka/videoplayertv/exoplayer/ExoMediaSourceHelper.java
Executable file → Normal file
8
mediaplayertv/src/main/java/com/pukka/videoplayertv/exoplayer/ExoMediaSourceHelper.java
Executable file → Normal file
@@ -64,6 +64,14 @@ public final class ExoMediaSourceHelper {
|
||||
}
|
||||
|
||||
public MediaSource getMediaSource(String uri, Map<String, String> headers, boolean isCache) {
|
||||
// 防御性边界检查:上游偶发传入空 URL(典型如 Jetsenx SDK 返回的双清晰度 URL 都为空),
|
||||
// 此时 Uri.parse("") 会得到 Uri.EMPTY,DefaultDataSource 因缺少 http/https scheme
|
||||
// 会退回到 FileDataSource,再用空路径去打开本地文件,触发 FileNotFoundException: ENOENT
|
||||
// 导致播放崩溃。返回 null 让调用方走 if (mMediaSource == null) return; 兜底。
|
||||
if (TextUtils.isEmpty(uri)) {
|
||||
android.util.Log.w("ExoMediaSourceHelper", "getMediaSource called with empty uri, return null to avoid FileDataSource ENOENT crash");
|
||||
return null;
|
||||
}
|
||||
Uri contentUri = Uri.parse(uri);
|
||||
// if ("rtmp".equals(contentUri.getScheme())) {
|
||||
// return new ProgressiveMediaSource.Factory(new RtmpDataSource.Factory())
|
||||
|
||||
113
mediaplayertv/src/main/java/com/pukka/videoplayertv/exoplayer/PkExoMediaPlayer.java
Executable file → Normal file
113
mediaplayertv/src/main/java/com/pukka/videoplayertv/exoplayer/PkExoMediaPlayer.java
Executable file → Normal file
@@ -6,6 +6,7 @@ import android.media.MediaFormat;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
@@ -20,6 +21,9 @@ 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.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer2.audio.AudioSink;
|
||||
import com.google.android.exoplayer2.audio.DefaultAudioSink;
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
@@ -32,6 +36,7 @@ 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.libcore.utils.SharedPreferenceUtils;
|
||||
import com.pukka.videoplayertv.AbstractMediaPlayer;
|
||||
import com.pukka.videoplayertv.IMediaDataSource;
|
||||
import com.pukka.videoplayertv.ITrackInfo;
|
||||
@@ -48,6 +53,13 @@ import java.util.Map;
|
||||
public final class PkExoMediaPlayer extends AbstractMediaPlayer implements Player.Listener {
|
||||
private final static String TAG = "PkExoMediaPlayer";
|
||||
private final static String TAG2 = "PkExoMediaPlayer|播放流程";
|
||||
/**
|
||||
* v1.3.36: 进程级禁用 AC3 caps 注入。
|
||||
* 直通初始化失败自动降级后置位,本进程内不再注入(设备级失败,重试无意义)。
|
||||
*/
|
||||
private static volatile boolean sAc3InjectionDisabled = false;
|
||||
/** 当前播放器实例的 AudioSink 是否注入了 AC3 caps(onPlayerError 降级判定用) */
|
||||
private volatile boolean mAc3Injected = false;
|
||||
/**
|
||||
* 视频/音频开始渲染
|
||||
*/
|
||||
@@ -106,9 +118,54 @@ public final class PkExoMediaPlayer extends AbstractMediaPlayer implements Playe
|
||||
DefaultLoadControl mLoadControl = new DefaultLoadControl.Builder()
|
||||
.setBufferDurationsMs(50000, 50000, 1500, 2000)
|
||||
.build();
|
||||
if (mRenderersFactory == null) {
|
||||
mRenderersFactory = new DefaultRenderersFactory(mAppContext) {
|
||||
@Override
|
||||
protected AudioSink buildAudioSink(Context context, boolean enableFloatOutput,
|
||||
boolean enableAudioTrackPlaybackParams, boolean enableOffload) {
|
||||
AudioCapabilities caps = AudioCapabilities.getCapabilities(context);
|
||||
boolean ac3Supported = caps.supportsEncoding(C.ENCODING_AC3);
|
||||
// 5.1 开关开启且系统未禁止环绕声时,强制注入 AC3 直通能力。
|
||||
// 修复:HAL 从 PCM 切回 AUTO 有延迟,系统能力查询可能返回过时结果,
|
||||
// 导致 ExoPlayer 不尝试 AC3 直通。
|
||||
// v1.3.36 关键修复:DefaultAudioSink.Builder(context) 传入 context 时,
|
||||
// getFormatSupport() 首次调用会懒创建 AudioCapabilitiesReceiver 并用系统
|
||||
// 实时 caps 覆盖注入值(反编译 2.19.1 字节码确认)→ 注入从未生效。
|
||||
// 必须用无参 Builder()(context=null)才能让注入的 caps 持续有效。
|
||||
// v1.3.36 降级保护:直通初始化失败后 sAc3InjectionDisabled 置位,本进程不再注入。
|
||||
if (!ac3Supported && !sAc3InjectionDisabled) {
|
||||
boolean surroundEnabled = SharedPreferenceUtils.getBoolean("Surround51Enabled", true);
|
||||
if (surroundEnabled) {
|
||||
int encodedSurround = Settings.Global.getInt(
|
||||
context.getContentResolver(), "encoded_surround_output", 0);
|
||||
// encoded_surround_output: 0=AUTO, 1=NEVER, 2=ALWAYS(部分固件映射异常), 3=MANUAL
|
||||
if (encodedSurround != 1) {
|
||||
LogUtils.d(TAG, "buildAudioSink: 系统未报告AC3,强制注入。"
|
||||
+ " encodedSurround=" + encodedSurround);
|
||||
caps = new AudioCapabilities(
|
||||
new int[]{C.ENCODING_PCM_16BIT, C.ENCODING_AC3}, 8);
|
||||
mAc3Injected = true;
|
||||
}
|
||||
}
|
||||
} else if (ac3Supported) {
|
||||
LogUtils.d(TAG, "buildAudioSink: 系统已报告AC3直通能力");
|
||||
} else {
|
||||
LogUtils.d(TAG, "buildAudioSink: 注入已禁用(本进程曾直通失败),走系统 caps");
|
||||
}
|
||||
return new DefaultAudioSink.Builder()
|
||||
.setAudioCapabilities(caps)
|
||||
.setEnableFloatOutput(enableFloatOutput)
|
||||
.setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
|
||||
.setOffloadMode(enableOffload
|
||||
? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_NOT_REQUIRED
|
||||
: DefaultAudioSink.OFFLOAD_MODE_DISABLED)
|
||||
.build();
|
||||
}
|
||||
}.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON);
|
||||
}
|
||||
ExoPlayer mInternalPlayer = new ExoPlayer.Builder(
|
||||
mAppContext,
|
||||
mRenderersFactory == null ? mRenderersFactory = new DefaultRenderersFactory(mAppContext).setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER) : mRenderersFactory,
|
||||
mRenderersFactory,
|
||||
new DefaultMediaSourceFactory(mAppContext),
|
||||
mTrackSelector == null ? mTrackSelector = new DefaultTrackSelector(mAppContext) : mTrackSelector,
|
||||
mLoadControl,
|
||||
@@ -608,10 +665,64 @@ public final class PkExoMediaPlayer extends AbstractMediaPlayer implements Playe
|
||||
LogUtils.d(TAG, "onPlayerError");
|
||||
String errorMessage = "code:" + error.errorCode + " ||message:" + error.getMessage();
|
||||
LogUtils.d(TAG2, "播放器报错 " + errorMessage);
|
||||
// v1.3.36: AC3 注入会话中的音频初始化类错误 → 疑似直通初始化失败
|
||||
// (平台有 raw decoder 但 policy 无 DIRECT 压缩输出时,AudioTrack(ENCODING_AC3) init 失败)。
|
||||
// 此时注入的 caps 不可达,摘除注入并自动重建播放器软解降级重试(一次为限),不上抛错误。
|
||||
if (mAc3Injected && !sAc3InjectionDisabled) {
|
||||
int code = error.errorCode;
|
||||
if (code == PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED
|
||||
|| code == PlaybackException.ERROR_CODE_DECODER_INIT_FAILED
|
||||
|| code == PlaybackException.ERROR_CODE_UNSPECIFIED) {
|
||||
LogUtils.d(TAG, "onPlayerError: 注入会话音频初始化失败(code=" + code
|
||||
+ "),自动降级为软解重试");
|
||||
sAc3InjectionDisabled = true;
|
||||
degradeAndRetry(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
initSeekTime();
|
||||
notifyOnError(error.errorCode, errorMessage.contains("Format")?264:9999);
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.3.36: 关闭 AC3 注入重建播放器,软解降级重试。
|
||||
* 当前片源内闭环恢复(保留进度),仅在重试再次失败时才上抛原错误。
|
||||
*/
|
||||
private void degradeAndRetry(PlaybackException error) {
|
||||
final long resumePos = mInternalPlayer != null
|
||||
? Math.max(0, mInternalPlayer.getCurrentPosition()) : 0;
|
||||
DispatchQueue.main.async(() -> {
|
||||
LogUtils.d(TAG, "degradeAndRetry: 关闭AC3注入重建播放器, resumePos=" + resumePos);
|
||||
try {
|
||||
if (mInternalPlayer != null) {
|
||||
mInternalPlayer.removeListener(PkExoMediaPlayer.this);
|
||||
mInternalPlayer.release();
|
||||
mInternalPlayer = null;
|
||||
}
|
||||
mRenderersFactory = null; // 置空以重建 AudioSink(sAc3InjectionDisabled=true,不再注入)
|
||||
mAc3Injected = false;
|
||||
mInternalPlayer = initPlayer();
|
||||
mInternalPlayer.setWakeMode(C.WAKE_MODE_LOCAL);
|
||||
setOptions();
|
||||
initMediaListener();
|
||||
if (mSpeedPlaybackParameters != null) {
|
||||
mInternalPlayer.setPlaybackParameters(mSpeedPlaybackParameters);
|
||||
}
|
||||
mIsPreparing = true;
|
||||
mInternalPlayer.setMediaSource(mMediaSource);
|
||||
mInternalPlayer.setPlayWhenReady(true);
|
||||
mInternalPlayer.prepare();
|
||||
if (resumePos > 3000) {
|
||||
mInternalPlayer.seekTo(resumePos);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LogUtils.d(TAG, "degradeAndRetry 失败,上抛原错误:" + t);
|
||||
initSeekTime();
|
||||
notifyOnError(error.errorCode, 9999);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderedFirstFrame() {
|
||||
LogUtils.d(TAG, "onRenderedFirstFrame");
|
||||
|
||||
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/exoplayer/PkHighExoMediaPlayer.java
Executable file → Normal file
0
mediaplayertv/src/main/java/com/pukka/videoplayertv/exoplayer/PkHighExoMediaPlayer.java
Executable file → Normal file
Reference in New Issue
Block a user