private void setSelector(MappingTrackSelector selector) { this.selector = selector; setTrackInfo(selector.getCurrentMappedTrackInfo()); setTrackGroups(trackInfo.getTrackGroups(rendererIndex)); setOverride(selector.getSelectionOverride(rendererIndex, trackGroups)); trackGroupsAdaptive = new boolean[trackGroups.length]; for (int i = 0; i < trackGroups.length; i++) { trackGroupsAdaptive[i] = adaptiveTrackSelectionFactory != null && trackInfo.getAdaptiveSupport(rendererIndex, i, false) != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED && trackGroups.get(i).length > 1; } // isDisabled = selector.getRendererDisabled(rendererIndex); // override = selector.getSelectionOverride(rendererIndex, trackGroups); }
/** * Shows the selection dialog for a given renderer. * * @param activity The parent activity. * @param title The dialog's title. * @param trackInfo The current track information. * @param rendererIndex The index of the renderer. */ public void showSelectionDialog(Activity activity, CharSequence title, MappedTrackInfo trackInfo, int rendererIndex) { this.trackInfo = trackInfo; this.rendererIndex = rendererIndex; trackGroups = trackInfo.getTrackGroups(rendererIndex); trackGroupsAdaptive = new boolean[trackGroups.length]; for (int i = 0; i < trackGroups.length; i++) { trackGroupsAdaptive[i] = adaptiveVideoTrackSelectionFactory != null && trackInfo.getAdaptiveSupport(rendererIndex, i, false) != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED && trackGroups.get(i).length > 1; } isDisabled = selector.getRendererDisabled(rendererIndex); override = selector.getSelectionOverride(rendererIndex, trackGroups); AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(title) .setView(buildView(builder.getContext())) .setPositiveButton(android.R.string.ok, this) .setNegativeButton(android.R.string.cancel, null) .create() .show(); }
@Override protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities, TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports) throws ExoPlaybackException { Assertions.checkState(rendererCapabilities[VIDEO_RENDERER_INDEX].getTrackType() == C.TRACK_TYPE_VIDEO); Assertions.checkState(rendererCapabilities[AUDIO_RENDERER_INDEX].getTrackType() == C.TRACK_TYPE_AUDIO); Assertions.checkState(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].length == 1); Assertions.checkState(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].length == 1); TrackSelection[] selections = new TrackSelection[rendererCapabilities.length]; selections[VIDEO_RENDERER_INDEX] = new RandomTrackSelection( rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0), getVideoTrackIndices(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0), rendererFormatSupports[VIDEO_RENDERER_INDEX][0], videoFormatIds, canIncludeAdditionalVideoFormats), 0 /* seed */); selections[AUDIO_RENDERER_INDEX] = new FixedTrackSelection( rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0), getTrackIndex(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0), audioFormatId)); includedAdditionalVideoFormats = selections[VIDEO_RENDERER_INDEX].length() > videoFormatIds.length; return selections; }
protected TrackSelection selectVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveTrackSelectionFactory, boolean exceedConstraintsIfNecessary, boolean exceedRendererCapabilitiesIfNecessary) throws ExoPlaybackException { TrackSelection selection = null; if (adaptiveTrackSelectionFactory != null) { selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth, viewportHeight, orientationMayChange, adaptiveTrackSelectionFactory); } if (selection == null) { selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange, exceedConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary); } return selection; }
private static TrackSelection selectAdaptiveVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveTrackSelectionFactory) throws ExoPlaybackException { int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) : RendererCapabilities.ADAPTIVE_SEAMLESS; boolean allowMixedMimeTypes = allowMixedMimeAdaptiveness && (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0; for (int i = 0; i < groups.length; i++) { TrackGroup group = groups.get(i); int[] adaptiveTracks = getAdaptiveVideoTracksForGroup(group, formatSupport[i], allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange); if (adaptiveTracks.length > 0) { return adaptiveTrackSelectionFactory.createTrackSelection(group, adaptiveTracks); } } return null; }
/** * Finds the renderer to which the provided {@link TrackGroup} should be associated. * <p> * A {@link TrackGroup} is associated to a renderer that reports * {@link RendererCapabilities#FORMAT_HANDLED} support for one or more of the tracks in the group, * or {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES} if no such renderer exists, or * {@link RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE} if again no such renderer exists. In * the case that two or more renderers report the same level of support, the renderer with the * lowest index is associated. * <p> * If all renderers report {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all of the * tracks in the group, then {@code renderers.length} is returned to indicate that no association * was made. * * @param rendererCapabilities The {@link RendererCapabilities} of the renderers. * @param group The {@link TrackGroup} whose associated renderer is to be found. * @return The index of the associated renderer, or {@code renderers.length} if no * association was made. * @throws ExoPlaybackException If an error occurs finding a renderer. */ private static int findRenderer(RendererCapabilities[] rendererCapabilities, TrackGroup group) throws ExoPlaybackException { int bestRendererIndex = rendererCapabilities.length; int bestFormatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE; for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) { RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex]; for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { int formatSupportLevel = rendererCapability.supportsFormat(group.getFormat(trackIndex)) & RendererCapabilities.FORMAT_SUPPORT_MASK; if (formatSupportLevel > bestFormatSupportLevel) { bestRendererIndex = rendererIndex; bestFormatSupportLevel = formatSupportLevel; if (bestFormatSupportLevel == RendererCapabilities.FORMAT_HANDLED) { // We can't do better. return bestRendererIndex; } } } } return bestRendererIndex; }
/** * Returns whether a renderer supports tunneling for a {@link TrackSelection}. * * @param formatSupport The result of {@link RendererCapabilities#supportsFormat} for each * track, indexed by group index and track index (in that order). * @param trackGroups The {@link TrackGroupArray}s for the renderer. * @param selection The track selection. * @return Whether the renderer supports tunneling for the {@link TrackSelection}. */ private static boolean rendererSupportsTunneling(int[][] formatSupport, TrackGroupArray trackGroups, TrackSelection selection) { if (selection == null) { return false; } int trackGroupIndex = trackGroups.indexOf(selection.getTrackGroup()); for (int i = 0; i < selection.length(); i++) { int trackFormatSupport = formatSupport[trackGroupIndex][selection.getIndexInTrackGroup(i)]; if ((trackFormatSupport & RendererCapabilities.TUNNELING_SUPPORT_MASK) != RendererCapabilities.TUNNELING_SUPPORTED) { return false; } } return true; }
/** * Returns the extent to which a renderer can support playback of the tracks associated to it. * * @param rendererIndex The renderer index. * @return One of {@link #RENDERER_SUPPORT_PLAYABLE_TRACKS}, * {@link #RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS}, * {@link #RENDERER_SUPPORT_UNSUPPORTED_TRACKS} and {@link #RENDERER_SUPPORT_NO_TRACKS}. */ public int getRendererSupport(int rendererIndex) { int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; int[][] rendererFormatSupport = formatSupport[rendererIndex]; for (int i = 0; i < rendererFormatSupport.length; i++) { for (int j = 0; j < rendererFormatSupport[i].length; j++) { int trackRendererSupport; switch (rendererFormatSupport[i][j] & RendererCapabilities.FORMAT_SUPPORT_MASK) { case RendererCapabilities.FORMAT_HANDLED: return RENDERER_SUPPORT_PLAYABLE_TRACKS; case RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES: trackRendererSupport = RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS; break; default: trackRendererSupport = RENDERER_SUPPORT_UNSUPPORTED_TRACKS; break; } bestRendererSupport = Math.max(bestRendererSupport, trackRendererSupport); } } return bestRendererSupport; }
/** * Returns the extent to which the renderer supports adaptation between specified tracks within * a {@link TrackGroup}. * * @param rendererIndex The renderer index. * @param groupIndex The index of the group. * @return One of {@link RendererCapabilities#ADAPTIVE_SEAMLESS}, * {@link RendererCapabilities#ADAPTIVE_NOT_SEAMLESS} and * {@link RendererCapabilities#ADAPTIVE_NOT_SUPPORTED}. */ public int getAdaptiveSupport(int rendererIndex, int groupIndex, int[] trackIndices) { int handledTrackCount = 0; int adaptiveSupport = RendererCapabilities.ADAPTIVE_SEAMLESS; boolean multipleMimeTypes = false; String firstSampleMimeType = null; for (int i = 0; i < trackIndices.length; i++) { int trackIndex = trackIndices[i]; String sampleMimeType = trackGroups[rendererIndex].get(groupIndex).getFormat(trackIndex) .sampleMimeType; if (handledTrackCount++ == 0) { firstSampleMimeType = sampleMimeType; } else { multipleMimeTypes |= !Util.areEqual(firstSampleMimeType, sampleMimeType); } adaptiveSupport = Math.min(adaptiveSupport, formatSupport[rendererIndex][groupIndex][i] & RendererCapabilities.ADAPTIVE_SUPPORT_MASK); } return multipleMimeTypes ? Math.min(adaptiveSupport, mixedMimeTypeAdaptiveSupport[rendererIndex]) : adaptiveSupport; }
private static String getFormatSupportString(int formatSupport) { switch (formatSupport) { case RendererCapabilities.FORMAT_HANDLED: return "YES"; case RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES: return "NO_EXCEEDS_CAPABILITIES"; case RendererCapabilities.FORMAT_UNSUPPORTED_DRM: return "NO_UNSUPPORTED_DRM"; case RendererCapabilities.FORMAT_UNSUPPORTED_SUBTYPE: return "NO_UNSUPPORTED_TYPE"; case RendererCapabilities.FORMAT_UNSUPPORTED_TYPE: return "NO"; default: return "?"; } }
protected TrackSelection selectVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveVideoTrackSelectionFactory, boolean exceedConstraintsIfNecessary, boolean exceedRendererCapabilitiesIfNecessary) throws ExoPlaybackException { TrackSelection selection = null; if (adaptiveVideoTrackSelectionFactory != null) { selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth, viewportHeight, orientationMayChange, adaptiveVideoTrackSelectionFactory); } if (selection == null) { selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange, exceedConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary); } return selection; }
private static TrackSelection selectAdaptiveVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveVideoTrackSelectionFactory) throws ExoPlaybackException { int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) : RendererCapabilities.ADAPTIVE_SEAMLESS; boolean allowMixedMimeTypes = allowMixedMimeAdaptiveness && (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0; for (int i = 0; i < groups.length; i++) { TrackGroup group = groups.get(i); int[] adaptiveTracks = getAdaptiveTracksForGroup(group, formatSupport[i], allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange); if (adaptiveTracks.length > 0) { return adaptiveVideoTrackSelectionFactory.createTrackSelection(group, adaptiveTracks); } } return null; }
/** * Shows the selection dialog for a given renderer. * * @param activity The parent activity. * @param title The dialog's title. * @param trackInfo The current track information. * @param rendererIndex The index of the renderer. */ public void showSelectionDialog(Activity activity, CharSequence title, MappedTrackInfo trackInfo, int rendererIndex) { this.trackInfo = trackInfo; this.rendererIndex = rendererIndex; trackGroups = trackInfo.getTrackGroups(rendererIndex); trackGroupsAdaptive = new boolean[trackGroups.length]; for (int i = 0; i < trackGroups.length; i++) { trackGroupsAdaptive[i] = adaptiveVideoTrackSelectionFactory != null && trackInfo.getAdaptiveSupport(rendererIndex, i, false) != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED && trackGroups.get(i).length > 1; } isDisabled = selector.getRendererDisabled(rendererIndex); override = selector.getSelectionOverride(rendererIndex, trackGroups); AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(title) .setView(buildView(LayoutInflater.from(builder.getContext()))) .setPositiveButton(android.R.string.ok, this) .setNegativeButton(android.R.string.cancel, null) .create() .show(); }
protected TrackSelection selectVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveVideoTrackSelectionFactory, boolean exceedConstraintsIfNecessary) throws ExoPlaybackException { TrackSelection selection = null; if (adaptiveVideoTrackSelectionFactory != null) { selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport, maxVideoWidth, maxVideoHeight, allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth, viewportHeight, orientationMayChange, adaptiveVideoTrackSelectionFactory); } if (selection == null) { selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight, viewportWidth, viewportHeight, orientationMayChange, exceedConstraintsIfNecessary); } return selection; }
private static TrackSelection selectAdaptiveVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveVideoTrackSelectionFactory) throws ExoPlaybackException { int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) : RendererCapabilities.ADAPTIVE_SEAMLESS; boolean allowMixedMimeTypes = allowMixedMimeAdaptiveness && (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0; for (int i = 0; i < groups.length; i++) { TrackGroup group = groups.get(i); int[] adaptiveTracks = getAdaptiveTracksForGroup(group, formatSupport[i], allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, viewportWidth, viewportHeight, orientationMayChange); if (adaptiveTracks.length > 0) { return adaptiveVideoTrackSelectionFactory.createTrackSelection(group, adaptiveTracks); } } return null; }
/** * Finds the renderer to which the provided {@link TrackGroup} should be associated. * <p> * A {@link TrackGroup} is associated to a renderer that reports * {@link RendererCapabilities#FORMAT_HANDLED} support for one or more of the tracks in the group, * or {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES} if no such renderer exists, or * {@link RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE} if again no such renderer exists. In * the case that two or more renderers report the same level of support, the renderer with the * lowest index is associated. * <p> * If all renderers report {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all of the * tracks in the group, then {@code renderers.length} is returned to indicate that no association * was made. * * @param rendererCapabilities The {@link RendererCapabilities} of the renderers. * @param group The {@link TrackGroup} whose associated renderer is to be found. * @return The index of the associated renderer, or {@code renderers.length} if no * association was made. * @throws ExoPlaybackException If an error occurs finding a renderer. */ private static int findRenderer(RendererCapabilities[] rendererCapabilities, TrackGroup group) throws ExoPlaybackException { int bestRendererIndex = rendererCapabilities.length; int bestSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE; for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) { RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex]; for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { int trackSupportLevel = rendererCapability.supportsFormat(group.getFormat(trackIndex)); if (trackSupportLevel > bestSupportLevel) { bestRendererIndex = rendererIndex; bestSupportLevel = trackSupportLevel; if (bestSupportLevel == RendererCapabilities.FORMAT_HANDLED) { // We can't do better. return bestRendererIndex; } } } } return bestRendererIndex; }
/** * Shows the selection dialog for a given renderer. * * @param activity The parent activity. * @param title The dialog's title. * @param trackInfo The current track information. * @param rendererIndex The index of the renderer. */ public void showSelectionDialog(Activity activity, CharSequence title, MappedTrackInfo trackInfo, int rendererIndex) { this.trackInfo = trackInfo; this.rendererIndex = rendererIndex; trackGroups = trackInfo.getTrackGroups(rendererIndex); trackGroupsAdaptive = new boolean[trackGroups.length]; for (int i = 0; i < trackGroups.length; i++) { trackGroupsAdaptive[i] = adaptiveTrackSelectionFactory != null && trackInfo.getAdaptiveSupport(rendererIndex, i, false) != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED && trackGroups.get(i).length > 1; } isDisabled = selector.getRendererDisabled(rendererIndex); override = selector.getSelectionOverride(rendererIndex, trackGroups); AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(title) .setView(buildView(builder.getContext())) .setPositiveButton(android.R.string.ok, this) .setNegativeButton(android.R.string.cancel, null) .create() .show(); }
private static TrackSelection selectAdaptiveVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, Parameters params, TrackSelection.Factory adaptiveTrackSelectionFactory) throws ExoPlaybackException { int requiredAdaptiveSupport = params.allowNonSeamlessAdaptiveness ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) : RendererCapabilities.ADAPTIVE_SEAMLESS; boolean allowMixedMimeTypes = params.allowMixedMimeAdaptiveness && (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0; for (int i = 0; i < groups.length; i++) { TrackGroup group = groups.get(i); int[] adaptiveTracks = getAdaptiveVideoTracksForGroup(group, formatSupport[i], allowMixedMimeTypes, requiredAdaptiveSupport, params.maxVideoWidth, params.maxVideoHeight, params.maxVideoBitrate, params.viewportWidth, params.viewportHeight, params.viewportOrientationMayChange); if (adaptiveTracks.length > 0) { return adaptiveTrackSelectionFactory.createTrackSelection(group, adaptiveTracks); } } return null; }
/** * Returns the extent to which a renderer can play the tracks in the track groups mapped to it. * * @param rendererIndex The renderer index. * @return One of {@link #RENDERER_SUPPORT_PLAYABLE_TRACKS}, * {@link #RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS}, * {@link #RENDERER_SUPPORT_UNSUPPORTED_TRACKS} and {@link #RENDERER_SUPPORT_NO_TRACKS}. */ public int getRendererSupport(int rendererIndex) { int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; int[][] rendererFormatSupport = formatSupport[rendererIndex]; for (int i = 0; i < rendererFormatSupport.length; i++) { for (int j = 0; j < rendererFormatSupport[i].length; j++) { int trackRendererSupport; switch (rendererFormatSupport[i][j] & RendererCapabilities.FORMAT_SUPPORT_MASK) { case RendererCapabilities.FORMAT_HANDLED: return RENDERER_SUPPORT_PLAYABLE_TRACKS; case RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES: trackRendererSupport = RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS; break; default: trackRendererSupport = RENDERER_SUPPORT_UNSUPPORTED_TRACKS; break; } bestRendererSupport = Math.max(bestRendererSupport, trackRendererSupport); } } return bestRendererSupport; }
/** * Returns the extent to which a renderer supports adaptation between specified tracks within * a {@link TrackGroup}. * * @param rendererIndex The renderer index. * @param groupIndex The index of the track group. * @return One of {@link RendererCapabilities#ADAPTIVE_SEAMLESS}, * {@link RendererCapabilities#ADAPTIVE_NOT_SEAMLESS} and * {@link RendererCapabilities#ADAPTIVE_NOT_SUPPORTED}. */ public int getAdaptiveSupport(int rendererIndex, int groupIndex, int[] trackIndices) { int handledTrackCount = 0; int adaptiveSupport = RendererCapabilities.ADAPTIVE_SEAMLESS; boolean multipleMimeTypes = false; String firstSampleMimeType = null; for (int i = 0; i < trackIndices.length; i++) { int trackIndex = trackIndices[i]; String sampleMimeType = trackGroups[rendererIndex].get(groupIndex).getFormat(trackIndex) .sampleMimeType; if (handledTrackCount++ == 0) { firstSampleMimeType = sampleMimeType; } else { multipleMimeTypes |= !Util.areEqual(firstSampleMimeType, sampleMimeType); } adaptiveSupport = Math.min(adaptiveSupport, formatSupport[rendererIndex][groupIndex][i] & RendererCapabilities.ADAPTIVE_SUPPORT_MASK); } return multipleMimeTypes ? Math.min(adaptiveSupport, mixedMimeTypeAdaptiveSupport[rendererIndex]) : adaptiveSupport; }
/** * Finds the renderer to which the provided {@link TrackGroup} should be mapped. * <p> * A {@link TrackGroup} is mapped to the renderer that reports the highest of (listed in * decreasing order of support) {@link RendererCapabilities#FORMAT_HANDLED}, * {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}, * {@link RendererCapabilities#FORMAT_UNSUPPORTED_DRM} and * {@link RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE}. In the case that two or more renderers * report the same level of support, the renderer with the lowest index is associated. * <p> * If all renderers report {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all of the * tracks in the group, then {@code renderers.length} is returned to indicate that the group was * not mapped to any renderer. * * @param rendererCapabilities The {@link RendererCapabilities} of the renderers. * @param group The track group to map to a renderer. * @return The index of the renderer to which the track group was mapped, or * {@code renderers.length} if it was not mapped to any renderer. * @throws ExoPlaybackException If an error occurs finding a renderer. */ private static int findRenderer(RendererCapabilities[] rendererCapabilities, TrackGroup group) throws ExoPlaybackException { int bestRendererIndex = rendererCapabilities.length; int bestFormatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE; for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) { RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex]; for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { int formatSupportLevel = rendererCapability.supportsFormat(group.getFormat(trackIndex)) & RendererCapabilities.FORMAT_SUPPORT_MASK; if (formatSupportLevel > bestFormatSupportLevel) { bestRendererIndex = rendererIndex; bestFormatSupportLevel = formatSupportLevel; if (bestFormatSupportLevel == RendererCapabilities.FORMAT_HANDLED) { // We can't do better. return bestRendererIndex; } } } } return bestRendererIndex; }
/** * Tests that track selector will select audio track with language that match preferred language * given by {@link Parameters}. */ @Test public void testSelectTracksSelectPreferredAudioLanguage() throws Exception { Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en"); trackSelector.setParameters(parameters); Format frAudioFormat = Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "fr"); Format enAudioFormat = Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "en"); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, singleTrackGroup(frAudioFormat, enAudioFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(enAudioFormat); }
/** * Tests that track selector will prefer selecting audio track with language that match preferred * language given by {@link Parameters} over track with {@link C#SELECTION_FLAG_DEFAULT}. */ @Test public void testSelectTracksSelectPreferredAudioLanguageOverSelectionFlag() throws Exception { Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en"); trackSelector.setParameters(parameters); Format frAudioFormat = Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, "fr"); Format enAudioFormat = Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "en"); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, singleTrackGroup(frAudioFormat, enAudioFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(enAudioFormat); }
/** * Tests that track selector will prefer tracks that are within renderer's capabilities over * track that exceed renderer's capabilities. */ @Test public void testSelectTracksPreferTrackWithinCapabilities() throws Exception { Format supportedFormat = Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null); Format exceededFormat = Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null); Map<String, Integer> mappedCapabilities = new HashMap<>(); mappedCapabilities.put(supportedFormat.id, FORMAT_HANDLED); mappedCapabilities.put(exceededFormat.id, FORMAT_EXCEEDS_CAPABILITIES); RendererCapabilities mappedAudioRendererCapabilities = new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {mappedAudioRendererCapabilities}, singleTrackGroup(exceededFormat, supportedFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFormat); }
/** * Tests that track selector will return a null track selection for a renderer when * all tracks exceed that renderer's capabilities when {@link Parameters} does not allow * exceeding-capabilities tracks. */ @Test public void testSelectTracksWithNoTrackWithinCapabilitiesAndSetByParamsReturnNoSelection() throws Exception { Parameters parameters = DEFAULT_PARAMETERS.withExceedRendererCapabilitiesIfNecessary(false); trackSelector.setParameters(parameters); Format audioFormat = Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES}, singleTrackGroup(audioFormat)); assertThat(result.selections.get(0)).isNull(); }
/** * Tests that track selector will prefer tracks that are within renderer's capabilities over * tracks that have {@link C#SELECTION_FLAG_DEFAULT} but exceed renderer's capabilities. */ @Test public void testSelectTracksPreferTrackWithinCapabilitiesOverSelectionFlag() throws Exception { Format supportedFormat = Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null); Format exceededWithSelectionFlagFormat = Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, null); Map<String, Integer> mappedCapabilities = new HashMap<>(); mappedCapabilities.put(supportedFormat.id, FORMAT_HANDLED); mappedCapabilities.put(exceededWithSelectionFlagFormat.id, FORMAT_EXCEEDS_CAPABILITIES); RendererCapabilities mappedAudioRendererCapabilities = new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {mappedAudioRendererCapabilities}, singleTrackGroup(exceededWithSelectionFlagFormat, supportedFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFormat); }
/** * Tests that track selector will prefer tracks that are within renderer's capabilities over * track that have language matching preferred audio given by {@link Parameters} but exceed * renderer's capabilities. */ @Test public void testSelectTracksPreferTrackWithinCapabilitiesOverPreferredLanguage() throws Exception { Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en"); trackSelector.setParameters(parameters); Format supportedFrFormat = Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "fr"); Format exceededEnFormat = Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "en"); Map<String, Integer> mappedCapabilities = new HashMap<>(); mappedCapabilities.put(exceededEnFormat.id, FORMAT_EXCEEDS_CAPABILITIES); mappedCapabilities.put(supportedFrFormat.id, FORMAT_HANDLED); RendererCapabilities mappedAudioRendererCapabilities = new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {mappedAudioRendererCapabilities}, singleTrackGroup(exceededEnFormat, supportedFrFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFrFormat); }
/** * Tests that track selector will prefer tracks that are within renderer's capabilities over * track that have both language matching preferred audio given by {@link Parameters} and * {@link C#SELECTION_FLAG_DEFAULT}, but exceed renderer's capabilities. */ @Test public void testSelectTracksPreferTrackWithinCapabilitiesOverSelectionFlagAndPreferredLanguage() throws Exception { Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en"); trackSelector.setParameters(parameters); Format supportedFrFormat = Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "fr"); Format exceededDefaultSelectionEnFormat = Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, "en"); Map<String, Integer> mappedCapabilities = new HashMap<>(); mappedCapabilities.put(exceededDefaultSelectionEnFormat.id, FORMAT_EXCEEDS_CAPABILITIES); mappedCapabilities.put(supportedFrFormat.id, FORMAT_HANDLED); RendererCapabilities mappedAudioRendererCapabilities = new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {mappedAudioRendererCapabilities}, singleTrackGroup(exceededDefaultSelectionEnFormat, supportedFrFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFrFormat); }
/** * Tests that track selector will select audio tracks with higher num channel when other factors * are the same, and tracks are within renderer's capabilities. */ @Test public void testSelectTracksWithinCapabilitiesSelectHigherNumChannel() throws Exception { Format lowerChannelFormat = Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null); Format higherChannelFormat = Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 6, 44100, null, null, 0, null); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, singleTrackGroup(higherChannelFormat, lowerChannelFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(higherChannelFormat); }
/** * Tests that track selector will select audio tracks with higher sample rate when other factors * are the same, and tracks are within renderer's capabilities. */ @Test public void testSelectTracksWithinCapabilitiesSelectHigherSampleRate() throws Exception { Format higherSampleRateFormat = Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null); Format lowerSampleRateFormat = Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 22050, null, null, 0, null); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, singleTrackGroup(higherSampleRateFormat, lowerSampleRateFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(higherSampleRateFormat); }
/** * Tests that track selector will select audio tracks with higher bit-rate when other factors * are the same, and tracks are within renderer's capabilities. */ @Test public void testSelectTracksWithinCapabilitiesSelectHigherBitrate() throws Exception { Format lowerBitrateFormat = Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 15000, Format.NO_VALUE, 2, 44100, null, null, 0, null); Format higherBitrateFormat = Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 30000, Format.NO_VALUE, 2, 44100, null, null, 0, null); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES}, singleTrackGroup(lowerBitrateFormat, higherBitrateFormat)); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(higherBitrateFormat); }