diff --git a/app/build.gradle b/app/build.gradle index d3ee421b1..e10b4087c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -98,6 +98,7 @@ dependencies { implementation 'androidx.media:media:1.7.0' implementation 'androidx.room:room-runtime:2.6.1' implementation 'cat.ereza:customactivityoncrash:2.4.0' + implementation('com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.2') { 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' @@ -137,7 +138,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/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 118d8c5e5..8adfd368e 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 @@ -195,7 +195,7 @@ public class CastActivity extends BaseActivity implements CustomKeyDownCast.List } private void onDecode() { - mPlayers.toggleDecode(); + mPlayers.toggleDecode(mBinding.exo); setDecode(); onReset(); } @@ -346,7 +346,8 @@ public class CastActivity extends BaseActivity implements CustomKeyDownCast.List private void onCheck(ErrorEvent event) { if (event.getCode() >= PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED && event.getCode() <= PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED) mPlayers.setFormat(ExoUtil.getMimeType(event.getCode())); - else mPlayers.toggleDecode(); + else if (event.getCode() == PlaybackException.ERROR_CODE_DECODER_INIT_FAILED) mPlayers.init(mBinding.exo); + else mPlayers.toggleDecode(mBinding.exo); mPlayers.setMediaItem(); setDecode(); } 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 d176bf732..bdc0bc421 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 @@ -385,7 +385,7 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick } private void onDecode() { - mPlayers.toggleDecode(); + mPlayers.toggleDecode(mBinding.exo); setDecode(); fetch(); } @@ -748,7 +748,8 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick private void onCheck(ErrorEvent event) { if (event.getCode() >= PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED && event.getCode() <= PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED) mPlayers.setFormat(ExoUtil.getMimeType(event.getCode())); - else mPlayers.toggleDecode(); + else if (event.getCode() == PlaybackException.ERROR_CODE_DECODER_INIT_FAILED) mPlayers.init(mBinding.exo); + else mPlayers.toggleDecode(mBinding.exo); mPlayers.setMediaItem(); setDecode(); } 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 ea68b2415..8d37b4556 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 @@ -843,7 +843,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List } private void onDecode() { - mPlayers.toggleDecode(); + mPlayers.toggleDecode(mBinding.exo); setDecode(); onRefresh(); } @@ -1142,7 +1142,8 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List private void onCheck(ErrorEvent event) { if (event.getCode() >= PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED && event.getCode() <= PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED) mPlayers.setFormat(ExoUtil.getMimeType(event.getCode())); - else mPlayers.toggleDecode(); + else if (event.getCode() == PlaybackException.ERROR_CODE_DECODER_INIT_FAILED) mPlayers.init(mBinding.exo); + else mPlayers.toggleDecode(mBinding.exo); mPlayers.setMediaItem(); setDecode(); } 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 31797b7d0..8e000d87e 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 @@ -277,8 +277,9 @@ public class Players implements Player.Listener, ParseCallback { return setSpeed(speed); } - public void toggleDecode() { + public void toggleDecode(PlayerView exo) { Setting.putDecode(decode = isHard() ? SOFT : HARD); + init(exo); } public String getPositionTime(long time) { diff --git a/app/src/main/java/com/fongmi/android/tv/player/exo/ExoUtil.java b/app/src/main/java/com/fongmi/android/tv/player/exo/ExoUtil.java index 552f084e0..b6c115ccf 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/exo/ExoUtil.java +++ b/app/src/main/java/com/fongmi/android/tv/player/exo/ExoUtil.java @@ -1,8 +1,5 @@ package com.fongmi.android.tv.player.exo; -import static androidx.media3.exoplayer.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON; -import static androidx.media3.exoplayer.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER; - import android.content.Context; import android.graphics.Color; import android.net.Uri; @@ -17,7 +14,6 @@ import androidx.media3.common.TrackSelectionOverride; import androidx.media3.common.Tracks; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.DefaultLoadControl; -import androidx.media3.exoplayer.DefaultRenderersFactory; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.LoadControl; import androidx.media3.exoplayer.RenderersFactory; @@ -56,7 +52,7 @@ public class ExoUtil { } public static RenderersFactory buildRenderersFactory(int decode) { - return new DefaultRenderersFactory(App.get()).setEnableDecoderFallback(true).setExtensionRendererMode(Players.isHard(decode) ? EXTENSION_RENDERER_MODE_ON : EXTENSION_RENDERER_MODE_PREFER); + return new NextRenderersFactory(App.get(), decode); } public static MediaSource.Factory buildMediaSourceFactory() { 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 new file mode 100644 index 000000000..cb9bfb796 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/player/exo/NextRenderersFactory.java @@ -0,0 +1,61 @@ +package com.fongmi.android.tv.player.exo; + +import android.content.Context; +import android.os.Handler; + +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; + +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(); + + public NextRenderersFactory(@NonNull Context context, int decode) { + super(context); + 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 (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); + out.add(extensionRendererIndex++, renderer); + Log.i(TAG, "Loaded FfmpegVideoRenderer."); + } catch (Exception e) { + throw new RuntimeException("Error instantiating Ffmpeg extension", e); + } + } +} \ No newline at end of file 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 08e257d32..ecb8b2c7d 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 @@ -416,7 +416,7 @@ public class LiveActivity extends BaseActivity implements CustomKeyDownLive.List } private void onDecode() { - mPlayers.toggleDecode(); + mPlayers.toggleDecode(mBinding.exo); setR1Callback(); setDecode(); fetch(); @@ -807,7 +807,8 @@ public class LiveActivity extends BaseActivity implements CustomKeyDownLive.List private void onCheck(ErrorEvent event) { if (event.getCode() >= PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED && event.getCode() <= PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED) mPlayers.setFormat(ExoUtil.getMimeType(event.getCode())); - else mPlayers.toggleDecode(); + else if (event.getCode() == PlaybackException.ERROR_CODE_DECODER_INIT_FAILED) mPlayers.init(mBinding.exo); + else mPlayers.toggleDecode(mBinding.exo); mPlayers.setMediaItem(); setDecode(); } 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 b57af2311..d02dc01eb 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 @@ -773,7 +773,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo } private void onDecode() { - mPlayers.toggleDecode(); + mPlayers.toggleDecode(mBinding.exo); setR1Callback(); setDecode(); onRefresh(); @@ -1162,7 +1162,8 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo private void onCheck(ErrorEvent event) { if (event.getCode() >= PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED && event.getCode() <= PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED) mPlayers.setFormat(ExoUtil.getMimeType(event.getCode())); - else mPlayers.toggleDecode(); + else if (event.getCode() == PlaybackException.ERROR_CODE_DECODER_INIT_FAILED) mPlayers.init(mBinding.exo); + else mPlayers.toggleDecode(mBinding.exo); mPlayers.setMediaItem(); setDecode(); }