mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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.
|
media segment has been established.
|
||||||
* Reduce startup latency for on-demand DASH playbacks by allowing codec
|
* Reduce startup latency for on-demand DASH playbacks by allowing codec
|
||||||
initialization to occur before the sidx box has been loaded.
|
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) ###
|
### 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.MediaSource.MediaPeriodId;
|
||||||
import com.google.android.exoplayer2.source.TrackGroup;
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@ -359,7 +360,11 @@ public abstract class MappingTrackSelector extends TrackSelector {
|
|||||||
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
|
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
|
||||||
TrackGroup group = trackGroups.get(groupIndex);
|
TrackGroup group = trackGroups.get(groupIndex);
|
||||||
// Associate the group to a preferred renderer.
|
// 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.
|
// Evaluate the support that the renderer provides for each track in the group.
|
||||||
@Capabilities
|
@Capabilities
|
||||||
int[] rendererFormatSupport =
|
int[] rendererFormatSupport =
|
||||||
@ -431,43 +436,65 @@ public abstract class MappingTrackSelector extends TrackSelector {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the renderer to which the provided {@link TrackGroup} should be mapped.
|
* 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
|
* <p>A {@link TrackGroup} is mapped to the renderer that reports the highest of (listed in
|
||||||
* decreasing order of support) {@link RendererCapabilities#FORMAT_HANDLED},
|
* decreasing order of support) {@link RendererCapabilities#FORMAT_HANDLED}, {@link
|
||||||
* {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES},
|
* RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}, {@link
|
||||||
* {@link RendererCapabilities#FORMAT_UNSUPPORTED_DRM} and
|
* RendererCapabilities#FORMAT_UNSUPPORTED_DRM} and {@link
|
||||||
* {@link RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE}. In the case that two or more renderers
|
* RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE}.
|
||||||
* report the same level of support, the renderer with the lowest index is associated.
|
*
|
||||||
* <p>
|
* <p>In the case that two or more renderers report the same level of support, the assignment
|
||||||
* If all renderers report {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all of the
|
* 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
|
* tracks in the group, then {@code renderers.length} is returned to indicate that the group was
|
||||||
* not mapped to any renderer.
|
* not mapped to any renderer.
|
||||||
*
|
*
|
||||||
* @param rendererCapabilities The {@link RendererCapabilities} of the renderers.
|
* @param rendererCapabilities The {@link RendererCapabilities} of the renderers.
|
||||||
* @param group The track group to map to a renderer.
|
* @param group The track group to map to a renderer.
|
||||||
* @return The index of the renderer to which the track group was mapped, or
|
* @param rendererTrackGroupCounts The number of already mapped track groups for each renderer.
|
||||||
* {@code renderers.length} if it was not mapped to any 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.
|
* @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 {
|
throws ExoPlaybackException {
|
||||||
int bestRendererIndex = rendererCapabilities.length;
|
int bestRendererIndex = rendererCapabilities.length;
|
||||||
@FormatSupport int bestFormatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
|
@FormatSupport int bestFormatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
|
||||||
|
boolean bestRendererIsUnassociated = true;
|
||||||
for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) {
|
for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) {
|
||||||
RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex];
|
RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex];
|
||||||
|
@FormatSupport int formatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE;
|
||||||
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
|
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
|
||||||
@FormatSupport
|
@FormatSupport
|
||||||
int formatSupportLevel =
|
int trackFormatSupportLevel =
|
||||||
RendererCapabilities.getFormatSupport(
|
RendererCapabilities.getFormatSupport(
|
||||||
rendererCapability.supportsFormat(group.getFormat(trackIndex)));
|
rendererCapability.supportsFormat(group.getFormat(trackIndex)));
|
||||||
if (formatSupportLevel > bestFormatSupportLevel) {
|
formatSupportLevel = Math.max(formatSupportLevel, trackFormatSupportLevel);
|
||||||
bestRendererIndex = rendererIndex;
|
}
|
||||||
bestFormatSupportLevel = formatSupportLevel;
|
boolean rendererIsUnassociated = rendererTrackGroupCounts[rendererIndex] == 0;
|
||||||
if (bestFormatSupportLevel == RendererCapabilities.FORMAT_HANDLED) {
|
if (formatSupportLevel > bestFormatSupportLevel
|
||||||
// We can't do better.
|
|| (formatSupportLevel == bestFormatSupportLevel
|
||||||
return bestRendererIndex;
|
&& preferUnassociatedRenderer
|
||||||
}
|
&& !bestRendererIsUnassociated
|
||||||
}
|
&& rendererIsUnassociated)) {
|
||||||
|
bestRendererIndex = rendererIndex;
|
||||||
|
bestFormatSupportLevel = formatSupportLevel;
|
||||||
|
bestRendererIsUnassociated = rendererIsUnassociated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bestRendererIndex;
|
return bestRendererIndex;
|
||||||
|
@ -44,17 +44,41 @@ public final class MappingTrackSelectorTest {
|
|||||||
new FakeRendererCapabilities(C.TRACK_TYPE_VIDEO);
|
new FakeRendererCapabilities(C.TRACK_TYPE_VIDEO);
|
||||||
private static final RendererCapabilities AUDIO_CAPABILITIES =
|
private static final RendererCapabilities AUDIO_CAPABILITIES =
|
||||||
new FakeRendererCapabilities(C.TRACK_TYPE_AUDIO);
|
new FakeRendererCapabilities(C.TRACK_TYPE_AUDIO);
|
||||||
private static final RendererCapabilities[] RENDERER_CAPABILITIES = new RendererCapabilities[] {
|
private static final RendererCapabilities METADATA_CAPABILITIES =
|
||||||
VIDEO_CAPABILITIES, AUDIO_CAPABILITIES
|
new FakeRendererCapabilities(C.TRACK_TYPE_METADATA);
|
||||||
};
|
|
||||||
private static final TrackGroup VIDEO_TRACK_GROUP = new TrackGroup(
|
private static final TrackGroup VIDEO_TRACK_GROUP =
|
||||||
Format.createVideoSampleFormat("video", MimeTypes.VIDEO_H264, null, Format.NO_VALUE,
|
new TrackGroup(
|
||||||
Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, null));
|
Format.createVideoSampleFormat(
|
||||||
private static final TrackGroup AUDIO_TRACK_GROUP = new TrackGroup(
|
"video",
|
||||||
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
|
MimeTypes.VIDEO_H264,
|
||||||
Format.NO_VALUE, 2, 44100, null, null, 0, null));
|
/* codecs= */ null,
|
||||||
private static final TrackGroupArray TRACK_GROUPS = new TrackGroupArray(
|
/* bitrate= */ Format.NO_VALUE,
|
||||||
VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP);
|
/* 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 final Timeline TIMELINE = new FakeTimeline(/* windowCount= */ 1);
|
||||||
|
|
||||||
private static MediaPeriodId periodId;
|
private static MediaPeriodId periodId;
|
||||||
@ -64,43 +88,68 @@ public final class MappingTrackSelectorTest {
|
|||||||
periodId = new MediaPeriodId(TIMELINE.getUidOfPeriod(/* periodIndex= */ 0));
|
periodId = new MediaPeriodId(TIMELINE.getUidOfPeriod(/* periodIndex= */ 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that the video and audio track groups are mapped onto the correct renderers.
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testMapping() throws ExoPlaybackException {
|
public void selectTracks_audioAndVideo_sameOrderAsRenderers_mappedToCorectRenderer()
|
||||||
|
throws ExoPlaybackException {
|
||||||
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||||
trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS, periodId, TIMELINE);
|
RendererCapabilities[] rendererCapabilities =
|
||||||
trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP);
|
new RendererCapabilities[] {VIDEO_CAPABILITIES, AUDIO_CAPABILITIES};
|
||||||
trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP);
|
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
|
@Test
|
||||||
public void testMappingReverseOrder() throws ExoPlaybackException {
|
public void selectTracks_audioAndVideo_reverseOrderToRenderers_mappedToCorectRenderer()
|
||||||
|
throws ExoPlaybackException {
|
||||||
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||||
RendererCapabilities[] reverseOrderRendererCapabilities = new RendererCapabilities[] {
|
TrackGroupArray trackGroups = new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP);
|
||||||
AUDIO_CAPABILITIES, VIDEO_CAPABILITIES};
|
RendererCapabilities[] reverseOrderRendererCapabilities =
|
||||||
trackSelector.selectTracks(reverseOrderRendererCapabilities, TRACK_GROUPS, periodId, TIMELINE);
|
new RendererCapabilities[] {AUDIO_CAPABILITIES, VIDEO_CAPABILITIES};
|
||||||
trackSelector.assertMappedTrackGroups(0, AUDIO_TRACK_GROUP);
|
|
||||||
trackSelector.assertMappedTrackGroups(1, VIDEO_TRACK_GROUP);
|
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
|
@Test
|
||||||
public void testMappingMulti() throws ExoPlaybackException {
|
public void selectTracks_multipleVideoAndAudioTracks_mappedToSameRenderer()
|
||||||
|
throws ExoPlaybackException {
|
||||||
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector();
|
||||||
TrackGroupArray multiTrackGroups = new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP,
|
TrackGroupArray trackGroups =
|
||||||
VIDEO_TRACK_GROUP);
|
new TrackGroupArray(
|
||||||
trackSelector.selectTracks(RENDERER_CAPABILITIES, multiTrackGroups, periodId, TIMELINE);
|
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(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