Merge pull request #6678 from phhusson:feature/enable-multi-metadata-tracks
PiperOrigin-RevId: 290032841
This commit is contained in:
commit
17d1343375
@ -71,6 +71,8 @@
|
||||
media segment has been established.
|
||||
* Reduce startup latency for on-demand DASH playbacks by allowing codec
|
||||
initialization to occur before the sidx box has been loaded.
|
||||
* Select multiple metadata tracks if multiple metadata renderers are available
|
||||
([#6676](https://github.com/google/ExoPlayer/issues/6676)).
|
||||
|
||||
### 2.11.1 (2019-12-20) ###
|
||||
|
||||
|
@ -30,6 +30,7 @@ import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -359,7 +360,11 @@ public abstract class MappingTrackSelector extends TrackSelector {
|
||||
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
|
||||
TrackGroup group = trackGroups.get(groupIndex);
|
||||
// Associate the group to a preferred renderer.
|
||||
int rendererIndex = findRenderer(rendererCapabilities, group);
|
||||
boolean preferUnassociatedRenderer =
|
||||
MimeTypes.getTrackType(group.getFormat(0).sampleMimeType) == C.TRACK_TYPE_METADATA;
|
||||
int rendererIndex =
|
||||
findRenderer(
|
||||
rendererCapabilities, group, rendererTrackGroupCounts, preferUnassociatedRenderer);
|
||||
// Evaluate the support that the renderer provides for each track in the group.
|
||||
@Capabilities
|
||||
int[] rendererFormatSupport =
|
||||
@ -431,43 +436,65 @@ public abstract class MappingTrackSelector extends TrackSelector {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* <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}.
|
||||
*
|
||||
* <p>In the case that two or more renderers report the same level of support, the assignment
|
||||
* depends on {@code preferUnassociatedRenderer}.
|
||||
*
|
||||
* <ul>
|
||||
* <li>If {@code preferUnassociatedRenderer} is false, the renderer with the lowest index is
|
||||
* chosen regardless of how many other track groups are already mapped to this renderer.
|
||||
* <li>If {@code preferUnassociatedRenderer} is true, the renderer with the lowest index and no
|
||||
* other mapped track group is chosen, or the renderer with the lowest index if all
|
||||
* available renderers have already mapped track groups.
|
||||
* </ul>
|
||||
*
|
||||
* <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.
|
||||
* @param rendererTrackGroupCounts The number of already mapped track groups for each renderer.
|
||||
* @param preferUnassociatedRenderer Whether renderers unassociated to any track group should be
|
||||
* preferred.
|
||||
* @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)
|
||||
private static int findRenderer(
|
||||
RendererCapabilities[] rendererCapabilities,
|
||||
TrackGroup group,
|
||||
int[] rendererTrackGroupCounts,
|
||||
boolean preferUnassociatedRenderer)
|
||||
throws ExoPlaybackException {
|
||||
int bestRendererIndex = rendererCapabilities.length;
|
||||
@FormatSupport int bestFormatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
|
||||
boolean bestRendererIsUnassociated = true;
|
||||
for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) {
|
||||
RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex];
|
||||
@FormatSupport int formatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
|
||||
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
|
||||
@FormatSupport
|
||||
int formatSupportLevel =
|
||||
int trackFormatSupportLevel =
|
||||
RendererCapabilities.getFormatSupport(
|
||||
rendererCapability.supportsFormat(group.getFormat(trackIndex)));
|
||||
if (formatSupportLevel > bestFormatSupportLevel) {
|
||||
bestRendererIndex = rendererIndex;
|
||||
bestFormatSupportLevel = formatSupportLevel;
|
||||
if (bestFormatSupportLevel == RendererCapabilities.FORMAT_HANDLED) {
|
||||
// We can't do better.
|
||||
return bestRendererIndex;
|
||||
}
|
||||
}
|
||||
formatSupportLevel = Math.max(formatSupportLevel, trackFormatSupportLevel);
|
||||
}
|
||||
boolean rendererIsUnassociated = rendererTrackGroupCounts[rendererIndex] == 0;
|
||||
if (formatSupportLevel > bestFormatSupportLevel
|
||||
|| (formatSupportLevel == bestFormatSupportLevel
|
||||
&& preferUnassociatedRenderer
|
||||
&& !bestRendererIsUnassociated
|
||||
&& rendererIsUnassociated)) {
|
||||
bestRendererIndex = rendererIndex;
|
||||
bestFormatSupportLevel = formatSupportLevel;
|
||||
bestRendererIsUnassociated = rendererIsUnassociated;
|
||||
}
|
||||
}
|
||||
return bestRendererIndex;
|
||||
|
@ -44,17 +44,41 @@ public final class MappingTrackSelectorTest {
|
||||
new FakeRendererCapabilities(C.TRACK_TYPE_VIDEO);
|
||||
private static final RendererCapabilities AUDIO_CAPABILITIES =
|
||||
new FakeRendererCapabilities(C.TRACK_TYPE_AUDIO);
|
||||
private static final RendererCapabilities[] RENDERER_CAPABILITIES = new RendererCapabilities[] {
|
||||
VIDEO_CAPABILITIES, AUDIO_CAPABILITIES
|
||||
};
|
||||
private static final TrackGroup VIDEO_TRACK_GROUP = new TrackGroup(
|
||||
Format.createVideoSampleFormat("video", MimeTypes.VIDEO_H264, null, Format.NO_VALUE,
|
||||
Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, null));
|
||||
private static final TrackGroup AUDIO_TRACK_GROUP = new TrackGroup(
|
||||
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
|
||||
Format.NO_VALUE, 2, 44100, null, null, 0, null));
|
||||
private static final TrackGroupArray TRACK_GROUPS = new TrackGroupArray(
|
||||
VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP);
|
||||
private static final RendererCapabilities METADATA_CAPABILITIES =
|
||||
new FakeRendererCapabilities(C.TRACK_TYPE_METADATA);
|
||||
|
||||
private static final TrackGroup VIDEO_TRACK_GROUP =
|
||||
new TrackGroup(
|
||||
Format.createVideoSampleFormat(
|
||||
"video",
|
||||
MimeTypes.VIDEO_H264,
|
||||
/* codecs= */ null,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxInputSize= */ Format.NO_VALUE,
|
||||
/* width= */ 1024,
|
||||
/* height= */ 768,
|
||||
/* frameRate= */ Format.NO_VALUE,
|
||||
/* initializationData= */ null,
|
||||
/* drmInitData= */ null));
|
||||
private static final TrackGroup AUDIO_TRACK_GROUP =
|
||||
new TrackGroup(
|
||||
Format.createAudioSampleFormat(
|
||||
"audio",
|
||||
MimeTypes.AUDIO_AAC,
|
||||
/* codecs= */ null,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxInputSize= */ Format.NO_VALUE,
|
||||
/* channelCount= */ 2,
|
||||
/* sampleRate= */ 44100,
|
||||
/* initializationData= */ null,
|
||||
/* drmInitData= */ null,
|
||||
/* selectionFlags= */ 0,
|
||||
/* language= */ null));
|
||||
private static final TrackGroup METADATA_TRACK_GROUP =
|
||||
new TrackGroup(
|
||||
Format.createSampleFormat(
|
||||
"metadata", MimeTypes.APPLICATION_ID3, /* subsampleOffsetUs= */ 0));
|
||||
|
||||
private static final Timeline TIMELINE = new FakeTimeline(/* windowCount= */ 1);
|
||||
|
||||
private static MediaPeriodId periodId;
|
||||
@ -64,43 +88,68 @@ public final class MappingTrackSelectorTest {
|
||||
periodId = new MediaPeriodId(TIMELINE.getUidOfPeriod(/* periodIndex= */ 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the video and audio track groups are mapped onto the correct renderers.
|
||||
*/
|
||||
@Test
|
||||
public void testMapping() throws ExoPlaybackException {
|
||||
public void selectTracks_audioAndVideo_sameOrderAsRenderers_mappedToCorectRenderer()
|
||||
throws ExoPlaybackException {
|
||||
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||
trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS, periodId, TIMELINE);
|
||||
trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP);
|
||||
RendererCapabilities[] rendererCapabilities =
|
||||
new RendererCapabilities[] {VIDEO_CAPABILITIES, AUDIO_CAPABILITIES};
|
||||
TrackGroupArray trackGroups = new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP);
|
||||
|
||||
trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
|
||||
|
||||
trackSelector.assertMappedTrackGroups(/* rendererIndex= */ 0, VIDEO_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(/* rendererIndex= */ 1, AUDIO_TRACK_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the video and audio track groups are mapped onto the correct renderers when the
|
||||
* renderer ordering is reversed.
|
||||
*/
|
||||
@Test
|
||||
public void testMappingReverseOrder() throws ExoPlaybackException {
|
||||
public void selectTracks_audioAndVideo_reverseOrderToRenderers_mappedToCorectRenderer()
|
||||
throws ExoPlaybackException {
|
||||
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||
RendererCapabilities[] reverseOrderRendererCapabilities = new RendererCapabilities[] {
|
||||
AUDIO_CAPABILITIES, VIDEO_CAPABILITIES};
|
||||
trackSelector.selectTracks(reverseOrderRendererCapabilities, TRACK_GROUPS, periodId, TIMELINE);
|
||||
trackSelector.assertMappedTrackGroups(0, AUDIO_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(1, VIDEO_TRACK_GROUP);
|
||||
TrackGroupArray trackGroups = new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP);
|
||||
RendererCapabilities[] reverseOrderRendererCapabilities =
|
||||
new RendererCapabilities[] {AUDIO_CAPABILITIES, VIDEO_CAPABILITIES};
|
||||
|
||||
trackSelector.selectTracks(reverseOrderRendererCapabilities, trackGroups, periodId, TIMELINE);
|
||||
|
||||
trackSelector.assertMappedTrackGroups(/* rendererIndex= */ 0, AUDIO_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(/* rendererIndex= */ 1, VIDEO_TRACK_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests video and audio track groups are mapped onto the correct renderers when there are
|
||||
* multiple track groups of the same type.
|
||||
*/
|
||||
@Test
|
||||
public void testMappingMulti() throws ExoPlaybackException {
|
||||
public void selectTracks_multipleVideoAndAudioTracks_mappedToSameRenderer()
|
||||
throws ExoPlaybackException {
|
||||
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||
TrackGroupArray multiTrackGroups = new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP,
|
||||
VIDEO_TRACK_GROUP);
|
||||
trackSelector.selectTracks(RENDERER_CAPABILITIES, multiTrackGroups, periodId, TIMELINE);
|
||||
TrackGroupArray trackGroups =
|
||||
new TrackGroupArray(
|
||||
VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP, AUDIO_TRACK_GROUP, VIDEO_TRACK_GROUP);
|
||||
RendererCapabilities[] rendererCapabilities =
|
||||
new RendererCapabilities[] {
|
||||
VIDEO_CAPABILITIES, AUDIO_CAPABILITIES, VIDEO_CAPABILITIES, AUDIO_CAPABILITIES
|
||||
};
|
||||
|
||||
trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
|
||||
|
||||
trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP, VIDEO_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP, AUDIO_TRACK_GROUP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectTracks_multipleMetadataTracks_mappedToDifferentRenderers()
|
||||
throws ExoPlaybackException {
|
||||
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||
TrackGroupArray trackGroups =
|
||||
new TrackGroupArray(VIDEO_TRACK_GROUP, METADATA_TRACK_GROUP, METADATA_TRACK_GROUP);
|
||||
RendererCapabilities[] rendererCapabilities =
|
||||
new RendererCapabilities[] {
|
||||
VIDEO_CAPABILITIES, METADATA_CAPABILITIES, METADATA_CAPABILITIES
|
||||
};
|
||||
|
||||
trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
|
||||
|
||||
trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(1, METADATA_TRACK_GROUP);
|
||||
trackSelector.assertMappedTrackGroups(2, METADATA_TRACK_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user