From cbf7615b6b9e1ef328064bd0f556c8e3f215c8d9 Mon Sep 17 00:00:00 2001 From: Kiall Mac Innes Date: Sat, 29 Apr 2017 19:24:21 +0100 Subject: [PATCH] Update to ExoPlayer 2.4.0k1 Fixes #149 --- app/build.gradle | 5 +- .../tvheadend/player/EventLogger.java | 7 + .../ie/macinnes/tvheadend/player/Player.java | 15 +- ...er.java => TvheadendRenderersFactory.java} | 137 +++++++++++------- .../player/TvheadendTrackSelector.java | 10 +- 5 files changed, 113 insertions(+), 61 deletions(-) rename app/src/main/java/ie/macinnes/tvheadend/player/{SimpleTvheadendPlayer.java => TvheadendRenderersFactory.java} (57%) diff --git a/app/build.gradle b/app/build.gradle index f2869c9..eafb003 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,8 +77,9 @@ dependencies { compile 'com.android.support:support-v4:25.3.1' compile 'com.android.support:leanback-v17:25.3.1' compile 'com.android.support:preference-leanback-v17:25.3.1' - compile 'com.google.android.exoplayer:exoplayer:r2.3.0k1' - compile 'com.google.android.exoplayer:extension-ffmpeg:r2.3.0k1' + compile 'com.google.android.exoplayer:exoplayer-core:r2.4.0k1' + compile 'com.google.android.exoplayer:exoplayer-ui:r2.4.0k1' + compile 'com.google.android.exoplayer:extension-ffmpeg:r2.4.0k1' // Used for testing local exoplayer builds // compile(name: 'library-debug', ext: 'aar') // Used for testing local exoplayer-ffmpeg builds diff --git a/app/src/main/java/ie/macinnes/tvheadend/player/EventLogger.java b/app/src/main/java/ie/macinnes/tvheadend/player/EventLogger.java index 581fb11..bc3d5fa 100644 --- a/app/src/main/java/ie/macinnes/tvheadend/player/EventLogger.java +++ b/app/src/main/java/ie/macinnes/tvheadend/player/EventLogger.java @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.audio.AudioRendererEventListener; @@ -102,6 +103,12 @@ public void onPositionDiscontinuity() { Log.d(TAG, "positionDiscontinuity"); } + @Override + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + Log.d(TAG, "playbackParameters " + String.format( + "[speed=%.2f, pitch=%.2f]", playbackParameters.speed, playbackParameters.pitch)); + } + @Override public void onTimelineChanged(Timeline timeline, Object manifest) { if (timeline == null) { diff --git a/app/src/main/java/ie/macinnes/tvheadend/player/Player.java b/app/src/main/java/ie/macinnes/tvheadend/player/Player.java index 75e32f1..1de7123 100644 --- a/app/src/main/java/ie/macinnes/tvheadend/player/Player.java +++ b/app/src/main/java/ie/macinnes/tvheadend/player/Player.java @@ -36,7 +36,9 @@ import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.LoadControl; +import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.RendererCapabilities; +import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.ExtractorsFactory; @@ -202,12 +204,10 @@ private void buildExoPlayer() { LoadControl loadControl = buildLoadControl(); - int extensionRendererMode = SimpleExoPlayer.EXTENSION_RENDERER_MODE_PREFER; - - mExoPlayer = new SimpleTvheadendPlayer( - mContext, mTrackSelector, loadControl, null, extensionRendererMode, - ExoPlayerFactory.DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS); + RenderersFactory renderersFactory = new TvheadendRenderersFactory( + mContext, null, TvheadendRenderersFactory.DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS); + mExoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, mTrackSelector, loadControl); mExoPlayer.addListener(this); // Add the EventLogger @@ -345,4 +345,9 @@ public void onPlayerError(ExoPlaybackException error) { public void onPositionDiscontinuity() { } + + @Override + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + + } } diff --git a/app/src/main/java/ie/macinnes/tvheadend/player/SimpleTvheadendPlayer.java b/app/src/main/java/ie/macinnes/tvheadend/player/TvheadendRenderersFactory.java similarity index 57% rename from app/src/main/java/ie/macinnes/tvheadend/player/SimpleTvheadendPlayer.java rename to app/src/main/java/ie/macinnes/tvheadend/player/TvheadendRenderersFactory.java index cacd817..4c0f1c0 100644 --- a/app/src/main/java/ie/macinnes/tvheadend/player/SimpleTvheadendPlayer.java +++ b/app/src/main/java/ie/macinnes/tvheadend/player/TvheadendRenderersFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Kiall Mac Innes + * Copyright (c) 2017 Kiall Mac Innes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,12 @@ import android.content.SharedPreferences; import android.os.Build; import android.os.Handler; +import android.os.Looper; import android.util.Log; -import com.google.android.exoplayer2.LoadControl; +import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.Renderer; +import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.audio.AudioCapabilities; import com.google.android.exoplayer2.audio.AudioProcessor; @@ -35,7 +37,8 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; -import com.google.android.exoplayer2.trackselection.TrackSelector; +import com.google.android.exoplayer2.metadata.MetadataRenderer; +import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.VideoRendererEventListener; @@ -44,19 +47,89 @@ import ie.macinnes.tvheadend.Constants; import ie.macinnes.tvheadend.R; +public class TvheadendRenderersFactory extends DefaultRenderersFactory { + private static final String TAG = TvheadendRenderersFactory.class.getName(); -public class SimpleTvheadendPlayer extends SimpleExoPlayer { - private static final String TAG = SimpleTvheadendPlayer.class.getName(); + private final Context mContext; + private final DrmSessionManager mDrmSessionManager; + private final long mAllowedVideoJoiningTimeMs; - public SimpleTvheadendPlayer(Context context, TrackSelector trackSelector, LoadControl loadControl, - DrmSessionManager drmSessionManager, int extensionRendererMode, - long allowedVideoJoiningTimeMs) { - super(context, trackSelector, loadControl, drmSessionManager, extensionRendererMode, allowedVideoJoiningTimeMs); + public TvheadendRenderersFactory(Context context, + DrmSessionManager drmSessionManager, + long allowedVideoJoiningTimeMs) { + super(context, drmSessionManager, EXTENSION_RENDERER_MODE_ON, allowedVideoJoiningTimeMs); + + mContext = context; + mDrmSessionManager = drmSessionManager; + mAllowedVideoJoiningTimeMs = allowedVideoJoiningTimeMs; + } + + /** + * Builds video renderers for use by the player. + * + * @param context The {@link Context} associated with the player. + * @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player + * will not be used for DRM protected playbacks. + * @param allowedVideoJoiningTimeMs The maximum duration in milliseconds for which video + * renderers can attempt to seamlessly join an ongoing playback. + * @param eventHandler A handler associated with the main thread's looper. + * @param eventListener An event listener. + * @param extensionRendererMode The extension renderer mode. + * @param out An array to which the built renderers should be appended. + */ + protected void buildVideoRenderers(Context context, + DrmSessionManager drmSessionManager, long allowedVideoJoiningTimeMs, + Handler eventHandler, VideoRendererEventListener eventListener, + @ExtensionRendererMode int extensionRendererMode, ArrayList out) { + SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.PREFERENCE_TVHEADEND, Context.MODE_PRIVATE); + + final boolean enableShieldWorkaround = sharedPreferences.getBoolean( + Constants.KEY_SHIELD_WORKAROUND_ENABLED, + context.getResources().getBoolean(R.bool.pref_default_shield_workaround_enabled) + ); + + if (Build.MODEL.equals("SHIELD Android TV") && enableShieldWorkaround) { + Log.d(TAG, "Adding ShieldVideoRenderer"); + out.add(new ShieldVideoRenderer( + context, + MediaCodecSelector.DEFAULT, + allowedVideoJoiningTimeMs, + drmSessionManager, + false, + eventHandler, + eventListener, + MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)); + } else { + Log.d(TAG, "Adding MediaCodecVideoRenderer"); + out.add(new MediaCodecVideoRenderer( + context, + MediaCodecSelector.DEFAULT, + allowedVideoJoiningTimeMs, + drmSessionManager, + false, + eventHandler, + eventListener, + MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)); + } } - @Override - protected void buildAudioRenderers(Context context, Handler mainHandler, DrmSessionManager drmSessionManager, - int extensionRendererMode, AudioRendererEventListener eventListener, AudioProcessor[] audioProcessors, + /** + * Builds audio renderers for use by the player. + * + * @param context The {@link Context} associated with the player. + * @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player + * will not be used for DRM protected playbacks. + * @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio + * buffers before output. May be empty. + * @param eventHandler A handler to use when invoking event listeners and outputs. + * @param eventListener An event listener. + * @param extensionRendererMode The extension renderer mode. + * @param out An array to which the built renderers should be appended. + */ + protected void buildAudioRenderers(Context context, + DrmSessionManager drmSessionManager, + AudioProcessor[] audioProcessors, Handler eventHandler, + AudioRendererEventListener eventListener, @ExtensionRendererMode int extensionRendererMode, ArrayList out) { AudioCapabilities audioCapabilities = AudioCapabilities.getCapabilities(context); @@ -70,7 +143,7 @@ protected void buildAudioRenderers(Context context, Handler mainHandler, DrmSess Log.d(TAG, "Adding MediaCodecAudioRenderer"); MediaCodecSelector mediaCodecSelector = buildMediaCodecSelector(enablePassthroughDecoder); out.add(new MediaCodecAudioRenderer(mediaCodecSelector, drmSessionManager, - true, mainHandler, eventListener, audioCapabilities)); + true, eventHandler, eventListener, audioCapabilities)); // FFMpeg Audio Decoder final boolean enableFfmpegAudioRenderer = sharedPreferences.getBoolean( @@ -80,7 +153,7 @@ protected void buildAudioRenderers(Context context, Handler mainHandler, DrmSess if (enableFfmpegAudioRenderer) { Log.d(TAG, "Adding FfmpegAudioRenderer"); - out.add(new FfmpegAudioRenderer(mainHandler, eventListener, audioProcessors)); + out.add(new FfmpegAudioRenderer(eventHandler, eventListener, audioProcessors)); } } @@ -106,40 +179,4 @@ public MediaCodecInfo getPassthroughDecoderInfo() throws MediaCodecUtil.DecoderQ } }; } - - @Override - protected void buildVideoRenderers(Context context, Handler mainHandler, DrmSessionManager drmSessionManager, - int extensionRendererMode, VideoRendererEventListener eventListener, long allowedVideoJoiningTimeMs, - ArrayList out) { - SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.PREFERENCE_TVHEADEND, Context.MODE_PRIVATE); - - final boolean enableShieldWorkaround = sharedPreferences.getBoolean( - Constants.KEY_SHIELD_WORKAROUND_ENABLED, - context.getResources().getBoolean(R.bool.pref_default_shield_workaround_enabled) - ); - - if (Build.MODEL.equals("SHIELD Android TV") && enableShieldWorkaround) { - Log.d(TAG, "Adding ShieldVideoRenderer"); - out.add(new ShieldVideoRenderer( - context, - MediaCodecSelector.DEFAULT, - allowedVideoJoiningTimeMs, - drmSessionManager, - false, - mainHandler, - eventListener, - SimpleExoPlayer.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)); - } else { - Log.d(TAG, "Adding MediaCodecVideoRenderer"); - out.add(new MediaCodecVideoRenderer( - context, - MediaCodecSelector.DEFAULT, - allowedVideoJoiningTimeMs, - drmSessionManager, - false, - mainHandler, - eventListener, - SimpleExoPlayer.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)); - } - } } diff --git a/app/src/main/java/ie/macinnes/tvheadend/player/TvheadendTrackSelector.java b/app/src/main/java/ie/macinnes/tvheadend/player/TvheadendTrackSelector.java index c065184..5fd71b4 100644 --- a/app/src/main/java/ie/macinnes/tvheadend/player/TvheadendTrackSelector.java +++ b/app/src/main/java/ie/macinnes/tvheadend/player/TvheadendTrackSelector.java @@ -137,14 +137,16 @@ protected TrackSelection selectVideoTrack( } @Override - protected TrackSelection selectAudioTrack( - TrackGroupArray groups, int[][] formatSupport, String preferredAudioLanguage, - boolean exceedRendererCapabilitiesIfNecessary) { + protected TrackSelection selectAudioTrack(TrackGroupArray groups, int[][] formatSupport, + String preferredAudioLanguage, boolean exceedRendererCapabilitiesIfNecessary, + boolean allowMixedMimeAdaptiveness, TrackSelection.Factory adaptiveTrackSelectionFactory) { Log.d(TAG, "TrackSelector selectAudioTrack"); // If we haven't explicitly chosen a track, defer to the DefaultTrackSelector implementation. if (mAudioTrackId == null) { - return super.selectAudioTrack(groups, formatSupport, preferredAudioLanguage, exceedRendererCapabilitiesIfNecessary); + return super.selectAudioTrack( + groups, formatSupport, preferredAudioLanguage, exceedRendererCapabilitiesIfNecessary, + allowMixedMimeAdaptiveness, adaptiveTrackSelectionFactory); } else { for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { TrackGroup trackGroup = groups.get(groupIndex);