From ba6368a07a36109fc3fa027705e6ab40258f8144 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 21 Jul 2016 02:08:21 +0100 Subject: [PATCH 1/5] Don't try to access the caption manager when in edit mode. Issue: #1991 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=137131819 --- .../android/exoplayer2/trackselection/TrackSelector.java | 1 + .../com/google/android/exoplayer2/ui/SubtitleView.java | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java index 41c62f6e0e..9c859312cb 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java @@ -46,6 +46,7 @@ public abstract class TrackSelector { * @param trackSelections The new track selections. */ void onTrackSelectionsChanged(TrackSelections trackSelections); + } private final Handler eventHandler; diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java b/library/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java index 0c8d9ef92e..49516ab6f4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java @@ -122,10 +122,10 @@ public final class SubtitleView extends View implements TextRenderer.Output { /** * Sets the text size to one derived from {@link CaptioningManager#getFontScale()}, or to a - * default size on API level 19 and earlier. + * default size before API level 19. */ public void setUserDefaultTextSize() { - float fontScale = Util.SDK_INT >= 19 ? getUserCaptionFontScaleV19() : 1f; + float fontScale = Util.SDK_INT >= 19 && !isInEditMode() ? getUserCaptionFontScaleV19() : 1f; setFractionalTextSize(DEFAULT_TEXT_SIZE_FRACTION * fontScale); } @@ -180,10 +180,11 @@ public final class SubtitleView extends View implements TextRenderer.Output { /** * Sets the caption style to be equivalent to the one returned by - * {@link CaptioningManager#getUserStyle()}, or to a default style on API level 19 and earlier. + * {@link CaptioningManager#getUserStyle()}, or to a default style before API level 19. */ public void setUserDefaultStyle() { - setStyle(Util.SDK_INT >= 19 ? getUserCaptionStyleV19() : CaptionStyleCompat.DEFAULT); + setStyle(Util.SDK_INT >= 19 && !isInEditMode() + ? getUserCaptionStyleV19() : CaptionStyleCompat.DEFAULT); } /** From 8b3025d450278572a3e0f1da09fe039d7b918678 Mon Sep 17 00:00:00 2001 From: "[]inger" <[]inger@google.com> Date: Tue, 25 Oct 2016 05:10:42 -0700 Subject: [PATCH 2/5] Make control view layout resource customizable. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=137145209 --- .../exoplayer2/ui/PlaybackControlView.java | 138 ++++++++++++------ .../exoplayer2/ui/SimpleExoPlayerView.java | 21 ++- .../res/layout/exo_playback_control_view.xml | 27 ++-- .../res/layout/exo_simple_player_view.xml | 14 +- library/src/main/res/values/attrs.xml | 3 + library/src/main/res/values/ids.xml | 26 ++++ library/src/main/res/values/styles.xml | 10 ++ 7 files changed, 163 insertions(+), 76 deletions(-) create mode 100644 library/src/main/res/values/ids.xml diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index 89c778d072..470e173c02 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -24,7 +24,6 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; -import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.TextView; import com.google.android.exoplayer2.C; @@ -38,6 +37,16 @@ import java.util.Locale; /** * A view to control video playback of an {@link ExoPlayer}. + * + * By setting the view attribute {@code controller_layout_id} a layout resource to use can + * be customized. All views are optional but if the buttons should have an appropriate logic + * assigned, the id of the views in the layout have to match the expected ids as follows: + * + * */ public class PlaybackControlView extends FrameLayout { @@ -63,12 +72,13 @@ public class PlaybackControlView extends FrameLayout { private final ComponentListener componentListener; private final View previousButton; private final View nextButton; - private final ImageButton playButton; + private final View playButton; + private final View pauseButton; + private final View fastForwardButton; + private final View rewindButton; private final TextView time; private final TextView timeCurrent; private final SeekBar progressBar; - private final View fastForwardButton; - private final View rewindButton; private final StringBuilder formatBuilder; private final Formatter formatter; private final Timeline.Window currentWindow; @@ -108,6 +118,7 @@ public class PlaybackControlView extends FrameLayout { public PlaybackControlView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + int layoutResourceId = R.layout.exo_playback_control_view; rewindMs = DEFAULT_REWIND_MS; fastForwardMs = DEFAULT_FAST_FORWARD_MS; showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS; @@ -119,32 +130,49 @@ public class PlaybackControlView extends FrameLayout { fastForwardMs = a.getInt(R.styleable.PlaybackControlView_fastforward_increment, fastForwardMs); showTimeoutMs = a.getInt(R.styleable.PlaybackControlView_show_timeout, showTimeoutMs); + layoutResourceId = a.getResourceId(R.styleable.PlaybackControlView_controller_layout_id, + layoutResourceId); } finally { a.recycle(); } } - currentWindow = new Timeline.Window(); formatBuilder = new StringBuilder(); formatter = new Formatter(formatBuilder, Locale.getDefault()); componentListener = new ComponentListener(); - LayoutInflater.from(context).inflate(R.layout.exo_playback_control_view, this); - time = (TextView) findViewById(R.id.time); - timeCurrent = (TextView) findViewById(R.id.time_current); - progressBar = (SeekBar) findViewById(R.id.mediacontroller_progress); - progressBar.setOnSeekBarChangeListener(componentListener); - progressBar.setMax(PROGRESS_BAR_MAX); - playButton = (ImageButton) findViewById(R.id.play); - playButton.setOnClickListener(componentListener); - previousButton = findViewById(R.id.prev); - previousButton.setOnClickListener(componentListener); - nextButton = findViewById(R.id.next); - nextButton.setOnClickListener(componentListener); - rewindButton = findViewById(R.id.rew); - rewindButton.setOnClickListener(componentListener); - fastForwardButton = findViewById(R.id.ffwd); - fastForwardButton.setOnClickListener(componentListener); + LayoutInflater.from(context).inflate(layoutResourceId, this); + time = (TextView) findViewById(R.id.exo_time); + timeCurrent = (TextView) findViewById(R.id.exo_time_current); + progressBar = (SeekBar) findViewById(R.id.exo_progress); + if (progressBar != null) { + progressBar.setOnSeekBarChangeListener(componentListener); + progressBar.setMax(PROGRESS_BAR_MAX); + } + playButton = findViewById(R.id.exo_play); + if (playButton != null) { + playButton.setOnClickListener(componentListener); + } + pauseButton = findViewById(R.id.exo_pause); + if (pauseButton != null) { + pauseButton.setOnClickListener(componentListener); + } + previousButton = findViewById(R.id.exo_prev); + if (previousButton != null) { + previousButton.setOnClickListener(componentListener); + } + nextButton = findViewById(R.id.exo_next); + if (nextButton != null) { + nextButton.setOnClickListener(componentListener); + } + rewindButton = findViewById(R.id.exo_rew); + if (rewindButton != null) { + rewindButton.setOnClickListener(componentListener); + } + fastForwardButton = findViewById(R.id.exo_ffwd); + if (fastForwardButton != null) { + fastForwardButton.setOnClickListener(componentListener); + } } /** @@ -285,11 +313,12 @@ public class PlaybackControlView extends FrameLayout { return; } boolean playing = player != null && player.getPlayWhenReady(); - String contentDescription = getResources().getString( - playing ? R.string.exo_controls_pause_description : R.string.exo_controls_play_description); - playButton.setContentDescription(contentDescription); - playButton.setImageResource( - playing ? R.drawable.exo_controls_pause : R.drawable.exo_controls_play); + if (playButton != null) { + playButton.setVisibility(playing ? GONE : VISIBLE); + } + if (pauseButton != null) { + pauseButton.setVisibility(playing ? VISIBLE : GONE); + } } private void updateNavigation() { @@ -313,7 +342,9 @@ public class PlaybackControlView extends FrameLayout { setButtonEnabled(enableNext, nextButton); setButtonEnabled(fastForwardMs > 0 && isSeekable, fastForwardButton); setButtonEnabled(rewindMs > 0 && isSeekable, rewindButton); - progressBar.setEnabled(isSeekable); + if (progressBar != null) { + progressBar.setEnabled(isSeekable); + } } private void updateProgress() { @@ -322,16 +353,21 @@ public class PlaybackControlView extends FrameLayout { } long duration = player == null ? 0 : player.getDuration(); long position = player == null ? 0 : player.getCurrentPosition(); - time.setText(stringForTime(duration)); - if (!dragging) { + if (time != null) { + time.setText(stringForTime(duration)); + } + if (timeCurrent != null && !dragging) { timeCurrent.setText(stringForTime(position)); } - if (!dragging) { - progressBar.setProgress(progressBarValue(position)); + + if (progressBar != null) { + if (!dragging) { + progressBar.setProgress(progressBarValue(position)); + } + long bufferedPosition = player == null ? 0 : player.getBufferedPosition(); + progressBar.setSecondaryProgress(progressBarValue(bufferedPosition)); + // Remove scheduled updates. } - long bufferedPosition = player == null ? 0 : player.getBufferedPosition(); - progressBar.setSecondaryProgress(progressBarValue(bufferedPosition)); - // Remove scheduled updates. removeCallbacks(updateProgressAction); // Schedule an update if necessary. int playbackState = player == null ? ExoPlayer.STATE_IDLE : player.getPlaybackState(); @@ -350,6 +386,9 @@ public class PlaybackControlView extends FrameLayout { } private void setButtonEnabled(boolean enabled, View view) { + if (view == null) { + return; + } view.setEnabled(enabled); if (Util.SDK_INT >= 11) { setViewAlphaV11(view, enabled ? 1f : 0.3f); @@ -500,7 +539,7 @@ public class PlaybackControlView extends FrameLayout { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { + if (fromUser && timeCurrent != null) { timeCurrent.setText(stringForTime(positionValue(progress))); } } @@ -508,7 +547,9 @@ public class PlaybackControlView extends FrameLayout { @Override public void onStopTrackingTouch(SeekBar seekBar) { dragging = false; - player.seekTo(positionValue(seekBar.getProgress())); + if (player != null) { + player.seekTo(positionValue(seekBar.getProgress())); + } hideAfterTimeout(); } @@ -542,17 +583,20 @@ public class PlaybackControlView extends FrameLayout { @Override public void onClick(View view) { - Timeline currentTimeline = player.getCurrentTimeline(); - if (nextButton == view) { - next(); - } else if (previousButton == view) { - previous(); - } else if (fastForwardButton == view) { - fastForward(); - } else if (rewindButton == view && currentTimeline != null) { - rewind(); - } else if (playButton == view) { - player.setPlayWhenReady(!player.getPlayWhenReady()); + if (player != null) { + if (nextButton == view) { + next(); + } else if (previousButton == view) { + previous(); + } else if (fastForwardButton == view) { + fastForward(); + } else if (rewindButton == view) { + rewind(); + } else if (playButton == view) { + player.setPlayWhenReady(true); + } else if (pauseButton == view) { + player.setPlayWhenReady(false); + } } hideAfterTimeout(); } diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index 51955ccef3..692ff70ce1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -90,23 +90,30 @@ public final class SimpleExoPlayerView extends FrameLayout { LayoutInflater.from(context).inflate(R.layout.exo_simple_player_view, this); componentListener = new ComponentListener(); - layout = (AspectRatioFrameLayout) findViewById(R.id.video_frame); + layout = (AspectRatioFrameLayout) findViewById(R.id.exo_video_frame); layout.setResizeMode(resizeMode); - shutterView = findViewById(R.id.shutter); - subtitleLayout = (SubtitleView) findViewById(R.id.subtitles); + shutterView = findViewById(R.id.exo_shutter); + subtitleLayout = (SubtitleView) findViewById(R.id.exo_subtitles); subtitleLayout.setUserDefaultStyle(); subtitleLayout.setUserDefaultTextSize(); - controller = (PlaybackControlView) findViewById(R.id.control); - controller.hide(); + View controllerPlaceholder = findViewById(R.id.exo_controller_placeholder); + + controller = new PlaybackControlView(context, attrs); controller.setRewindIncrementMs(rewindMs); controller.setFastForwardIncrementMs(fastForwardMs); + controller.setLayoutParams(controllerPlaceholder.getLayoutParams()); + controller.hide(); this.controllerShowTimeoutMs = controllerShowTimeoutMs; + ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent()); + int controllerIndex = parent.indexOfChild(controllerPlaceholder); + parent.removeView(controllerPlaceholder); + parent.addView(controller, controllerIndex); + View view = useTextureView ? new TextureView(context) : new SurfaceView(context); ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); view.setLayoutParams(params); surfaceView = view; layout.addView(surfaceView, 0); diff --git a/library/src/main/res/layout/exo_playback_control_view.xml b/library/src/main/res/layout/exo_playback_control_view.xml index 5c9d2aeeb9..f9de461b0c 100644 --- a/library/src/main/res/layout/exo_playback_control_view.xml +++ b/library/src/main/res/layout/exo_playback_control_view.xml @@ -14,7 +14,6 @@ limitations under the License. --> - - - + - + + - @@ -56,7 +53,7 @@ android:layout_height="wrap_content" android:orientation="horizontal"> - - - - - + - - diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index d58882c0aa..1cc22314c0 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -23,6 +23,7 @@ + @@ -31,6 +32,7 @@ + @@ -41,6 +43,7 @@ + diff --git a/library/src/main/res/values/ids.xml b/library/src/main/res/values/ids.xml new file mode 100644 index 0000000000..9e810f1c2b --- /dev/null +++ b/library/src/main/res/values/ids.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/library/src/main/res/values/styles.xml b/library/src/main/res/values/styles.xml index fe1e26e967..3a1e73f887 100644 --- a/library/src/main/res/values/styles.xml +++ b/library/src/main/res/values/styles.xml @@ -41,4 +41,14 @@ @string/exo_controls_rewind_description + + + + From 1809836c2171b4452f57ab938fcc35d6d4826f38 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 25 Oct 2016 06:37:41 -0700 Subject: [PATCH 3/5] Provide a method for creating a reader for a particular PID This allows the user to create section readers(usually) for reserved pids, like SDT, EIT, CAT, etc. Issue:#726 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=137150853 --- .../androidTest/assets/ts/sample_with_sdt.ts | Bin 0 -> 4096 bytes .../extractor/ts/TsExtractorTest.java | 103 +++++++++++++++--- .../ts/DefaultTsPayloadReaderFactory.java | 6 + .../exoplayer2/extractor/ts/TsExtractor.java | 6 + .../extractor/ts/TsPayloadReader.java | 10 ++ 5 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 library/src/androidTest/assets/ts/sample_with_sdt.ts diff --git a/library/src/androidTest/assets/ts/sample_with_sdt.ts b/library/src/androidTest/assets/ts/sample_with_sdt.ts new file mode 100644 index 0000000000000000000000000000000000000000..8d1eccee156c0cbf01b1fd557dfcd843baedc11a GIT binary patch literal 4096 zcmd7VXHXN&x(D#klqy|%lU}745e+T$PyI zQWX-4^iUNP5J40LPwttyGxvNupUyq|VP|$`XXiJ&GtYk?6CgVqz~Jz@no&Ohz<%uy z#P@fMu&E#wi~T(WOf)$3UPOQoER^}5D?@)zBoYQskoP|M^WQK`fB-fCpoQh1c8Ko@ zU|6XF`riQiHw-2aik-TJ2jWY)ce2ePfck%?ShDRQzl2`jX*pSf!B#Bj+ixo z{qoyUjCc*}azcFLPZr3WA!Oa~aLt6umR-B^3|Bi^4!6KuyF+TcZEwZ7vtKqFlij`Z zZuif(CXY!%=+;<)67Z&VZAO{Yxo9{21;8JjJ^< zG3KQas0tHj37O17cj=cKQ-!yO{MI&8{mNfkulh(k^@T0KEfR~k>qd=V&7-~K;XE3a z9FQ`KoueMr%h|t9NQ6r6jpyj&lU(1Tm3G^(gAyzO9Qy52E^g-4Ngo7J8_zkrB?P(g za7@#O@hZNNpyAiw?HAU4`+fibG`~J+iXUMMX>Htj<_t-}?xP-V4|BxI-S8Q?ph~z; zLdQ(x(38>Z#FuA5_Fdk^8@g75M;oP7k`kvfi0BJz%S%=9qJA46Fob*IzH3{%QnV#< z#P{osO5NJ!ci%GvR)Ic+`$W|dbbk4VUUmf1d^QT(dZ(Z$h?LsZ&T95*#EEJqCA_}KP*K~>V?xxLdcAk|p`shZC zDh3&JxU>dfA~aIdRtTdAn7~!b2p86v7@8yp^tZh$dAFh#X>Z?^Zcez#afrH8)oP(?H#Vc%7j9gJ z$XBI?nk4Ccru}|xik&L~q}*l0g&mf5cmWM#f60nkg=mwZ^0}frdRv`L@#OLKmy?kx z=PJ$<&^*jc{oyD*v0^&d`O^gV9tNZenv!!b4cL^yafMVyip`T3Cm4sah z7r&~bS6waN96Bz*K0&l$X?uO{6?_^z9r2fohbB{SvT~}cE?xDY5Lok6*@~Sv0$+x+ z4>D^^>taGG8v%KKB!azK%yxc$HnE9lH<}lcNmc(osy1i9KL`wI;(yAb*y4>U3>O9$ zU0&%R;lXLH$R&-gb%o9zFp=+QPs$>8d;v>3$~F#XJdBeY)j8HaF?y{|G{;W0GK*D4 z?Vi{o5EZj{5dZ*i6C5QQ`G;b^T9o^P>~OiE;?jVVWKv@4(*#F1F8OH-^Y}7c_SJ-k zI8$ z_JL}&X%nY?auM>tCsflycAzNEq^e^28l8jcmcFG=j^LPgfv00}|0^d!pXo?Ob&8!Q z3*V&iyiR0GJ}$7ZW1a@i8uOdxx%oM9Z&ikS^q@rwXKW?DF4^FA-k{9p-TR9s;R@fu z{Fis%%Fk}xA7E5B@G@xEgyOQCq|}m%b94*xYGzt+vQhqdgt{E2;9ra?7CCI2I^3=@ zm*=9h*_3QqE2=y&Zu=hLn;^1Yi=Sjtty(s{Q|K~7PkP$t6wn!8NcO`NzaR!Uf7ekd zxnra_X8QW&l~cV3Az5;je0mm-<27YBz@{D56uY(`;6IZueFLf?oH|qM=470VqJvMa z`CUMtzD?pnj*H8Lfw@9JKHaNzf`+OP(uq@R4$fMaI>F<*BBKg(^d$O9t6l5dnl84Q zS>}oLWRuRnW>bnT(*x@V|0kQSatd%(>Whiu;g8`4s6s&&B5id@&mA0k zTJUZ1N)`cJe+FgnFo_O;9@4R;o$Eel@KlMnv_b=AIPque|HZ04sUd~M`2a7e{V0)vsZ*+Ua*Mer{GEE+gX(qV1wn+43H0CNVy<*L*fcl{ezL zxezhfF#URjw}^Q=HfRiaa_g%Ws=;3Y-5T}MZZYaPm!&|KeM(cqxU-ow)~iVzP3VxCcDrNPxIIWB$k*HL8os=%FEjw`&u>6; zA*mq8AxHTmyl}7tVgrkLg#q@oL>qO0s!I?=-r=XE_C3!U%iXY@cX|GVrkgTn+SD6D z)bC?}*Egnf)##L#tDB0QeSL=C{(xP2cqYN^IhbQ6-ZdM=kdK|8oux%|#3{N$MiC3Z z1Zl0@!1Ir9Pmc37-_%)a38Y#W;l3EzMLSVDN8@_YkN7EeLEpKx2kp)Ge{;{XH^1F( zxoj)&%9^p>Wr&^C(B`^(Qe>;q*&CR^2yVT#|}0Aq@RF2Gk~U!e$Sw)$u$ ze+Y|8wyKla7xBF}H>{Js((;`zCo6g#fTiJRa*B^hr92UhW$o=(c#u`|UjkCG8d={f%%+S== z*f#_EAO?1D0jcPmyrI~Iq`N=rYP3io0*fQs&7pi5N_H8@ah{ei35LN$1-cP}8gs~G z|KFjm;b!V@Q37oYtVeY%JZwHtA~X5y}95;eWF3Y9Y~iQCss`}Pg48d zNk>}5U$(aE2yq0)SMl_#}3`PoD2 z!z(RAkD^M2tt=uz%Xh3u!rai9I)92?gvHKT1i;1+Y^CaqgbQ?D6vIf<^C48!eH_h6 zD=EpxoH6wD%*P^_ql55A)fS)~wa2Y&^Y$>B$?ZMi=zcb`rm-Ra$&~cu!HQ?gZF-&(4`6d+L5@WAlV4hUjEY zv9lG6$R61#v3O&5%_<3n^ejk?klu7EEB1wg99zp1-_a7+Jc%WAI|`JmR(Ol@bhH3H zZ>&hX2^<@Q+sa;&JBtIJG|a_J7~BU z<|&Y1Y3ze+`N!XrfS`=dS`14)75JtJ1~s|-KGIvnDL)aU}is*k|edzmp zV&zknUu@t7gS8fdRolJxo%q{@il8m{qS(F(#jf(7%BE9Zcz|*=#AjZTz`9%`a$9AL zv#-UrfI~0&5@rdhh||Y92kj(JEeyAJjNy16oYEM>KE|6qgV|Qhd((C|1~&@I7JvEh zW4MDh(cv{9i;0p=s{fiz6_mco>3{pCbxPkPE-ofrNhYu7W+r92zYuSxCK(wk^Fdm< z-XkT!`CaXnQO{2eSDa!D*8OcNZ`Ha~(H^eeh_9arcYy2F{7&7m4)2WyZlwe)g{XZtJR5Txkk;UcF}kCgKTziE6;E2R`xWy=M;wT{R07q?&<&l literal 0 HcmV?d00001 diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java index a455a3b841..b271ea566c 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.extractor.ts; import android.test.InstrumentationTestCase; +import android.util.SparseArray; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorOutput; @@ -73,7 +74,7 @@ public final class TsExtractorTest extends InstrumentationTestCase { } public void testCustomPesReader() throws Exception { - CustomEsReaderFactory factory = new CustomEsReaderFactory(); + CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false); TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory, false); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) @@ -82,13 +83,12 @@ public final class TsExtractorTest extends InstrumentationTestCase { .setSimulatePartialReads(false).build(); FakeExtractorOutput output = new FakeExtractorOutput(); tsExtractor.init(output); - tsExtractor.seek(input.getPosition()); PositionHolder seekPositionHolder = new PositionHolder(); int readResult = Extractor.RESULT_CONTINUE; while (readResult != Extractor.RESULT_END_OF_INPUT) { readResult = tsExtractor.read(input, seekPositionHolder); } - CustomEsReader reader = factory.reader; + CustomEsReader reader = factory.esReader; assertEquals(2, reader.packetsRead); TrackOutput trackOutput = reader.getTrackOutput(); assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */)); @@ -97,6 +97,23 @@ public final class TsExtractorTest extends InstrumentationTestCase { ((FakeTrackOutput) trackOutput).format); } + public void testCustomInitialSectionReader() throws Exception { + CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(false, true); + TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory, false); + FakeExtractorInput input = new FakeExtractorInput.Builder() + .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample_with_sdt.ts")) + .setSimulateIOErrors(false) + .setSimulateUnknownLength(false) + .setSimulatePartialReads(false).build(); + tsExtractor.init(new FakeExtractorOutput()); + PositionHolder seekPositionHolder = new PositionHolder(); + int readResult = Extractor.RESULT_CONTINUE; + while (readResult != Extractor.RESULT_END_OF_INPUT) { + readResult = tsExtractor.read(input, seekPositionHolder); + } + assertEquals(1, factory.sdtReader.consumedSdts); + } + private static void writeJunkData(ByteArrayOutputStream out, int length) throws IOException { for (int i = 0; i < length; i++) { if (((byte) i) == TS_SYNC_BYTE) { @@ -107,6 +124,45 @@ public final class TsExtractorTest extends InstrumentationTestCase { } } + private static final class CustomTsPayloadReaderFactory implements TsPayloadReader.Factory { + + private final boolean provideSdtReader; + private final boolean provideCustomEsReader; + private final TsPayloadReader.Factory defaultFactory; + private CustomEsReader esReader; + private SdtSectionReader sdtReader; + + public CustomTsPayloadReaderFactory(boolean provideCustomEsReader, boolean provideSdtReader) { + this.provideCustomEsReader = provideCustomEsReader; + this.provideSdtReader = provideSdtReader; + defaultFactory = new DefaultTsPayloadReaderFactory(); + } + + @Override + public SparseArray createInitialPayloadReaders() { + if (provideSdtReader) { + assertNull(sdtReader); + SparseArray mapping = new SparseArray<>(); + sdtReader = new SdtSectionReader(); + mapping.put(17, new SectionReader(sdtReader)); + return mapping; + } else { + return defaultFactory.createInitialPayloadReaders(); + } + } + + @Override + public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) { + if (provideCustomEsReader && streamType == 3) { + esReader = new CustomEsReader(esInfo.language); + return new PesReader(esReader); + } else { + return defaultFactory.createPayloadReader(streamType, esInfo); + } + } + + } + private static final class CustomEsReader implements ElementaryStreamReader { private final String language; @@ -147,23 +203,38 @@ public final class TsExtractorTest extends InstrumentationTestCase { } - private static final class CustomEsReaderFactory implements TsPayloadReader.Factory { + private static final class SdtSectionReader implements SectionPayloadReader { - private final TsPayloadReader.Factory defaultFactory; - private CustomEsReader reader; - - public CustomEsReaderFactory() { - defaultFactory = new DefaultTsPayloadReaderFactory(); - } + private int consumedSdts; @Override - public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) { - if (streamType == 3) { - reader = new CustomEsReader(esInfo.language); - return new PesReader(reader); - } else { - return defaultFactory.createPayloadReader(streamType, esInfo); + public void consume(ParsableByteArray sectionData) { + // table_id(8), section_syntax_indicator(1), reserved_future_use(1), reserved(2), + // section_length(12), transport_stream_id(16), reserved(2), version_number(5), + // current_next_indicator(1), section_number(8), last_section_number(8), + // original_network_id(16), reserved_future_use(8) + sectionData.skipBytes(11); + // Start of the service loop. + assertEquals(0x5566 /* arbitrary service id */, sectionData.readUnsignedShort()); + // reserved_future_use(6), EIT_schedule_flag(1), EIT_present_following_flag(1) + sectionData.skipBytes(1); + // Assert there is only one service. + // Remove running_status(3), free_CA_mode(1) from the descriptors_loop_length with the mask. + assertEquals(sectionData.readUnsignedShort() & 0xFFF, sectionData.bytesLeft()); + while (sectionData.bytesLeft() > 0) { + int descriptorTag = sectionData.readUnsignedByte(); + int descriptorLength = sectionData.readUnsignedByte(); + if (descriptorTag == 72 /* service descriptor */) { + assertEquals(1, sectionData.readUnsignedByte()); // Service type: Digital TV. + int serviceProviderNameLength = sectionData.readUnsignedByte(); + assertEquals("Some provider", sectionData.readString(serviceProviderNameLength)); + int serviceNameLength = sectionData.readUnsignedByte(); + assertEquals("Some Channel", sectionData.readString(serviceNameLength)); + } else { + sectionData.skipBytes(descriptorLength); + } } + consumedSdts++; } } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java index 5aabc29a5d..96f7ff423f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.extractor.ts; import android.support.annotation.IntDef; +import android.util.SparseArray; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -49,6 +50,11 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact this.flags = flags; } + @Override + public SparseArray createInitialPayloadReaders() { + return new SparseArray<>(); + } + @Override public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) { switch (streamType) { diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index b52b5cc047..6913207529 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -253,6 +253,12 @@ public final class TsExtractor implements Extractor { private void resetPayloadReaders() { trackIds.clear(); tsPayloadReaders.clear(); + SparseArray initialPayloadReaders = + payloadReaderFactory.createInitialPayloadReaders(); + int initialPayloadReadersSize = initialPayloadReaders.size(); + for (int i = 0; i < initialPayloadReadersSize; i++) { + tsPayloadReaders.put(initialPayloadReaders.keyAt(i), initialPayloadReaders.valueAt(i)); + } tsPayloadReaders.put(TS_PAT_PID, new SectionReader(new PatReader())); id3Reader = null; } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java index 28e9fb9095..3916be39c9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.ts; +import android.util.SparseArray; import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TimestampAdjuster; import com.google.android.exoplayer2.extractor.TrackOutput; @@ -30,6 +31,15 @@ public interface TsPayloadReader { */ interface Factory { + /** + * Returns the initial mapping from PIDs to payload readers. + *

+ * This method allows the injection of payload readers for reserved PIDs, excluding PID 0. + * + * @return A {@link SparseArray} that maps PIDs to payload readers. + */ + SparseArray createInitialPayloadReaders(); + /** * Returns a {@link TsPayloadReader} for a given stream type and elementary stream information. * May return null if the stream type is not supported. From 0b8e9754ca7a8b2634aa3ebaa85681b51b540743 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 25 Oct 2016 09:00:19 -0700 Subject: [PATCH 4/5] Pass initialization parameters to section readers Unlike with PesReaders, sections don't have a standard way of providing timestmaps or even generating tracks. We need to pass this information so that readers decide what to do with it. Issue:#726 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=137162494 --- .../exoplayer2/extractor/ts/TsExtractorTest.java | 6 ++++++ .../extractor/ts/SectionPayloadReader.java | 15 +++++++++++++++ .../exoplayer2/extractor/ts/SectionReader.java | 3 +-- .../exoplayer2/extractor/ts/TsExtractor.java | 14 ++++++++++++-- .../exoplayer2/extractor/ts/TsPayloadReader.java | 7 ++++--- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java index b271ea566c..58893f15c1 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java @@ -207,6 +207,12 @@ public final class TsExtractorTest extends InstrumentationTestCase { private int consumedSdts; + @Override + public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, + TrackIdGenerator idGenerator) { + // Do nothing. + } + @Override public void consume(ParsableByteArray sectionData) { // table_id(8), section_syntax_indicator(1), reserved_future_use(1), reserved(2), diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionPayloadReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionPayloadReader.java index 9be41af594..2bddc56582 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionPayloadReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionPayloadReader.java @@ -15,6 +15,10 @@ */ package com.google.android.exoplayer2.extractor.ts; +import com.google.android.exoplayer2.extractor.ExtractorOutput; +import com.google.android.exoplayer2.extractor.TimestampAdjuster; +import com.google.android.exoplayer2.extractor.TrackOutput; +import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.util.ParsableByteArray; /** @@ -22,6 +26,17 @@ import com.google.android.exoplayer2.util.ParsableByteArray; */ public interface SectionPayloadReader { + /** + * Initializes the section payload reader. + * + * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. + * @param extractorOutput The {@link ExtractorOutput} that receives the extracted data. + * @param idGenerator A {@link PesReader.TrackIdGenerator} that generates unique track ids for the + * {@link TrackOutput}s. + */ + void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, + TrackIdGenerator idGenerator); + /** * Called by a {@link SectionReader} when a full section is received. * diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java index ccf00f8d19..9a181897ab 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java @@ -43,8 +43,7 @@ public final class SectionReader implements TsPayloadReader { @Override public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { - // TODO: Injectable section readers might want to generate metadata tracks. - // Do nothing. + reader.init(timestampAdjuster, extractorOutput, idGenerator); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index 6913207529..219101b8d3 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -65,8 +65,6 @@ public final class TsExtractor implements Extractor { public static final int TS_STREAM_TYPE_H265 = 0x24; public static final int TS_STREAM_TYPE_ID3 = 0x15; - private static final String TAG = "TsExtractor"; - private static final int TS_PACKET_SIZE = 188; private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet. private static final int TS_PAT_PID = 0; @@ -274,6 +272,12 @@ public final class TsExtractor implements Extractor { patScratch = new ParsableBitArray(new byte[4]); } + @Override + public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, + TrackIdGenerator idGenerator) { + // Do nothing. + } + @Override public void consume(ParsableByteArray sectionData) { // table_id(8), section_syntax_indicator(1), '0'(1), reserved(2), section_length(12), @@ -316,6 +320,12 @@ public final class TsExtractor implements Extractor { this.pid = pid; } + @Override + public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, + TrackIdGenerator idGenerator) { + // Do nothing. + } + @Override public void consume(ParsableByteArray sectionData) { // table_id(8), section_syntax_indicator(1), '0'(1), reserved(2), section_length(12), diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java index 3916be39c9..304c8c1282 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java @@ -99,9 +99,10 @@ public interface TsPayloadReader { /** * Initializes the payload reader. * - * @param timestampAdjuster - * @param extractorOutput - * @param idGenerator + * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. + * @param extractorOutput The {@link ExtractorOutput} that receives the extracted data. + * @param idGenerator A {@link PesReader.TrackIdGenerator} that generates unique track ids for the + * {@link TrackOutput}s. */ void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TrackIdGenerator idGenerator); From 2c54363204b04bfd58375f30eb6ccc214ef818c3 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 25 Oct 2016 11:46:55 -0700 Subject: [PATCH 5/5] Report track groups and selections through ExoPlayer TrackSelector no longer has a listener. Instead, tracks change events are reported through ExoPlayer.EventListener. Applications interested in retrieving the selection info should retrieve it directly from the TrackSelector by calling an exposed getter. Pretty sure the ref'd issue is fixed as a side effect of this change. Issue: #1942 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=137183073 --- .../android/exoplayer2/demo/EventLogger.java | 44 +++++---- .../exoplayer2/demo/PlayerActivity.java | 47 +++++----- .../exoplayer2/ext/flac/FlacPlaybackTest.java | 10 +- .../exoplayer2/ext/opus/OpusPlaybackTest.java | 10 +- .../exoplayer2/ext/vp9/VpxPlaybackTest.java | 10 +- .../exoplayer2/DefaultLoadControl.java | 4 +- .../google/android/exoplayer2/ExoPlayer.java | 43 ++++++++- .../android/exoplayer2/ExoPlayerFactory.java | 12 +-- .../android/exoplayer2/ExoPlayerImpl.java | 79 +++++++++++++--- .../exoplayer2/ExoPlayerImplInternal.java | 94 ++++++++++++------- .../android/exoplayer2/LoadControl.java | 4 +- .../android/exoplayer2/SimpleExoPlayer.java | 72 +++++--------- .../exoplayer2/source/TrackGroupArray.java | 5 + .../trackselection/DefaultTrackSelector.java | 14 +-- .../trackselection/MappingTrackSelector.java | 42 ++++++--- ...lections.java => TrackSelectionArray.java} | 12 +-- .../trackselection/TrackSelector.java | 92 +++--------------- .../exoplayer2/ui/DebugTextViewHelper.java | 7 ++ .../exoplayer2/ui/PlaybackControlView.java | 7 ++ .../exoplayer2/ui/SimpleExoPlayerView.java | 11 ++- .../playbacktests/gts/DashTest.java | 3 - .../playbacktests/util/ExoHostedTest.java | 9 +- 22 files changed, 355 insertions(+), 276 deletions(-) rename library/src/main/java/com/google/android/exoplayer2/trackselection/{TrackSelections.java => TrackSelectionArray.java} (85%) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java index d79de04657..f6554e71a8 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java @@ -38,10 +38,10 @@ import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelections; -import com.google.android.exoplayer2.trackselection.TrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.video.VideoRendererEventListener; import java.io.IOException; @@ -55,7 +55,7 @@ import java.util.Locale; /* package */ final class EventLogger implements ExoPlayer.EventListener, AudioRendererEventListener, VideoRendererEventListener, AdaptiveMediaSourceEventListener, ExtractorMediaSource.EventListener, StreamingDrmSessionManager.EventListener, - TrackSelector.EventListener, MetadataRenderer.Output> { + MetadataRenderer.Output> { private static final String TAG = "EventLogger"; private static final int MAX_TIMELINE_ITEM_LINES = 3; @@ -67,11 +67,13 @@ import java.util.Locale; TIME_FORMAT.setGroupingUsed(false); } + private final MappingTrackSelector trackSelector; private final Timeline.Window window; private final Timeline.Period period; private final long startTimeMs; - public EventLogger() { + public EventLogger(MappingTrackSelector trackSelector) { + this.trackSelector = trackSelector; window = new Timeline.Window(); period = new Timeline.Period(); startTimeMs = SystemClock.elapsedRealtime(); @@ -126,27 +128,29 @@ import java.util.Locale; Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e); } - // MappingTrackSelector.EventListener - @Override - public void onTrackSelectionsChanged(TrackSelections trackSelections) { + public void onTracksChanged(TrackGroupArray ignored, TrackSelectionArray trackSelections) { + MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo(); + if (mappedTrackInfo == null) { + Log.d(TAG, "Tracks []"); + return; + } Log.d(TAG, "Tracks ["); // Log tracks associated to renderers. - MappedTrackInfo info = trackSelections.info; - for (int rendererIndex = 0; rendererIndex < trackSelections.length; rendererIndex++) { - TrackGroupArray trackGroups = info.getTrackGroups(rendererIndex); + for (int rendererIndex = 0; rendererIndex < mappedTrackInfo.length; rendererIndex++) { + TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); TrackSelection trackSelection = trackSelections.get(rendererIndex); - if (trackGroups.length > 0) { + if (rendererTrackGroups.length > 0) { Log.d(TAG, " Renderer:" + rendererIndex + " ["); - for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { - TrackGroup trackGroup = trackGroups.get(groupIndex); - String adaptiveSupport = getAdaptiveSupportString( - trackGroup.length, info.getAdaptiveSupport(rendererIndex, groupIndex, false)); + for (int groupIndex = 0; groupIndex < rendererTrackGroups.length; groupIndex++) { + TrackGroup trackGroup = rendererTrackGroups.get(groupIndex); + String adaptiveSupport = getAdaptiveSupportString(trackGroup.length, + mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false)); Log.d(TAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " ["); for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { String status = getTrackStatusString(trackSelection, trackGroup, trackIndex); String formatSupport = getFormatSupportString( - info.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex)); + mappedTrackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex)); Log.d(TAG, " " + status + " Track:" + trackIndex + ", " + getFormatString(trackGroup.getFormat(trackIndex)) + ", supported=" + formatSupport); @@ -157,12 +161,12 @@ import java.util.Locale; } } // Log tracks not associated with a renderer. - TrackGroupArray trackGroups = info.getUnassociatedTrackGroups(); - if (trackGroups.length > 0) { + TrackGroupArray unassociatedTrackGroups = mappedTrackInfo.getUnassociatedTrackGroups(); + if (unassociatedTrackGroups.length > 0) { Log.d(TAG, " Renderer:None ["); - for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { + for (int groupIndex = 0; groupIndex < unassociatedTrackGroups.length; groupIndex++) { Log.d(TAG, " Group:" + groupIndex + " ["); - TrackGroup trackGroup = trackGroups.get(groupIndex); + TrackGroup trackGroup = unassociatedTrackGroups.get(groupIndex); for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { String status = getTrackStatusString(false); String formatSupport = getFormatSupportString( diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index e9aa46f85f..5351890d6f 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -58,8 +58,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelections; -import com.google.android.exoplayer2.trackselection.TrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.DebugTextViewHelper; import com.google.android.exoplayer2.ui.PlaybackControlView; import com.google.android.exoplayer2.ui.SimpleExoPlayerView; @@ -78,7 +77,7 @@ import java.util.UUID; * An activity that plays media using {@link SimpleExoPlayer}. */ public class PlayerActivity extends Activity implements OnClickListener, ExoPlayer.EventListener, - TrackSelector.EventListener, PlaybackControlView.VisibilityListener { + PlaybackControlView.VisibilityListener { public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid"; public static final String DRM_LICENSE_URL = "drm_license_url"; @@ -203,8 +202,11 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay if (view == retryButton) { initializePlayer(); } else if (view.getParent() == debugRootView) { - trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(), - trackSelector.getCurrentSelections().info, (int) view.getTag()); + MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo(); + if (mappedTrackInfo != null) { + trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(), + trackSelector.getCurrentMappedTrackInfo(), (int) view.getTag()); + } } } @@ -249,20 +251,20 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay } } - eventLogger = new EventLogger(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(BANDWIDTH_METER); - trackSelector = new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory); - trackSelector.addListener(this); - trackSelector.addListener(eventLogger); + trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory); player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(), drmSessionManager, preferExtensionDecoders); player.addListener(this); + + eventLogger = new EventLogger(trackSelector); player.addListener(eventLogger); player.setAudioDebugListener(eventLogger); player.setVideoDebugListener(eventLogger); player.setId3Output(eventLogger); + simpleExoPlayerView.setPlayer(player); if (isTimelineStatic) { if (playerPosition == C.TIME_UNSET) { @@ -447,17 +449,17 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay showControls(); } - // MappingTrackSelector.EventListener implementation - @Override - public void onTrackSelectionsChanged(TrackSelections trackSelections) { + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { updateButtonVisibilities(); - MappedTrackInfo trackInfo = trackSelections.info; - if (trackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_VIDEO)) { - showToast(R.string.error_unsupported_video); - } - if (trackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_AUDIO)) { - showToast(R.string.error_unsupported_audio); + MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo(); + if (mappedTrackInfo != null) { + if (mappedTrackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_VIDEO)) { + showToast(R.string.error_unsupported_video); + } + if (mappedTrackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_AUDIO)) { + showToast(R.string.error_unsupported_audio); + } } } @@ -473,14 +475,13 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay return; } - TrackSelections trackSelections = trackSelector.getCurrentSelections(); - if (trackSelections == null) { + MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo(); + if (mappedTrackInfo == null) { return; } - int rendererCount = trackSelections.length; - for (int i = 0; i < rendererCount; i++) { - TrackGroupArray trackGroups = trackSelections.info.getTrackGroups(i); + for (int i = 0; i < mappedTrackInfo.length; i++) { + TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(i); if (trackGroups.length != 0) { Button button = new Button(this); int label; diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java index 29a22f380a..990c470a93 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.ext.flac; import android.content.Context; import android.net.Uri; -import android.os.Handler; import android.os.Looper; import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.ExoPlaybackException; @@ -27,7 +26,9 @@ import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; /** @@ -72,7 +73,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase { public void run() { Looper.prepare(); LibflacAudioRenderer audioRenderer = new LibflacAudioRenderer(); - DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); + DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( @@ -91,6 +92,11 @@ public class FlacPlaybackTest extends InstrumentationTestCase { // Do nothing. } + @Override + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + // Do nothing. + } + @Override public void onPositionDiscontinuity() { // Do nothing. diff --git a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java index 4f723698a4..3e07186995 100644 --- a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java +++ b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.ext.opus; import android.content.Context; import android.net.Uri; -import android.os.Handler; import android.os.Looper; import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.ExoPlaybackException; @@ -27,7 +26,9 @@ import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; /** @@ -72,7 +73,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase { public void run() { Looper.prepare(); LibopusAudioRenderer audioRenderer = new LibopusAudioRenderer(); - DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); + DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( @@ -91,6 +92,11 @@ public class OpusPlaybackTest extends InstrumentationTestCase { // Do nothing. } + @Override + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + // Do nothing. + } + @Override public void onPositionDiscontinuity() { // Do nothing. diff --git a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java index c5f61cf231..b1ddf2368c 100644 --- a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java +++ b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.ext.vp9; import android.content.Context; import android.net.Uri; -import android.os.Handler; import android.os.Looper; import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.ExoPlaybackException; @@ -27,7 +26,9 @@ import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; /** @@ -88,7 +89,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase { public void run() { Looper.prepare(); LibvpxVideoRenderer videoRenderer = new LibvpxVideoRenderer(true, 0); - DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); + DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( @@ -110,6 +111,11 @@ public class VpxPlaybackTest extends InstrumentationTestCase { // Do nothing. } + @Override + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + // Do nothing. + } + @Override public void onPositionDiscontinuity() { // Do nothing. diff --git a/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java b/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java index e7a0f8b1b8..e6a39d8a27 100644 --- a/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java +++ b/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java @@ -16,7 +16,7 @@ package com.google.android.exoplayer2; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.TrackSelections; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.util.Util; @@ -111,7 +111,7 @@ public final class DefaultLoadControl implements LoadControl { @Override public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, - TrackSelections trackSelections) { + TrackSelectionArray trackSelections) { targetBufferSize = 0; for (int i = 0; i < renderers.length; i++) { if (trackSelections.get(i) != null) { diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index e3c9b6e114..31efdd82b1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -22,11 +22,13 @@ import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.SingleSampleMediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; @@ -110,6 +112,15 @@ public interface ExoPlayer { */ interface EventListener { + /** + * Called when the available or selected tracks change. + * + * @param trackGroups The available tracks. Never null, but may be of length zero. + * @param trackSelections The track selections for each {@link Renderer}. Never null and always + * of length {@link #getRendererCount()}, but may contain null elements. + */ + void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections); + /** * Called when the player starts or stops loading the source. * @@ -259,11 +270,11 @@ public interface ExoPlayer { * @param resetPosition Whether the playback position should be reset to the default position in * the first {@link Timeline.Window}. If false, playback will start from the position defined * by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}. - * @param resetTimeline Whether the timeline and manifest should be reset. Should be true unless - * the player is being prepared to play the same media as it was playing previously (e.g. if - * playback failed and is being retried). + * @param resetState Whether the timeline, manifest, tracks and track selections should be reset. + * Should be true unless the player is being prepared to play the same media as it was playing + * previously (e.g. if playback failed and is being retried). */ - void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetTimeline); + void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState); /** * Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}. @@ -356,6 +367,30 @@ public interface ExoPlayer { */ void blockingSendMessages(ExoPlayerMessage... messages); + /** + * Returns the number of renderers. + */ + int getRendererCount(); + + /** + * Returns the track type that the renderer at a given index handles. + * + * @see Renderer#getTrackType() + * @param index The index of the renderer. + * @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}. + */ + int getRendererType(int index); + + /** + * Returns the available track groups. + */ + TrackGroupArray getCurrentTrackGroups(); + + /** + * Returns the current track selections for each renderer. + */ + TrackSelectionArray getCurrentTrackSelections(); + /** * Returns the current manifest. The type depends on the {@link MediaSource} passed to * {@link #prepare}. diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java index 91ab56805a..e43a9c0357 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java @@ -42,7 +42,7 @@ public final class ExoPlayerFactory { * @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param loadControl The {@link LoadControl} that will be used by the instance. */ - public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, + public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, LoadControl loadControl) { return newSimpleInstance(context, trackSelector, loadControl, null); } @@ -57,7 +57,7 @@ public final class ExoPlayerFactory { * @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance * will not be used for DRM protected playbacks. */ - public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, + public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, LoadControl loadControl, DrmSessionManager drmSessionManager) { return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager, false); } @@ -75,7 +75,7 @@ public final class ExoPlayerFactory { * available extensions over those defined in the core library. Note that extensions must be * included in the application build for setting this flag to have any effect. */ - public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, + public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, LoadControl loadControl, DrmSessionManager drmSessionManager, boolean preferExtensionDecoders) { return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager, @@ -97,7 +97,7 @@ public final class ExoPlayerFactory { * @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to * seamlessly join an ongoing playback. */ - public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, + public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, LoadControl loadControl, DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) { return new SimpleExoPlayer(context, trackSelector, loadControl, drmSessionManager, @@ -111,7 +111,7 @@ public final class ExoPlayerFactory { * @param renderers The {@link Renderer}s that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance. */ - public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector) { + public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector) { return newInstance(renderers, trackSelector, new DefaultLoadControl()); } @@ -123,7 +123,7 @@ public final class ExoPlayerFactory { * @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param loadControl The {@link LoadControl} that will be used by the instance. */ - public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector, + public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl) { return new ExoPlayerImpl(renderers, trackSelector, loadControl); } diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 3eb2ceb38b..2e0502c019 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -22,7 +22,11 @@ import android.os.Message; import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo; +import com.google.android.exoplayer2.ExoPlayerImplInternal.TrackInfo; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.util.Assertions; import java.util.concurrent.CopyOnWriteArraySet; @@ -34,12 +38,16 @@ import java.util.concurrent.CopyOnWriteArraySet; private static final String TAG = "ExoPlayerImpl"; + private final Renderer[] renderers; + private final TrackSelector trackSelector; + private final TrackSelectionArray emptyTrackSelections; private final Handler eventHandler; - private final ExoPlayerImplInternal internalPlayer; + private final ExoPlayerImplInternal internalPlayer; private final CopyOnWriteArraySet listeners; private final Timeline.Window window; private final Timeline.Period period; + private boolean tracksSelected; private boolean pendingInitialSeek; private boolean playWhenReady; private int playbackState; @@ -47,6 +55,8 @@ import java.util.concurrent.CopyOnWriteArraySet; private boolean isLoading; private Timeline timeline; private Object manifest; + private TrackGroupArray trackGroups; + private TrackSelectionArray trackSelections; // Playback information when there is no pending seek/set source operation. private PlaybackInfo playbackInfo; @@ -63,16 +73,19 @@ import java.util.concurrent.CopyOnWriteArraySet; * @param loadControl The {@link LoadControl} that will be used by the instance. */ @SuppressLint("HandlerLeak") - public ExoPlayerImpl(Renderer[] renderers, TrackSelector trackSelector, - LoadControl loadControl) { + public ExoPlayerImpl(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl) { Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION); - Assertions.checkNotNull(renderers); Assertions.checkState(renderers.length > 0); + this.renderers = Assertions.checkNotNull(renderers); + this.trackSelector = Assertions.checkNotNull(trackSelector); this.playWhenReady = false; this.playbackState = STATE_IDLE; this.listeners = new CopyOnWriteArraySet<>(); + emptyTrackSelections = new TrackSelectionArray(new TrackSelection[renderers.length]); window = new Timeline.Window(); period = new Timeline.Period(); + trackGroups = TrackGroupArray.EMPTY; + trackSelections = emptyTrackSelections; eventHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -80,8 +93,8 @@ import java.util.concurrent.CopyOnWriteArraySet; } }; playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0, 0); - internalPlayer = new ExoPlayerImplInternal<>(renderers, trackSelector, loadControl, - playWhenReady, eventHandler, playbackInfo); + internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, loadControl, playWhenReady, + eventHandler, playbackInfo); } @Override @@ -105,12 +118,23 @@ import java.util.concurrent.CopyOnWriteArraySet; } @Override - public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetTimeline) { - if (resetTimeline && (timeline != null || manifest != null)) { - timeline = null; - manifest = null; - for (EventListener listener : listeners) { - listener.onTimelineChanged(null, null); + public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { + if (resetState) { + if (timeline != null || manifest != null) { + timeline = null; + manifest = null; + for (EventListener listener : listeners) { + listener.onTimelineChanged(null, null); + } + } + if (tracksSelected) { + tracksSelected = false; + trackGroups = TrackGroupArray.EMPTY; + trackSelections = emptyTrackSelections; + trackSelector.onSelectionActivated(null); + for (EventListener listener : listeners) { + listener.onTracksChanged(trackGroups, trackSelections); + } } } internalPlayer.prepare(mediaSource, resetPosition); @@ -266,6 +290,26 @@ import java.util.concurrent.CopyOnWriteArraySet; : (int) (duration == 0 ? 100 : (bufferedPosition * 100) / duration); } + @Override + public int getRendererCount() { + return renderers.length; + } + + @Override + public int getRendererType(int index) { + return renderers[index].getTrackType(); + } + + @Override + public TrackGroupArray getCurrentTrackGroups() { + return trackGroups; + } + + @Override + public TrackSelectionArray getCurrentTrackSelections() { + return trackSelections; + } + @Override public Timeline getCurrentTimeline() { return timeline; @@ -293,6 +337,17 @@ import java.util.concurrent.CopyOnWriteArraySet; } break; } + case ExoPlayerImplInternal.MSG_TRACKS_CHANGED: { + TrackInfo trackInfo = (TrackInfo) msg.obj; + tracksSelected = true; + trackGroups = trackInfo.groups; + trackSelections = trackInfo.selections; + trackSelector.onSelectionActivated(trackInfo.info); + for (EventListener listener : listeners) { + listener.onTracksChanged(trackGroups, trackSelections); + } + break; + } case ExoPlayerImplInternal.MSG_SEEK_ACK: { if (--pendingSeekAcks == 0) { playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj; diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 9d6c435635..6d8f3af0c3 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -26,8 +26,9 @@ import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SampleStream; +import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelections; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MediaClock; @@ -40,7 +41,7 @@ import java.io.IOException; /** * Implements the internal behavior of {@link ExoPlayerImpl}. */ -/* package */ final class ExoPlayerImplInternal implements Handler.Callback, +/* package */ final class ExoPlayerImplInternal implements Handler.Callback, MediaPeriod.Callback, TrackSelector.InvalidationListener, MediaSource.Listener { /** @@ -64,15 +65,30 @@ import java.io.IOException; } + public static final class TrackInfo { + + public final TrackGroupArray groups; + public final TrackSelectionArray selections; + public final Object info; + + public TrackInfo(TrackGroupArray groups, TrackSelectionArray selections, Object info) { + this.groups = groups; + this.selections = selections; + this.info = info; + } + + } + private static final String TAG = "ExoPlayerImplInternal"; // External messages public static final int MSG_STATE_CHANGED = 1; public static final int MSG_LOADING_CHANGED = 2; - public static final int MSG_SEEK_ACK = 3; - public static final int MSG_POSITION_DISCONTINUITY = 4; - public static final int MSG_SOURCE_INFO_REFRESHED = 5; - public static final int MSG_ERROR = 6; + public static final int MSG_TRACKS_CHANGED = 3; + public static final int MSG_SEEK_ACK = 4; + public static final int MSG_POSITION_DISCONTINUITY = 5; + public static final int MSG_SOURCE_INFO_REFRESHED = 6; + public static final int MSG_ERROR = 7; // Internal messages private static final int MSG_PREPARE = 0; @@ -100,7 +116,7 @@ import java.io.IOException; private final Renderer[] renderers; private final RendererCapabilities[] rendererCapabilities; - private final TrackSelector trackSelector; + private final TrackSelector trackSelector; private final LoadControl loadControl; private final StandaloneMediaClock standaloneMediaClock; private final Handler handler; @@ -128,13 +144,13 @@ import java.io.IOException; private boolean isTimelineReady; private boolean isTimelineEnded; private int bufferAheadPeriodCount; - private MediaPeriodHolder playingPeriodHolder; - private MediaPeriodHolder readingPeriodHolder; - private MediaPeriodHolder loadingPeriodHolder; + private MediaPeriodHolder playingPeriodHolder; + private MediaPeriodHolder readingPeriodHolder; + private MediaPeriodHolder loadingPeriodHolder; private Timeline timeline; - public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector, + public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, boolean playWhenReady, Handler eventHandler, PlaybackInfo playbackInfo) { this.renderers = renderers; @@ -538,7 +554,7 @@ import java.io.IOException; periodIndex = C.INDEX_UNSET; } - MediaPeriodHolder newPlayingPeriodHolder = null; + MediaPeriodHolder newPlayingPeriodHolder = null; if (playingPeriodHolder == null) { // We're still waiting for the first period to be prepared. if (loadingPeriodHolder != null) { @@ -546,7 +562,7 @@ import java.io.IOException; } } else { // Clear the timeline, but keep the requested period if it is already prepared. - MediaPeriodHolder periodHolder = playingPeriodHolder; + MediaPeriodHolder periodHolder = playingPeriodHolder; while (periodHolder != null) { if (periodHolder.index == periodIndex && periodHolder.prepared) { newPlayingPeriodHolder = periodHolder; @@ -680,7 +696,7 @@ import java.io.IOException; return; } // Reselect tracks on each period in turn, until the selection changes. - MediaPeriodHolder periodHolder = playingPeriodHolder; + MediaPeriodHolder periodHolder = playingPeriodHolder; boolean selectionsChangedForReadPeriod = true; while (true) { if (periodHolder == null || !periodHolder.prepared) { @@ -745,7 +761,7 @@ import java.io.IOException; } } } - trackSelector.onSelectionActivated(playingPeriodHolder.trackSelections); + eventHandler.obtainMessage(MSG_TRACKS_CHANGED, periodHolder.getTrackInfo()).sendToTarget(); enableRenderers(rendererWasEnabledFlags, enabledRendererCount); } else { // Release and re-prepare/buffer periods after the one whose selection changed. @@ -817,11 +833,11 @@ import java.io.IOException; playingPeriodHolder.setIndex(timeline, timeline.getWindow(period.windowIndex, window), index); - MediaPeriodHolder previousPeriodHolder = playingPeriodHolder; + MediaPeriodHolder previousPeriodHolder = playingPeriodHolder; boolean seenReadingPeriod = false; bufferAheadPeriodCount = 0; while (previousPeriodHolder.next != null) { - MediaPeriodHolder periodHolder = previousPeriodHolder.next; + MediaPeriodHolder periodHolder = previousPeriodHolder.next; index++; timeline.getPeriod(index, period, true); if (!periodHolder.uid.equals(period.uid)) { @@ -962,9 +978,8 @@ import java.io.IOException; MediaPeriod newMediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex, loadControl.getAllocator(), periodStartPositionUs); newMediaPeriod.prepare(this); - MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder<>(renderers, - rendererCapabilities, trackSelector, mediaSource, newMediaPeriod, newPeriodUid, - periodStartPositionUs); + MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities, + trackSelector, mediaSource, newMediaPeriod, newPeriodUid, periodStartPositionUs); timeline.getWindow(windowIndex, window); newPeriodHolder.setIndex(timeline, window, newLoadingPeriodIndex); if (loadingPeriodHolder != null) { @@ -1018,9 +1033,9 @@ import java.io.IOException; } } if (readingPeriodHolder.next != null && readingPeriodHolder.next.prepared) { - TrackSelections oldTrackSelections = readingPeriodHolder.trackSelections; + TrackSelectionArray oldTrackSelections = readingPeriodHolder.trackSelections; readingPeriodHolder = readingPeriodHolder.next; - TrackSelections newTrackSelections = readingPeriodHolder.trackSelections; + TrackSelectionArray newTrackSelections = readingPeriodHolder.trackSelections; for (int i = 0; i < renderers.length; i++) { Renderer renderer = renderers[i]; TrackSelection oldSelection = oldTrackSelections.get(i); @@ -1094,14 +1109,14 @@ import java.io.IOException; } } - private void releasePeriodHoldersFrom(MediaPeriodHolder periodHolder) { + private void releasePeriodHoldersFrom(MediaPeriodHolder periodHolder) { while (periodHolder != null) { periodHolder.release(); periodHolder = periodHolder.next; } } - private void setPlayingPeriodHolder(MediaPeriodHolder periodHolder) + private void setPlayingPeriodHolder(MediaPeriodHolder periodHolder) throws ExoPlaybackException { int enabledRendererCount = 0; boolean[] rendererWasEnabledFlags = new boolean[renderers.length]; @@ -1125,7 +1140,7 @@ import java.io.IOException; } } - trackSelector.onSelectionActivated(periodHolder.trackSelections); + eventHandler.obtainMessage(MSG_TRACKS_CHANGED, periodHolder.getTrackInfo()).sendToTarget(); playingPeriodHolder = periodHolder; enableRenderers(rendererWasEnabledFlags, enabledRendererCount); } @@ -1182,7 +1197,7 @@ import java.io.IOException; /** * Holds a {@link MediaPeriod} with information required to play it as part of a timeline. */ - private static final class MediaPeriodHolder { + private static final class MediaPeriodHolder { public final MediaPeriod mediaPeriod; public final Object uid; @@ -1196,19 +1211,21 @@ import java.io.IOException; public boolean prepared; public boolean hasEnabledTracks; public long rendererPositionOffsetUs; - public MediaPeriodHolder next; + public MediaPeriodHolder next; public boolean needsContinueLoading; private final Renderer[] renderers; private final RendererCapabilities[] rendererCapabilities; - private final TrackSelector trackSelector; + private final TrackSelector trackSelector; private final MediaSource mediaSource; - private TrackSelections trackSelections; - private TrackSelections periodTrackSelections; + private Object trackSelectionsInfo; + private TrackGroupArray trackGroups; + private TrackSelectionArray trackSelections; + private TrackSelectionArray periodTrackSelections; public MediaPeriodHolder(Renderer[] renderers, RendererCapabilities[] rendererCapabilities, - TrackSelector trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod, + TrackSelector trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod, Object uid, long positionUs) { this.renderers = renderers; this.rendererCapabilities = rendererCapabilities; @@ -1221,7 +1238,7 @@ import java.io.IOException; startPositionUs = positionUs; } - public void setNext(MediaPeriodHolder next) { + public void setNext(MediaPeriodHolder next) { this.next = next; } @@ -1238,17 +1255,20 @@ import java.io.IOException; public void handlePrepared(long positionUs, LoadControl loadControl) throws ExoPlaybackException { prepared = true; + trackGroups = mediaPeriod.getTrackGroups(); selectTracks(); startPositionUs = updatePeriodTrackSelection(positionUs, loadControl, false); } public boolean selectTracks() throws ExoPlaybackException { - TrackSelections newTrackSelections = trackSelector.selectTracks(rendererCapabilities, - mediaPeriod.getTrackGroups()); + Pair selectorResult = trackSelector.selectTracks( + rendererCapabilities, trackGroups); + TrackSelectionArray newTrackSelections = selectorResult.first; if (newTrackSelections.equals(periodTrackSelections)) { return false; } trackSelections = newTrackSelections; + trackSelectionsInfo = selectorResult.second; return true; } @@ -1283,10 +1303,14 @@ import java.io.IOException; } // The track selection has changed. - loadControl.onTracksSelected(renderers, mediaPeriod.getTrackGroups(), trackSelections); + loadControl.onTracksSelected(renderers, trackGroups, trackSelections); return positionUs; } + public TrackInfo getTrackInfo() { + return new TrackInfo(trackGroups, trackSelections, trackSelectionsInfo); + } + public void release() { try { mediaSource.releasePeriod(mediaPeriod); diff --git a/library/src/main/java/com/google/android/exoplayer2/LoadControl.java b/library/src/main/java/com/google/android/exoplayer2/LoadControl.java index 6176c6085b..c092480222 100644 --- a/library/src/main/java/com/google/android/exoplayer2/LoadControl.java +++ b/library/src/main/java/com/google/android/exoplayer2/LoadControl.java @@ -17,7 +17,7 @@ package com.google.android.exoplayer2; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.TrackSelections; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.Allocator; /** @@ -38,7 +38,7 @@ public interface LoadControl { * @param trackSelections The track selections that were made. */ void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, - TrackSelections trackSelections); + TrackSelectionArray trackSelections); /** * Called by the player when stopped. diff --git a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 4b673d3750..d9c405cad6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -39,9 +39,10 @@ import com.google.android.exoplayer2.metadata.MetadataRenderer; import com.google.android.exoplayer2.metadata.id3.Id3Decoder; import com.google.android.exoplayer2.metadata.id3.Id3Frame; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.TextRenderer; -import com.google.android.exoplayer2.trackselection.TrackSelections; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.VideoRendererEventListener; @@ -86,11 +87,6 @@ public final class SimpleExoPlayer implements ExoPlayer { */ void onRenderedFirstFrame(); - /** - * Called when a video track is no longer selected. - */ - void onVideoTracksDisabled(); - } private static final String TAG = "SimpleExoPlayer"; @@ -103,7 +99,6 @@ public final class SimpleExoPlayer implements ExoPlayer { private final int videoRendererCount; private final int audioRendererCount; - private boolean videoTracksEnabled; private Format videoFormat; private Format audioFormat; @@ -122,12 +117,11 @@ public final class SimpleExoPlayer implements ExoPlayer { private float volume; private PlaybackParamsHolder playbackParamsHolder; - /* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector, + /* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector, LoadControl loadControl, DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) { mainHandler = new Handler(); componentListener = new ComponentListener(); - trackSelector.addListener(componentListener); // Build the renderers. ArrayList renderersList = new ArrayList<>(); @@ -164,26 +158,6 @@ public final class SimpleExoPlayer implements ExoPlayer { player = new ExoPlayerImpl(renderers, trackSelector, loadControl); } - /** - * Returns the number of renderers. - * - * @return The number of renderers. - */ - public int getRendererCount() { - return renderers.length; - } - - /** - * Returns the track type that the renderer at a given index handles. - * - * @see Renderer#getTrackType() - * @param index The index of the renderer. - * @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}. - */ - public int getRendererType(int index) { - return renderers[index].getTrackType(); - } - /** * Clears any {@link Surface}, {@link SurfaceHolder}, {@link SurfaceView} or {@link TextureView} * currently set on the player. @@ -517,6 +491,26 @@ public final class SimpleExoPlayer implements ExoPlayer { return player.getBufferedPercentage(); } + @Override + public int getRendererCount() { + return player.getRendererCount(); + } + + @Override + public int getRendererType(int index) { + return player.getRendererType(index); + } + + @Override + public TrackGroupArray getCurrentTrackGroups() { + return player.getCurrentTrackGroups(); + } + + @Override + public TrackSelectionArray getCurrentTrackSelections() { + return player.getCurrentTrackSelections(); + } + @Override public Timeline getCurrentTimeline() { return player.getCurrentTimeline(); @@ -651,8 +645,7 @@ public final class SimpleExoPlayer implements ExoPlayer { private final class ComponentListener implements VideoRendererEventListener, AudioRendererEventListener, TextRenderer.Output, MetadataRenderer.Output>, - SurfaceHolder.Callback, TextureView.SurfaceTextureListener, - TrackSelector.EventListener { + SurfaceHolder.Callback, TextureView.SurfaceTextureListener { // VideoRendererEventListener implementation @@ -831,23 +824,6 @@ public final class SimpleExoPlayer implements ExoPlayer { // Do nothing. } - // TrackSelector.EventListener implementation - - @Override - public void onTrackSelectionsChanged(TrackSelections trackSelections) { - boolean videoTracksEnabled = false; - for (int i = 0; i < renderers.length; i++) { - if (renderers[i].getTrackType() == C.TRACK_TYPE_VIDEO && trackSelections.get(i) != null) { - videoTracksEnabled = true; - break; - } - } - if (videoListener != null && SimpleExoPlayer.this.videoTracksEnabled && !videoTracksEnabled) { - videoListener.onVideoTracksDisabled(); - } - SimpleExoPlayer.this.videoTracksEnabled = videoTracksEnabled; - } - } @TargetApi(23) diff --git a/library/src/main/java/com/google/android/exoplayer2/source/TrackGroupArray.java b/library/src/main/java/com/google/android/exoplayer2/source/TrackGroupArray.java index d562ec43e1..394cec891b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/TrackGroupArray.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/TrackGroupArray.java @@ -23,6 +23,11 @@ import java.util.Arrays; */ public final class TrackGroupArray { + /** + * The empty array. + */ + public static final TrackGroupArray EMPTY = new TrackGroupArray(); + /** * The number of groups in the array. Greater than or equal to zero. */ diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 81d79ac055..02c2defdfc 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.trackselection; import android.content.Context; import android.graphics.Point; -import android.os.Handler; import android.text.TextUtils; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; @@ -326,25 +325,18 @@ public class DefaultTrackSelector extends MappingTrackSelector { /** * Constructs an instance that does not support adaptive video. - * - * @param eventHandler A handler to use when delivering events to listeners. May be null if - * listeners will not be added. */ - public DefaultTrackSelector(Handler eventHandler) { - this(eventHandler, null); + public DefaultTrackSelector() { + this(null); } /** * Constructs an instance that uses a factory to create adaptive video track selections. * - * @param eventHandler A handler to use when delivering events to listeners. May be null if - * listeners will not be added. * @param adaptiveVideoTrackSelectionFactory A factory for adaptive video {@link TrackSelection}s, * or null if the selector should not support adaptive video. */ - public DefaultTrackSelector(Handler eventHandler, - TrackSelection.Factory adaptiveVideoTrackSelectionFactory) { - super(eventHandler); + public DefaultTrackSelector(TrackSelection.Factory adaptiveVideoTrackSelectionFactory) { this.adaptiveVideoTrackSelectionFactory = adaptiveVideoTrackSelectionFactory; params = new AtomicReference<>(new Parameters()); } diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java index 3307fc3baa..7454ff6801 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java @@ -15,14 +15,13 @@ */ package com.google.android.exoplayer2.trackselection; -import android.os.Handler; +import android.util.Pair; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.util.Util; import java.util.Arrays; import java.util.HashMap; @@ -32,7 +31,7 @@ import java.util.Map; * Base class for {@link TrackSelector}s that first establish a mapping between {@link TrackGroup}s * and renderers, and then from that mapping create a {@link TrackSelection} for each renderer. */ -public abstract class MappingTrackSelector extends TrackSelector { +public abstract class MappingTrackSelector extends TrackSelector { /** * A track selection override. @@ -83,16 +82,21 @@ public abstract class MappingTrackSelector extends TrackSelector> selectionOverrides; private final SparseBooleanArray rendererDisabledFlags; - /** - * @param eventHandler A handler to use when delivering events to listeners added via - * {@link #addListener(EventListener)}. - */ - public MappingTrackSelector(Handler eventHandler) { - super(eventHandler); + private MappedTrackInfo currentMappedTrackInfo; + + public MappingTrackSelector() { selectionOverrides = new SparseArray<>(); rendererDisabledFlags = new SparseBooleanArray(); } + /** + * Returns the mapping information associated with the current track selections, or null if no + * selection is currently active. + */ + public final MappedTrackInfo getCurrentMappedTrackInfo() { + return currentMappedTrackInfo; + } + /** * Sets whether the renderer at the specified index is disabled. * @@ -224,7 +228,7 @@ public abstract class MappingTrackSelector extends TrackSelector selectTracks( + public final Pair selectTracks( RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups) throws ExoPlaybackException { // Structures into which data will be written during the selection. The extra item at the end @@ -294,7 +298,13 @@ public abstract class MappingTrackSelector extends TrackSelector(mappedTrackInfo, trackSelections); + return Pair.create(new TrackSelectionArray(trackSelections), + mappedTrackInfo); + } + + @Override + public final void onSelectionActivated(Object info) { + currentMappedTrackInfo = (MappedTrackInfo) info; } /** @@ -409,12 +419,16 @@ public abstract class MappingTrackSelector extends TrackSelector { +public final class TrackSelectionArray { - /** - * Opaque information associated with the result. - */ - public final T info; /** * The number of selections in the result. Greater than or equal to zero. */ @@ -37,11 +33,9 @@ public final class TrackSelections { private int hashCode; /** - * @param info Opaque information associated with the result. * @param trackSelections The selections. Must not be null, but may contain null elements. */ - public TrackSelections(T info, TrackSelection... trackSelections) { - this.info = info; + public TrackSelectionArray(TrackSelection... trackSelections) { this.trackSelections = trackSelections; this.length = trackSelections.length; } @@ -81,7 +75,7 @@ public final class TrackSelections { if (obj == null || getClass() != obj.getClass()) { return false; } - TrackSelections other = (TrackSelections) obj; + TrackSelectionArray other = (TrackSelectionArray) obj; return Arrays.equals(trackSelections, other.trackSelections); } diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java index 9c859312cb..5a9d3923bf 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java @@ -15,15 +15,13 @@ */ package com.google.android.exoplayer2.trackselection; -import android.os.Handler; +import android.util.Pair; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.util.Assertions; -import java.util.concurrent.CopyOnWriteArraySet; /** Selects tracks to be consumed by available renderers. */ -public abstract class TrackSelector { +public abstract class TrackSelector { /** * Notified when previous selections by a {@link TrackSelector} are no longer valid. @@ -37,56 +35,7 @@ public abstract class TrackSelector { } - /** Listener of {@link TrackSelector} events. */ - public interface EventListener { - - /** - * Called when the track selections have changed. - * - * @param trackSelections The new track selections. - */ - void onTrackSelectionsChanged(TrackSelections trackSelections); - - } - - private final Handler eventHandler; - private final CopyOnWriteArraySet> listeners; - private InvalidationListener listener; - private TrackSelections activeSelections; - - /** - * @param eventHandler A handler to use when delivering events to listeners added via {@link - * #addListener(EventListener)}. - */ - public TrackSelector(Handler eventHandler) { - this.eventHandler = Assertions.checkNotNull(eventHandler); - this.listeners = new CopyOnWriteArraySet<>(); - } - - /** - * Registers a listener to receive events from the selector. The listener's methods will be called - * using the {@link Handler} that was passed to the constructor. - * - * @param listener The listener to register. - */ - public final void addListener(EventListener listener) { - listeners.add(listener); - } - - /** - * Unregister a listener. The listener will no longer receive events from the selector. - * - * @param listener The listener to unregister. - */ - public final void removeListener(EventListener listener) { - listeners.remove(listener); - } - - /** Returns the current track selections. */ - public final TrackSelections getCurrentSelections() { - return activeSelections; - } /** * Initializes the selector. @@ -98,28 +47,27 @@ public abstract class TrackSelector { } /** - * Generates {@link TrackSelections} for the renderers. + * Generates {@link TrackSelectionArray} for the renderers. * - * @param rendererCapabilities The {@link RendererCapabilities} of the renderers for which {@link - * TrackSelection}s are to be generated. + * @param rendererCapabilities The {@link RendererCapabilities} of the renderers for which + * {@link TrackSelection}s are to be generated. * @param trackGroups The available track groups. - * @return The track selections. + * @return The track selections, and an implementation specific object that will be returned to + * the selector via {@link #onSelectionActivated(Object)} should the selections be activated. * @throws ExoPlaybackException If an error occurs selecting tracks. */ - public abstract TrackSelections selectTracks( + public abstract Pair selectTracks( RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups) throws ExoPlaybackException; /** - * Called when {@link TrackSelections} previously generated by {@link - * #selectTracks(RendererCapabilities[], TrackGroupArray)} are activated. + * Called when {@link TrackSelectionArray} previously generated by + * {@link #selectTracks(RendererCapabilities[], TrackGroupArray)} are activated. * - * @param activeSelections The activated {@link TrackSelections}. + * @param info The information associated with the selections, or null if the selected tracks are + * being cleared. */ - public final void onSelectionActivated(TrackSelections activeSelections) { - this.activeSelections = activeSelections; - notifyTrackSelectionsChanged(activeSelections); - } + public abstract void onSelectionActivated(Object info); /** * Invalidates all previously generated track selections. @@ -130,18 +78,4 @@ public abstract class TrackSelector { } } - private void notifyTrackSelectionsChanged(final TrackSelections activeSelections) { - if (eventHandler != null) { - eventHandler.post( - new Runnable() { - @Override - public void run() { - for (EventListener listener : listeners) { - listener.onTrackSelectionsChanged(activeSelections); - } - } - }); - } - } - } diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java b/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java index af38836fc9..1bf5b59a4a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java @@ -22,6 +22,8 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.decoder.DecoderCounters; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; /** * A helper class for periodically updating a {@link TextView} with debug information obtained from @@ -98,6 +100,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe // Do nothing. } + @Override + public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) { + // Do nothing. + } + // Runnable implementation. @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index 470e173c02..49e1b6bafb 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -31,6 +31,8 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.R; import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.util.Util; import java.util.Formatter; import java.util.Locale; @@ -576,6 +578,11 @@ public class PlaybackControlView extends FrameLayout { // Do nothing. } + @Override + public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) { + // Do nothing. + } + @Override public void onPlayerError(ExoPlaybackException error) { // Do nothing. diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index 692ff70ce1..198e6870e8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -27,13 +27,16 @@ import android.view.TextureView; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.R; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.TextRenderer; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import java.util.List; /** @@ -325,7 +328,13 @@ public final class SimpleExoPlayerView extends FrameLayout { } @Override - public void onVideoTracksDisabled() { + public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) { + for (int i = 0; i < selections.length; i++) { + if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null) { + return; + } + } + // No enabled video renderers. Close the shutter. shutterView.setVisibility(VISIBLE); } diff --git a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java index 9e9a03a277..8aee627993 100644 --- a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java +++ b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java @@ -19,8 +19,6 @@ import android.annotation.TargetApi; import android.media.MediaDrm; import android.media.UnsupportedSchemeException; import android.net.Uri; -import android.os.Handler; -import android.os.Looper; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import com.google.android.exoplayer2.C; @@ -805,7 +803,6 @@ public final class DashTest extends ActivityInstrumentationTestCase2