diff --git a/app/build.gradle b/app/build.gradle index be5011389..0269fcda0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { minSdk 21 //noinspection ExpiredTargetSdkVersion targetSdk 28 - versionCode 244 - versionName "0726" + versionCode 245 + versionName "2.4.5" javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] @@ -112,7 +112,6 @@ dependencies { implementation 'androidx.room:room-runtime:2.6.1' implementation 'androidx.media:media:1.7.0' implementation 'cat.ereza:customactivityoncrash:2.4.0' - implementation('com.github.anilbeesetti.nextlib:nextlib-media3ext:0.7.1') { exclude group: 'androidx.media3', module: 'media3-exoplayer' } implementation 'com.github.bassaer:materialdesigncolors:1.0.0' implementation 'com.github.bumptech.glide:glide:4.16.0' implementation 'com.github.bumptech.glide:annotations:4.16.0' @@ -142,6 +141,7 @@ dependencies { implementation(ext: 'aar', name: 'dlna-core-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'dlna-dmc-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'dlna-dmr-release', group: 'fongmi', version: 'release') + implementation(ext: 'aar', name: 'media3ext-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-common-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-container-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-database-release', group: 'fongmi', version: 'release') @@ -150,7 +150,6 @@ dependencies { implementation(ext: 'aar', name: 'lib-datasource-rtmp-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-decoder-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-decoder-av1-release', group: 'fongmi', version: 'release') - implementation(ext: 'aar', name: 'lib-decoder-ffmpeg-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-exoplayer-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-exoplayer-dash-release', group: 'fongmi', version: 'release') implementation(ext: 'aar', name: 'lib-exoplayer-hls-release', group: 'fongmi', version: 'release') diff --git a/app/libs/media3ext-release.aar b/app/libs/media3ext-release.aar new file mode 100644 index 000000000..b67325e5e Binary files /dev/null and b/app/libs/media3ext-release.aar differ diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java index 2efb40e39..62c32d4f0 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java @@ -144,7 +144,7 @@ public class CastActivity extends BaseActivity implements CustomKeyDownCast.List } private void setVideoView() { - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); mPlayers.setPlayer(Setting.getPlayer()); findViewById(R.id.timeBar).setNextFocusUpId(R.id.reset); mBinding.control.reset.setText(ResUtil.getStringArray(R.array.select_reset)[0]); @@ -220,7 +220,7 @@ public class CastActivity extends BaseActivity implements CustomKeyDownCast.List private void onDecode(boolean save) { mPlayers.toggleDecode(save); - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); setDecodeView(); onReset(); } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index c214dbd26..2a837fcef 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -210,7 +210,7 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, GroupP } private void setVideoView() { - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); setScale(Setting.getLiveScale()); mBinding.control.invert.setActivated(Setting.isInvert()); mBinding.control.across.setActivated(Setting.isAcross()); @@ -430,7 +430,7 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, GroupP private void onDecode(boolean save) { mPlayers.toggleDecode(save); - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); setDecodeView(); fetch(); } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java index 5a4c66557..520be734a 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java @@ -450,7 +450,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List } private void setVideoView() { - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); mBinding.control.reset.setText(ResUtil.getStringArray(R.array.select_reset)[Setting.getReset()]); } @@ -1072,7 +1072,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List private void onDecode(boolean save) { mPlayers.toggleDecode(save); - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); setDecodeView(); onRefresh(); } diff --git a/app/src/main/java/com/fongmi/android/tv/Setting.java b/app/src/main/java/com/fongmi/android/tv/Setting.java index 17105a442..b11d0e7bc 100644 --- a/app/src/main/java/com/fongmi/android/tv/Setting.java +++ b/app/src/main/java/com/fongmi/android/tv/Setting.java @@ -258,6 +258,14 @@ public class Setting { Prefers.put("update", update); } + public static boolean isPlayWithOthers() { + return Prefers.getBoolean("play_with_others", false); + } + + public static void putPlayWithOthers(boolean play) { + Prefers.put("play_with_others", play); + } + public static boolean isDanmu() { return Prefers.getBoolean("danmu"); } diff --git a/app/src/main/java/com/fongmi/android/tv/api/Decoder.java b/app/src/main/java/com/fongmi/android/tv/api/Decoder.java index 73f0a4986..c52c9ac85 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/Decoder.java +++ b/app/src/main/java/com/fongmi/android/tv/api/Decoder.java @@ -35,16 +35,9 @@ public class Decoder { } private static String fix(String url, String data) { - if (url.startsWith("file") || url.startsWith("assets")) url = UrlUtil.convert(url); + url = UrlUtil.convert(url); Matcher matcher = JS_URI.matcher(data); - while (matcher.find()) { - String ext = matcher.group(0); - String t = ext.replace("\"./", "\"" + UrlUtil.resolve(url, "./")); - t = t.replace("\"../", "\"" + UrlUtil.resolve(url, "../")); - t = t.replace("./", "__JS1__"); - t = t.replace("../", "__JS2__"); - data = data.replace(ext, t); - } + while (matcher.find()) data = replace(url, data, matcher.group()); if (data.contains("../")) data = data.replace("../", UrlUtil.resolve(url, "../")); if (data.contains("./")) data = data.replace("./", UrlUtil.resolve(url, "./")); if (data.contains("__JS1__")) data = data.replace("__JS1__", "./"); @@ -52,6 +45,13 @@ public class Decoder { return data; } + private static String replace(String url, String data, String ext) { + String t = ext.replace("\"./", "\"" + UrlUtil.resolve(url, "./")); + t = t.replace("\"../", "\"" + UrlUtil.resolve(url, "../")); + t = t.replace("./", "__JS1__").replace("../", "__JS2__"); + return data.replace(ext, t); + } + public static String getExt(String ext) { try { return base64(getData(ext.substring(4))); diff --git a/app/src/main/java/com/fongmi/android/tv/player/Players.java b/app/src/main/java/com/fongmi/android/tv/player/Players.java index daec1b17c..431e3e42d 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/Players.java +++ b/app/src/main/java/com/fongmi/android/tv/player/Players.java @@ -140,16 +140,16 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, ParseCal MediaControllerCompat.setMediaController(activity, session.getController()); } - public void set(PlayerView exo, IjkVideoView ijk) { + public void init(PlayerView exo, IjkVideoView ijk) { releaseExo(); releaseIjk(); - setupExo(exo); - setupIjk(ijk); + initExo(exo); + initIjk(ijk); } - private void setupExo(PlayerView view) { + private void initExo(PlayerView view) { exoPlayer = new ExoPlayer.Builder(App.get()).setLoadControl(ExoUtil.buildLoadControl()).setTrackSelector(ExoUtil.buildTrackSelector()).setRenderersFactory(ExoUtil.buildRenderersFactory(decode)).setMediaSourceFactory(ExoUtil.buildMediaSourceFactory()).build(); - exoPlayer.setAudioAttributes(AudioAttributes.DEFAULT, true); + exoPlayer.setAudioAttributes(AudioAttributes.DEFAULT, !Setting.isPlayWithOthers()); exoPlayer.addAnalyticsListener(new EventLogger()); exoPlayer.setHandleAudioBecomingNoisy(true); view.setRender(Setting.getRender()); @@ -158,7 +158,7 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, ParseCal view.setPlayer(exoPlayer); } - private void setupIjk(IjkVideoView view) { + private void initIjk(IjkVideoView view) { ijkPlayer = view.render(Setting.getRender()).decode(decode); ijkPlayer.addListener(this); ijkPlayer.setPlayer(player); diff --git a/app/src/main/java/com/fongmi/android/tv/player/exo/NextRenderersFactory.java b/app/src/main/java/com/fongmi/android/tv/player/exo/NextRenderersFactory.java index f0ab74689..c5171c7c3 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/exo/NextRenderersFactory.java +++ b/app/src/main/java/com/fongmi/android/tv/player/exo/NextRenderersFactory.java @@ -7,6 +7,8 @@ import androidx.annotation.NonNull; import androidx.media3.common.util.Log; import androidx.media3.exoplayer.DefaultRenderersFactory; import androidx.media3.exoplayer.Renderer; +import androidx.media3.exoplayer.audio.AudioRendererEventListener; +import androidx.media3.exoplayer.audio.AudioSink; import androidx.media3.exoplayer.mediacodec.MediaCodecSelector; import androidx.media3.exoplayer.video.VideoRendererEventListener; @@ -14,24 +16,39 @@ import com.fongmi.android.tv.player.Players; import java.util.ArrayList; +import io.github.anilbeesetti.nextlib.media3ext.ffdecoder.FfmpegAudioRenderer; import io.github.anilbeesetti.nextlib.media3ext.ffdecoder.FfmpegVideoRenderer; public class NextRenderersFactory extends DefaultRenderersFactory { private static final String TAG = NextRenderersFactory.class.getSimpleName(); - private final int decode; public NextRenderersFactory(@NonNull Context context, int decode) { super(context); - this.decode = decode; setEnableDecoderFallback(true); setExtensionRendererMode(Players.isHard(decode) ? EXTENSION_RENDERER_MODE_ON : EXTENSION_RENDERER_MODE_PREFER); } + @Override + protected void buildAudioRenderers(@NonNull Context context, int extensionRendererMode, @NonNull MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, @NonNull AudioSink audioSink, @NonNull Handler eventHandler, @NonNull AudioRendererEventListener eventListener, @NonNull ArrayList out) { + super.buildAudioRenderers(context, extensionRendererMode, mediaCodecSelector, enableDecoderFallback, audioSink, eventHandler, eventListener, out); + int extensionRendererIndex = out.size(); + if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) { + extensionRendererIndex--; + } + try { + Renderer renderer = new FfmpegAudioRenderer(eventHandler, eventListener, audioSink); + out.add(extensionRendererIndex++, renderer); + Log.i(TAG, "Loaded FfmpegAudioRenderer."); + } catch (Exception e) { + throw new RuntimeException("Error instantiating Ffmpeg extension", e); + } + } + @Override protected void buildVideoRenderers(@NonNull Context context, int extensionRendererMode, @NonNull MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, @NonNull Handler eventHandler, @NonNull VideoRendererEventListener eventListener, long allowedVideoJoiningTimeMs, @NonNull ArrayList out) { super.buildVideoRenderers(context, extensionRendererMode, mediaCodecSelector, enableDecoderFallback, eventHandler, eventListener, allowedVideoJoiningTimeMs, out); - if (Players.isHard(decode)) return; + if (extensionRendererMode == EXTENSION_RENDERER_MODE_ON) return; int extensionRendererIndex = out.size(); try { Renderer renderer = new FfmpegVideoRenderer(allowedVideoJoiningTimeMs, eventHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY); diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 3559941c4..de725bbe1 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -112,6 +112,7 @@ 缩放比例 字幕样式 字幕大小 + 同时播放 弹幕加载 弹幕速度 弹幕大小 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 3675d3751..8f1b270e9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -112,6 +112,7 @@ 縮放比例 字幕樣式 字幕大小 + 同时播放 彈幕載入 彈幕速度 彈幕大小 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9c4b2e099..d7dd4e4ec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -113,6 +113,7 @@ Scale Caption style Subtitle size + Play with others Danmu load Danmu speed Danmu size diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index 86574f88e..9df541313 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -235,7 +235,7 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, Custom } private void setVideoView() { - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); setScale(Setting.getLiveScale()); mBinding.control.action.invert.setActivated(Setting.isInvert()); mBinding.control.action.across.setActivated(Setting.isAcross()); @@ -468,7 +468,7 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, Custom private void onDecode(boolean save) { mPlayers.toggleDecode(save); - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); setDecodeView(); setR1Callback(); fetch(); diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java index 49a07a21d..a912f9f1b 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java @@ -432,7 +432,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo } private void setVideoView() { - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); if (isPort() && ResUtil.isLand(this)) enterFullscreen(); mBinding.control.action.reset.setText(ResUtil.getStringArray(R.array.select_reset)[Setting.getReset()]); mBinding.video.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> mPiP.update(getActivity(), view)); @@ -924,7 +924,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo private void onDecode(boolean save) { mPlayers.toggleDecode(save); - mPlayers.set(getExo(), getIjk()); + mPlayers.init(getExo(), getIjk()); setDecodeView(); setR1Callback(); onRefresh(); diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingPlayerFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingPlayerFragment.java index 799b5c259..87e5460ee 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingPlayerFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingPlayerFragment.java @@ -58,6 +58,7 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B mBinding.captionText.setText(getSwitch(Setting.isCaption())); mBinding.bufferText.setText(String.valueOf(Setting.getBuffer())); mBinding.subtitleText.setText(String.valueOf(Setting.getSubtitle())); + mBinding.playWithOthersText.setText(getSwitch(Setting.isPlayWithOthers())); mBinding.danmuLoadText.setText(getSwitch(Setting.isDanmuLoad())); mBinding.rtspText.setText((rtsp = ResUtil.getStringArray(R.array.select_rtsp))[Setting.getRtsp()]); mBinding.flagText.setText((flag = ResUtil.getStringArray(R.array.select_flag))[Setting.getFlag()]); @@ -85,6 +86,7 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B mBinding.caption.setOnClickListener(this::setCaption); mBinding.subtitle.setOnClickListener(this::onSubtitle); mBinding.caption.setOnLongClickListener(this::onCaption); + mBinding.playWithOthers.setOnClickListener(this::setPlayWithOthers); mBinding.danmuLoad.setOnClickListener(this::setDanmuLoad); mBinding.background.setOnClickListener(this::onBackground); } @@ -94,6 +96,7 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B mBinding.http.setVisibility(Players.isExo(Setting.getPlayer()) ? View.VISIBLE : View.GONE); mBinding.buffer.setVisibility(Players.isExo(Setting.getPlayer()) ? View.VISIBLE : View.GONE); mBinding.tunnel.setVisibility(Players.isExo(Setting.getPlayer()) ? View.VISIBLE : View.GONE); + mBinding.playWithOthers.setVisibility(Players.isExo(Setting.getPlayer()) ? View.VISIBLE : View.GONE); } private void onUa(View view) { @@ -189,6 +192,11 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B mBinding.subtitleText.setText(String.valueOf(size)); } + private void setPlayWithOthers(View view) { + Setting.putPlayWithOthers(!Setting.isPlayWithOthers()); + mBinding.playWithOthersText.setText(getSwitch(Setting.isPlayWithOthers())); + } + private void setDanmuLoad(View view) { Setting.putDanmuLoad(!Setting.isDanmuLoad()); mBinding.danmuLoadText.setText(getSwitch(Setting.isDanmuLoad())); diff --git a/app/src/mobile/res/layout/fragment_setting_player.xml b/app/src/mobile/res/layout/fragment_setting_player.xml index 2aa330629..ca8ddb8eb 100644 --- a/app/src/mobile/res/layout/fragment_setting_player.xml +++ b/app/src/mobile/res/layout/fragment_setting_player.xml @@ -239,6 +239,33 @@ + + + + + + + +