• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.trackselection;
17 
18 import android.util.Pair;
19 import androidx.annotation.IntDef;
20 import androidx.annotation.Nullable;
21 import com.google.android.exoplayer2.C;
22 import com.google.android.exoplayer2.ExoPlaybackException;
23 import com.google.android.exoplayer2.Renderer;
24 import com.google.android.exoplayer2.RendererCapabilities;
25 import com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport;
26 import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
27 import com.google.android.exoplayer2.RendererCapabilities.FormatSupport;
28 import com.google.android.exoplayer2.RendererConfiguration;
29 import com.google.android.exoplayer2.Timeline;
30 import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
31 import com.google.android.exoplayer2.source.TrackGroup;
32 import com.google.android.exoplayer2.source.TrackGroupArray;
33 import com.google.android.exoplayer2.util.MimeTypes;
34 import com.google.android.exoplayer2.util.Util;
35 import java.lang.annotation.Documented;
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.Arrays;
39 import org.checkerframework.checker.nullness.compatqual.NullableType;
40 
41 /**
42  * Base class for {@link TrackSelector}s that first establish a mapping between {@link TrackGroup}s
43  * and {@link Renderer}s, and then from that mapping create a {@link TrackSelection} for each
44  * renderer.
45  */
46 public abstract class MappingTrackSelector extends TrackSelector {
47 
48   /**
49    * Provides mapped track information for each renderer.
50    */
51   public static final class MappedTrackInfo {
52 
53     /**
54      * Levels of renderer support. Higher numerical values indicate higher levels of support. One of
55      * {@link #RENDERER_SUPPORT_NO_TRACKS}, {@link #RENDERER_SUPPORT_UNSUPPORTED_TRACKS}, {@link
56      * #RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS} or {@link #RENDERER_SUPPORT_PLAYABLE_TRACKS}.
57      */
58     @Documented
59     @Retention(RetentionPolicy.SOURCE)
60     @IntDef({
61       RENDERER_SUPPORT_NO_TRACKS,
62       RENDERER_SUPPORT_UNSUPPORTED_TRACKS,
63       RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS,
64       RENDERER_SUPPORT_PLAYABLE_TRACKS
65     })
66     @interface RendererSupport {}
67     /** The renderer does not have any associated tracks. */
68     public static final int RENDERER_SUPPORT_NO_TRACKS = 0;
69     /**
70      * The renderer has tracks mapped to it, but all are unsupported. In other words, {@link
71      * #getTrackSupport(int, int, int)} returns {@link RendererCapabilities#FORMAT_UNSUPPORTED_DRM},
72      * {@link RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE} or {@link
73      * RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all tracks mapped to the renderer.
74      */
75     public static final int RENDERER_SUPPORT_UNSUPPORTED_TRACKS = 1;
76     /**
77      * The renderer has tracks mapped to it and at least one is of a supported type, but all such
78      * tracks exceed the renderer's capabilities. In other words, {@link #getTrackSupport(int, int,
79      * int)} returns {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES} for at least one
80      * track mapped to the renderer, but does not return {@link
81      * RendererCapabilities#FORMAT_HANDLED} for any tracks mapped to the renderer.
82      */
83     public static final int RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS = 2;
84     /**
85      * The renderer has tracks mapped to it, and at least one such track is playable. In other
86      * words, {@link #getTrackSupport(int, int, int)} returns {@link
87      * RendererCapabilities#FORMAT_HANDLED} for at least one track mapped to the renderer.
88      */
89     public static final int RENDERER_SUPPORT_PLAYABLE_TRACKS = 3;
90 
91     /** @deprecated Use {@link #getRendererCount()}. */
92     @Deprecated public final int length;
93 
94     private final int rendererCount;
95     private final String[] rendererNames;
96     private final int[] rendererTrackTypes;
97     private final TrackGroupArray[] rendererTrackGroups;
98     @AdaptiveSupport private final int[] rendererMixedMimeTypeAdaptiveSupports;
99     @Capabilities private final int[][][] rendererFormatSupports;
100     private final TrackGroupArray unmappedTrackGroups;
101 
102     /**
103      * @param rendererNames The name of each renderer.
104      * @param rendererTrackTypes The track type handled by each renderer.
105      * @param rendererTrackGroups The {@link TrackGroup}s mapped to each renderer.
106      * @param rendererMixedMimeTypeAdaptiveSupports The {@link AdaptiveSupport} for mixed MIME type
107      *     adaptation for the renderer.
108      * @param rendererFormatSupports The {@link Capabilities} for each mapped track, indexed by
109      *     renderer, track group and track (in that order).
110      * @param unmappedTrackGroups {@link TrackGroup}s not mapped to any renderer.
111      */
112     @SuppressWarnings("deprecation")
MappedTrackInfo( String[] rendererNames, int[] rendererTrackTypes, TrackGroupArray[] rendererTrackGroups, @AdaptiveSupport int[] rendererMixedMimeTypeAdaptiveSupports, @Capabilities int[][][] rendererFormatSupports, TrackGroupArray unmappedTrackGroups)113     /* package */ MappedTrackInfo(
114         String[] rendererNames,
115         int[] rendererTrackTypes,
116         TrackGroupArray[] rendererTrackGroups,
117         @AdaptiveSupport int[] rendererMixedMimeTypeAdaptiveSupports,
118         @Capabilities int[][][] rendererFormatSupports,
119         TrackGroupArray unmappedTrackGroups) {
120       this.rendererNames = rendererNames;
121       this.rendererTrackTypes = rendererTrackTypes;
122       this.rendererTrackGroups = rendererTrackGroups;
123       this.rendererFormatSupports = rendererFormatSupports;
124       this.rendererMixedMimeTypeAdaptiveSupports = rendererMixedMimeTypeAdaptiveSupports;
125       this.unmappedTrackGroups = unmappedTrackGroups;
126       this.rendererCount = rendererTrackTypes.length;
127       this.length = rendererCount;
128     }
129 
130     /** Returns the number of renderers. */
getRendererCount()131     public int getRendererCount() {
132       return rendererCount;
133     }
134 
135     /**
136      * Returns the name of the renderer at a given index.
137      *
138      * @see Renderer#getName()
139      * @param rendererIndex The renderer index.
140      * @return The name of the renderer.
141      */
getRendererName(int rendererIndex)142     public String getRendererName(int rendererIndex) {
143       return rendererNames[rendererIndex];
144     }
145 
146     /**
147      * Returns the track type that the renderer at a given index handles.
148      *
149      * @see Renderer#getTrackType()
150      * @param rendererIndex The renderer index.
151      * @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}.
152      */
getRendererType(int rendererIndex)153     public int getRendererType(int rendererIndex) {
154       return rendererTrackTypes[rendererIndex];
155     }
156 
157     /**
158      * Returns the {@link TrackGroup}s mapped to the renderer at the specified index.
159      *
160      * @param rendererIndex The renderer index.
161      * @return The corresponding {@link TrackGroup}s.
162      */
getTrackGroups(int rendererIndex)163     public TrackGroupArray getTrackGroups(int rendererIndex) {
164       return rendererTrackGroups[rendererIndex];
165     }
166 
167     /**
168      * Returns the extent to which a renderer can play the tracks that are mapped to it.
169      *
170      * @param rendererIndex The renderer index.
171      * @return The {@link RendererSupport}.
172      */
173     @RendererSupport
getRendererSupport(int rendererIndex)174     public int getRendererSupport(int rendererIndex) {
175       @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS;
176       @Capabilities int[][] rendererFormatSupport = rendererFormatSupports[rendererIndex];
177       for (@Capabilities int[] trackGroupFormatSupport : rendererFormatSupport) {
178         for (@Capabilities int trackFormatSupport : trackGroupFormatSupport) {
179           int trackRendererSupport;
180           switch (RendererCapabilities.getFormatSupport(trackFormatSupport)) {
181             case RendererCapabilities.FORMAT_HANDLED:
182               return RENDERER_SUPPORT_PLAYABLE_TRACKS;
183             case RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES:
184               trackRendererSupport = RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS;
185               break;
186             case RendererCapabilities.FORMAT_UNSUPPORTED_TYPE:
187             case RendererCapabilities.FORMAT_UNSUPPORTED_SUBTYPE:
188             case RendererCapabilities.FORMAT_UNSUPPORTED_DRM:
189               trackRendererSupport = RENDERER_SUPPORT_UNSUPPORTED_TRACKS;
190               break;
191             default:
192               throw new IllegalStateException();
193           }
194           bestRendererSupport = Math.max(bestRendererSupport, trackRendererSupport);
195         }
196       }
197       return bestRendererSupport;
198     }
199 
200     /** @deprecated Use {@link #getTypeSupport(int)}. */
201     @Deprecated
202     @RendererSupport
getTrackTypeRendererSupport(int trackType)203     public int getTrackTypeRendererSupport(int trackType) {
204       return getTypeSupport(trackType);
205     }
206 
207     /**
208      * Returns the extent to which tracks of a specified type are supported. This is the best level
209      * of support obtained from {@link #getRendererSupport(int)} for all renderers that handle the
210      * specified type. If no such renderers exist then {@link #RENDERER_SUPPORT_NO_TRACKS} is
211      * returned.
212      *
213      * @param trackType The track type. One of the {@link C} {@code TRACK_TYPE_*} constants.
214      * @return The {@link RendererSupport}.
215      */
216     @RendererSupport
getTypeSupport(int trackType)217     public int getTypeSupport(int trackType) {
218       @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS;
219       for (int i = 0; i < rendererCount; i++) {
220         if (rendererTrackTypes[i] == trackType) {
221           bestRendererSupport = Math.max(bestRendererSupport, getRendererSupport(i));
222         }
223       }
224       return bestRendererSupport;
225     }
226 
227     /** @deprecated Use {@link #getTrackSupport(int, int, int)}. */
228     @Deprecated
229     @FormatSupport
getTrackFormatSupport(int rendererIndex, int groupIndex, int trackIndex)230     public int getTrackFormatSupport(int rendererIndex, int groupIndex, int trackIndex) {
231       return getTrackSupport(rendererIndex, groupIndex, trackIndex);
232     }
233 
234     /**
235      * Returns the extent to which an individual track is supported by the renderer.
236      *
237      * @param rendererIndex The renderer index.
238      * @param groupIndex The index of the track group to which the track belongs.
239      * @param trackIndex The index of the track within the track group.
240      * @return The {@link FormatSupport}.
241      */
242     @FormatSupport
getTrackSupport(int rendererIndex, int groupIndex, int trackIndex)243     public int getTrackSupport(int rendererIndex, int groupIndex, int trackIndex) {
244       return RendererCapabilities.getFormatSupport(
245           rendererFormatSupports[rendererIndex][groupIndex][trackIndex]);
246     }
247 
248     /**
249      * Returns the extent to which a renderer supports adaptation between supported tracks in a
250      * specified {@link TrackGroup}.
251      *
252      * <p>Tracks for which {@link #getTrackSupport(int, int, int)} returns {@link
253      * RendererCapabilities#FORMAT_HANDLED} are always considered. Tracks for which {@link
254      * #getTrackSupport(int, int, int)} returns {@link
255      * RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES} are also considered if {@code
256      * includeCapabilitiesExceededTracks} is set to {@code true}. Tracks for which {@link
257      * #getTrackSupport(int, int, int)} returns {@link RendererCapabilities#FORMAT_UNSUPPORTED_DRM},
258      * {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} or {@link
259      * RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE} are never considered.
260      *
261      * @param rendererIndex The renderer index.
262      * @param groupIndex The index of the track group.
263      * @param includeCapabilitiesExceededTracks Whether tracks that exceed the capabilities of the
264      *     renderer are included when determining support.
265      * @return The {@link AdaptiveSupport}.
266      */
267     @AdaptiveSupport
getAdaptiveSupport( int rendererIndex, int groupIndex, boolean includeCapabilitiesExceededTracks)268     public int getAdaptiveSupport(
269         int rendererIndex, int groupIndex, boolean includeCapabilitiesExceededTracks) {
270       int trackCount = rendererTrackGroups[rendererIndex].get(groupIndex).length;
271       // Iterate over the tracks in the group, recording the indices of those to consider.
272       int[] trackIndices = new int[trackCount];
273       int trackIndexCount = 0;
274       for (int i = 0; i < trackCount; i++) {
275         @FormatSupport int fixedSupport = getTrackSupport(rendererIndex, groupIndex, i);
276         if (fixedSupport == RendererCapabilities.FORMAT_HANDLED
277             || (includeCapabilitiesExceededTracks
278             && fixedSupport == RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES)) {
279           trackIndices[trackIndexCount++] = i;
280         }
281       }
282       trackIndices = Arrays.copyOf(trackIndices, trackIndexCount);
283       return getAdaptiveSupport(rendererIndex, groupIndex, trackIndices);
284     }
285 
286     /**
287      * Returns the extent to which a renderer supports adaptation between specified tracks within a
288      * {@link TrackGroup}.
289      *
290      * @param rendererIndex The renderer index.
291      * @param groupIndex The index of the track group.
292      * @return The {@link AdaptiveSupport}.
293      */
294     @AdaptiveSupport
getAdaptiveSupport(int rendererIndex, int groupIndex, int[] trackIndices)295     public int getAdaptiveSupport(int rendererIndex, int groupIndex, int[] trackIndices) {
296       int handledTrackCount = 0;
297       @AdaptiveSupport int adaptiveSupport = RendererCapabilities.ADAPTIVE_SEAMLESS;
298       boolean multipleMimeTypes = false;
299       String firstSampleMimeType = null;
300       for (int i = 0; i < trackIndices.length; i++) {
301         int trackIndex = trackIndices[i];
302         String sampleMimeType =
303             rendererTrackGroups[rendererIndex].get(groupIndex).getFormat(trackIndex).sampleMimeType;
304         if (handledTrackCount++ == 0) {
305           firstSampleMimeType = sampleMimeType;
306         } else {
307           multipleMimeTypes |= !Util.areEqual(firstSampleMimeType, sampleMimeType);
308         }
309         adaptiveSupport =
310             Math.min(
311                 adaptiveSupport,
312                 RendererCapabilities.getAdaptiveSupport(
313                     rendererFormatSupports[rendererIndex][groupIndex][i]));
314       }
315       return multipleMimeTypes
316           ? Math.min(adaptiveSupport, rendererMixedMimeTypeAdaptiveSupports[rendererIndex])
317           : adaptiveSupport;
318     }
319 
320     /** @deprecated Use {@link #getUnmappedTrackGroups()}. */
321     @Deprecated
getUnassociatedTrackGroups()322     public TrackGroupArray getUnassociatedTrackGroups() {
323       return getUnmappedTrackGroups();
324     }
325 
326     /** Returns {@link TrackGroup}s not mapped to any renderer. */
getUnmappedTrackGroups()327     public TrackGroupArray getUnmappedTrackGroups() {
328       return unmappedTrackGroups;
329     }
330 
331   }
332 
333   @Nullable private MappedTrackInfo currentMappedTrackInfo;
334 
335   /**
336    * Returns the mapping information for the currently active track selection, or null if no
337    * selection is currently active.
338    */
getCurrentMappedTrackInfo()339   public final @Nullable MappedTrackInfo getCurrentMappedTrackInfo() {
340     return currentMappedTrackInfo;
341   }
342 
343   // TrackSelector implementation.
344 
345   @Override
onSelectionActivated(Object info)346   public final void onSelectionActivated(Object info) {
347     currentMappedTrackInfo = (MappedTrackInfo) info;
348   }
349 
350   @Override
selectTracks( RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups, MediaPeriodId periodId, Timeline timeline)351   public final TrackSelectorResult selectTracks(
352       RendererCapabilities[] rendererCapabilities,
353       TrackGroupArray trackGroups,
354       MediaPeriodId periodId,
355       Timeline timeline)
356       throws ExoPlaybackException {
357     // Structures into which data will be written during the selection. The extra item at the end
358     // of each array is to store data associated with track groups that cannot be associated with
359     // any renderer.
360     int[] rendererTrackGroupCounts = new int[rendererCapabilities.length + 1];
361     TrackGroup[][] rendererTrackGroups = new TrackGroup[rendererCapabilities.length + 1][];
362     @Capabilities int[][][] rendererFormatSupports = new int[rendererCapabilities.length + 1][][];
363     for (int i = 0; i < rendererTrackGroups.length; i++) {
364       rendererTrackGroups[i] = new TrackGroup[trackGroups.length];
365       rendererFormatSupports[i] = new int[trackGroups.length][];
366     }
367 
368     // Determine the extent to which each renderer supports mixed mimeType adaptation.
369     @AdaptiveSupport
370     int[] rendererMixedMimeTypeAdaptationSupports =
371         getMixedMimeTypeAdaptationSupports(rendererCapabilities);
372 
373     // Associate each track group to a preferred renderer, and evaluate the support that the
374     // renderer provides for each track in the group.
375     for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
376       TrackGroup group = trackGroups.get(groupIndex);
377       // Associate the group to a preferred renderer.
378       boolean preferUnassociatedRenderer =
379           MimeTypes.getTrackType(group.getFormat(0).sampleMimeType) == C.TRACK_TYPE_METADATA;
380       int rendererIndex =
381           findRenderer(
382               rendererCapabilities, group, rendererTrackGroupCounts, preferUnassociatedRenderer);
383       // Evaluate the support that the renderer provides for each track in the group.
384       @Capabilities
385       int[] rendererFormatSupport =
386           rendererIndex == rendererCapabilities.length
387               ? new int[group.length]
388               : getFormatSupport(rendererCapabilities[rendererIndex], group);
389       // Stash the results.
390       int rendererTrackGroupCount = rendererTrackGroupCounts[rendererIndex];
391       rendererTrackGroups[rendererIndex][rendererTrackGroupCount] = group;
392       rendererFormatSupports[rendererIndex][rendererTrackGroupCount] = rendererFormatSupport;
393       rendererTrackGroupCounts[rendererIndex]++;
394     }
395 
396     // Create a track group array for each renderer, and trim each rendererFormatSupports entry.
397     TrackGroupArray[] rendererTrackGroupArrays = new TrackGroupArray[rendererCapabilities.length];
398     String[] rendererNames = new String[rendererCapabilities.length];
399     int[] rendererTrackTypes = new int[rendererCapabilities.length];
400     for (int i = 0; i < rendererCapabilities.length; i++) {
401       int rendererTrackGroupCount = rendererTrackGroupCounts[i];
402       rendererTrackGroupArrays[i] =
403           new TrackGroupArray(
404               Util.nullSafeArrayCopy(rendererTrackGroups[i], rendererTrackGroupCount));
405       rendererFormatSupports[i] =
406           Util.nullSafeArrayCopy(rendererFormatSupports[i], rendererTrackGroupCount);
407       rendererNames[i] = rendererCapabilities[i].getName();
408       rendererTrackTypes[i] = rendererCapabilities[i].getTrackType();
409     }
410 
411     // Create a track group array for track groups not mapped to a renderer.
412     int unmappedTrackGroupCount = rendererTrackGroupCounts[rendererCapabilities.length];
413     TrackGroupArray unmappedTrackGroupArray =
414         new TrackGroupArray(
415             Util.nullSafeArrayCopy(
416                 rendererTrackGroups[rendererCapabilities.length], unmappedTrackGroupCount));
417 
418     // Package up the track information and selections.
419     MappedTrackInfo mappedTrackInfo =
420         new MappedTrackInfo(
421             rendererNames,
422             rendererTrackTypes,
423             rendererTrackGroupArrays,
424             rendererMixedMimeTypeAdaptationSupports,
425             rendererFormatSupports,
426             unmappedTrackGroupArray);
427 
428     Pair<@NullableType RendererConfiguration[], @NullableType TrackSelection[]> result =
429         selectTracks(
430             mappedTrackInfo, rendererFormatSupports, rendererMixedMimeTypeAdaptationSupports);
431     return new TrackSelectorResult(result.first, result.second, mappedTrackInfo);
432   }
433 
434   /**
435    * Given mapped track information, returns a track selection and configuration for each renderer.
436    *
437    * @param mappedTrackInfo Mapped track information.
438    * @param rendererFormatSupports The {@link Capabilities} for ach mapped track, indexed by
439    *     renderer, track group and track (in that order).
440    * @param rendererMixedMimeTypeAdaptationSupport The {@link AdaptiveSupport} for mixed MIME type
441    *     adaptation for the renderer.
442    * @return A pair consisting of the track selections and configurations for each renderer. A null
443    *     configuration indicates the renderer should be disabled, in which case the track selection
444    *     will also be null. A track selection may also be null for a non-disabled renderer if {@link
445    *     RendererCapabilities#getTrackType()} is {@link C#TRACK_TYPE_NONE}.
446    * @throws ExoPlaybackException If an error occurs while selecting the tracks.
447    */
448   protected abstract Pair<@NullableType RendererConfiguration[], @NullableType TrackSelection[]>
selectTracks( MappedTrackInfo mappedTrackInfo, @Capabilities int[][][] rendererFormatSupports, @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupport)449       selectTracks(
450           MappedTrackInfo mappedTrackInfo,
451           @Capabilities int[][][] rendererFormatSupports,
452           @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupport)
453           throws ExoPlaybackException;
454 
455   /**
456    * Finds the renderer to which the provided {@link TrackGroup} should be mapped.
457    *
458    * <p>A {@link TrackGroup} is mapped to the renderer that reports the highest of (listed in
459    * decreasing order of support) {@link RendererCapabilities#FORMAT_HANDLED}, {@link
460    * RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}, {@link
461    * RendererCapabilities#FORMAT_UNSUPPORTED_DRM} and {@link
462    * RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE}.
463    *
464    * <p>In the case that two or more renderers report the same level of support, the assignment
465    * depends on {@code preferUnassociatedRenderer}.
466    *
467    * <ul>
468    *   <li>If {@code preferUnassociatedRenderer} is false, the renderer with the lowest index is
469    *       chosen regardless of how many other track groups are already mapped to this renderer.
470    *   <li>If {@code preferUnassociatedRenderer} is true, the renderer with the lowest index and no
471    *       other mapped track group is chosen, or the renderer with the lowest index if all
472    *       available renderers have already mapped track groups.
473    * </ul>
474    *
475    * <p>If all renderers report {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all of the
476    * tracks in the group, then {@code renderers.length} is returned to indicate that the group was
477    * not mapped to any renderer.
478    *
479    * @param rendererCapabilities The {@link RendererCapabilities} of the renderers.
480    * @param group The track group to map to a renderer.
481    * @param rendererTrackGroupCounts The number of already mapped track groups for each renderer.
482    * @param preferUnassociatedRenderer Whether renderers unassociated to any track group should be
483    *     preferred.
484    * @return The index of the renderer to which the track group was mapped, or {@code
485    *     renderers.length} if it was not mapped to any renderer.
486    * @throws ExoPlaybackException If an error occurs finding a renderer.
487    */
findRenderer( RendererCapabilities[] rendererCapabilities, TrackGroup group, int[] rendererTrackGroupCounts, boolean preferUnassociatedRenderer)488   private static int findRenderer(
489       RendererCapabilities[] rendererCapabilities,
490       TrackGroup group,
491       int[] rendererTrackGroupCounts,
492       boolean preferUnassociatedRenderer)
493       throws ExoPlaybackException {
494     int bestRendererIndex = rendererCapabilities.length;
495     @FormatSupport int bestFormatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
496     boolean bestRendererIsUnassociated = true;
497     for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) {
498       RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex];
499       @FormatSupport int formatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
500       for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
501         @FormatSupport
502         int trackFormatSupportLevel =
503             RendererCapabilities.getFormatSupport(
504                 rendererCapability.supportsFormat(group.getFormat(trackIndex)));
505         formatSupportLevel = Math.max(formatSupportLevel, trackFormatSupportLevel);
506       }
507       boolean rendererIsUnassociated = rendererTrackGroupCounts[rendererIndex] == 0;
508       if (formatSupportLevel > bestFormatSupportLevel
509           || (formatSupportLevel == bestFormatSupportLevel
510               && preferUnassociatedRenderer
511               && !bestRendererIsUnassociated
512               && rendererIsUnassociated)) {
513         bestRendererIndex = rendererIndex;
514         bestFormatSupportLevel = formatSupportLevel;
515         bestRendererIsUnassociated = rendererIsUnassociated;
516       }
517     }
518     return bestRendererIndex;
519   }
520 
521   /**
522    * Calls {@link RendererCapabilities#supportsFormat} for each track in the specified {@link
523    * TrackGroup}, returning the results in an array.
524    *
525    * @param rendererCapabilities The {@link RendererCapabilities} of the renderer.
526    * @param group The track group to evaluate.
527    * @return An array containing {@link Capabilities} for each track in the group.
528    * @throws ExoPlaybackException If an error occurs determining the format support.
529    */
530   @Capabilities
getFormatSupport(RendererCapabilities rendererCapabilities, TrackGroup group)531   private static int[] getFormatSupport(RendererCapabilities rendererCapabilities, TrackGroup group)
532       throws ExoPlaybackException {
533     @Capabilities int[] formatSupport = new int[group.length];
534     for (int i = 0; i < group.length; i++) {
535       formatSupport[i] = rendererCapabilities.supportsFormat(group.getFormat(i));
536     }
537     return formatSupport;
538   }
539 
540   /**
541    * Calls {@link RendererCapabilities#supportsMixedMimeTypeAdaptation()} for each renderer,
542    * returning the results in an array.
543    *
544    * @param rendererCapabilities The {@link RendererCapabilities} of the renderers.
545    * @return An array containing the {@link AdaptiveSupport} for mixed MIME type adaptation for the
546    *     renderer.
547    * @throws ExoPlaybackException If an error occurs determining the adaptation support.
548    */
549   @AdaptiveSupport
getMixedMimeTypeAdaptationSupports( RendererCapabilities[] rendererCapabilities)550   private static int[] getMixedMimeTypeAdaptationSupports(
551       RendererCapabilities[] rendererCapabilities) throws ExoPlaybackException {
552     @AdaptiveSupport int[] mixedMimeTypeAdaptationSupport = new int[rendererCapabilities.length];
553     for (int i = 0; i < mixedMimeTypeAdaptationSupport.length; i++) {
554       mixedMimeTypeAdaptationSupport[i] = rendererCapabilities[i].supportsMixedMimeTypeAdaptation();
555     }
556     return mixedMimeTypeAdaptationSupport;
557   }
558 
559 }
560