MediaPlayer2: use TrackInfo based track APIs

Updated APIs include:
* MediaPlayer2.SubtitleData
* MediaPlayer2.selectTrack
* MediaPlayer2.deselectTrack
* MediaPlayer2.getSelectedTrack

Bug: 129098908
Test: android.media.cts.MediaPlayer2Test
Change-Id: Ib1de8b947959bebeae2a79977e055f82cfff67b7
This commit is contained in:
Robert Shih
2019-03-20 14:48:18 -07:00
parent 11e8d568f1
commit 49a7d80e02
2 changed files with 157 additions and 58 deletions

View File

@@ -25552,8 +25552,8 @@ package android.media {
method @NonNull public Object clearNextDataSources();
method public void clearPendingCommands();
method public void close();
method @NonNull public Object deselectTrack(int);
method @NonNull public Object deselectTrack(@NonNull android.media.DataSourceDesc, int);
method @NonNull public Object deselectTrack(@NonNull android.media.MediaPlayer2.TrackInfo);
method @NonNull public Object deselectTrack(@NonNull android.media.DataSourceDesc, @NonNull android.media.MediaPlayer2.TrackInfo);
method @NonNull public android.media.AudioAttributes getAudioAttributes();
method public int getAudioSessionId();
method public long getBufferedPosition();
@@ -25568,8 +25568,8 @@ package android.media {
method public float getPlayerVolume();
method @Nullable public android.media.AudioDeviceInfo getPreferredDevice();
method @Nullable public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSelectedTrack(int);
method public int getSelectedTrack(@NonNull android.media.DataSourceDesc, int);
method @Nullable public android.media.MediaPlayer2.TrackInfo getSelectedTrack(int);
method @Nullable public android.media.MediaPlayer2.TrackInfo getSelectedTrack(@NonNull android.media.DataSourceDesc, int);
method public int getState();
method @NonNull public android.media.SyncParams getSyncParams();
method @Nullable public android.media.MediaTimestamp getTimestamp();
@@ -25587,8 +25587,8 @@ package android.media {
method public void reset();
method @NonNull public Object seekTo(long);
method @NonNull public Object seekTo(long, int);
method @NonNull public Object selectTrack(int);
method @NonNull public Object selectTrack(@NonNull android.media.DataSourceDesc, int);
method @NonNull public Object selectTrack(@NonNull android.media.MediaPlayer2.TrackInfo);
method @NonNull public Object selectTrack(@NonNull android.media.DataSourceDesc, @NonNull android.media.MediaPlayer2.TrackInfo);
method @NonNull public Object setAudioAttributes(@NonNull android.media.AudioAttributes);
method @NonNull public Object setAudioSessionId(int);
method @NonNull public Object setAuxEffectSendLevel(float);
@@ -25715,7 +25715,7 @@ package android.media {
method public void onError(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, int, int);
method public void onInfo(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, int, int);
method public void onMediaTimeDiscontinuity(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.MediaTimestamp);
method public void onSubtitleData(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.SubtitleData);
method public void onSubtitleData(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.MediaPlayer2.SubtitleData);
method public void onTimedMetaDataAvailable(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.media.TimedMetaData);
method public void onVideoSizeChanged(@NonNull android.media.MediaPlayer2, @NonNull android.media.DataSourceDesc, @NonNull android.util.Size);
}
@@ -25739,6 +25739,13 @@ package android.media {
ctor public MediaPlayer2.NoDrmSchemeException(@Nullable String);
}
public static final class MediaPlayer2.SubtitleData {
method @NonNull public byte[] getData();
method public long getDurationUs();
method public long getStartTimeUs();
method @NonNull public android.media.MediaPlayer2.TrackInfo getTrackInfo();
}
public static class MediaPlayer2.TrackInfo {
method @Nullable public android.media.MediaFormat getFormat();
method @NonNull public String getLanguage();

View File

@@ -1962,6 +1962,17 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
private native byte[] native_invoke(byte[] request);
/**
* @hide
*/
@IntDef(flag = false, prefix = "MEDIA_TRACK_TYPE", value = {
TrackInfo.MEDIA_TRACK_TYPE_VIDEO,
TrackInfo.MEDIA_TRACK_TYPE_AUDIO,
TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
})
@Retention(RetentionPolicy.SOURCE)
public @interface TrackType {}
/**
* Class for MediaPlayer2 to return each audio/video/subtitle track's metadata.
*
@@ -2009,10 +2020,11 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
public static final int MEDIA_TRACK_TYPE_METADATA = 5;
final int mId;
final int mTrackType;
final MediaFormat mFormat;
static TrackInfo create(Iterator<Value> in) {
static TrackInfo create(int idx, Iterator<Value> in) {
int trackType = in.next().getInt32Value();
// TODO: build the full MediaFormat; currently we are using createSubtitleFormat
// even for audio/video tracks, meaning we only set the mime and language.
@@ -2025,11 +2037,12 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
format.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value());
format.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value());
}
return new TrackInfo(trackType, format);
return new TrackInfo(idx, trackType, format);
}
/** @hide */
TrackInfo(int type, MediaFormat format) {
TrackInfo(int id, int type, MediaFormat format) {
mId = id;
mTrackType = type;
mFormat = format;
}
@@ -2116,7 +2129,7 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
}
TrackInfo[] trackInfo = new TrackInfo[size];
for (int i = 0; i < size; ++i) {
trackInfo[i] = TrackInfo.create(in);
trackInfo[i] = TrackInfo.create(i, in);
}
return trackInfo;
}
@@ -2124,54 +2137,56 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
/**
* Returns the index of the audio, video, or subtitle track currently selected for playback.
* The return value is an index into the array returned by {@link #getTrackInfo}, and can
* be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
* be used in calls to {@link #selectTrack(TrackInfo)} or {@link #deselectTrack(TrackInfo)}.
* Same as {@link #getSelectedTrack(DataSourceDesc, int)} with
* {@code dsd = getCurrentDataSource()}.
*
* @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
* {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
* {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
* @return index of the audio, video, or subtitle track currently selected for playback;
* a negative integer is returned when there is no selected track for {@code trackType} or
* @return metadata corresponding to the audio, video, or subtitle track currently selected for
* playback; {@code null} is returned when there is no selected track for {@code trackType} or
* when {@code trackType} is not one of audio, video, or subtitle.
* @throws IllegalStateException if called after {@link #close()}
* @throws NullPointerException if current data source is null
*
* @see #getTrackInfo()
* @see #selectTrack(int)
* @see #deselectTrack(int)
* @see #selectTrack(TrackInfo)
* @see #deselectTrack(TrackInfo)
*/
public int getSelectedTrack(int trackType) {
@Nullable
public TrackInfo getSelectedTrack(@TrackType int trackType) {
return getSelectedTrack(getCurrentDataSource(), trackType);
}
/**
* Returns the index of the audio, video, or subtitle track currently selected for playback.
* The return value is an index into the array returned by {@link #getTrackInfo}, and can
* be used in calls to {@link #selectTrack(DataSourceDesc, int)} or
* {@link #deselectTrack(DataSourceDesc, int)}.
* be used in calls to {@link #selectTrack(DataSourceDesc, TrackInfo)} or
* {@link #deselectTrack(DataSourceDesc, TrackInfo)}.
*
* @param dsd the descriptor of data source of which you want to get selected track
* @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
* {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
* {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
* @return index of the audio, video, or subtitle track currently selected for playback;
* a negative integer is returned when there is no selected track for {@code trackType} or
* @return metadata corresponding to the audio, video, or subtitle track currently selected for
* playback; {@code null} is returned when there is no selected track for {@code trackType} or
* when {@code trackType} is not one of audio, video, or subtitle.
* @throws IllegalStateException if called after {@link #close()}
* @throws NullPointerException if dsd is null
*
* @see #getTrackInfo(DataSourceDesc)
* @see #selectTrack(DataSourceDesc, int)
* @see #deselectTrack(DataSourceDesc, int)
* @see #selectTrack(DataSourceDesc, TrackInfo)
* @see #deselectTrack(DataSourceDesc, TrackInfo)
*/
public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) {
@Nullable
public TrackInfo getSelectedTrack(@NonNull DataSourceDesc dsd, @TrackType int trackType) {
if (dsd == null) {
throw new NullPointerException("non-null dsd is expected");
}
SourceInfo sourceInfo = getSourceInfo(dsd);
if (sourceInfo == null) {
return -1;
return null;
}
PlayerMessage request = PlayerMessage.newBuilder()
@@ -2181,26 +2196,30 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
.build();
PlayerMessage response = invoke(request);
if (response == null) {
return -1;
return null;
}
return response.getValues(0).getInt32Value();
// TODO: return full TrackInfo data from native player instead of index
final int idx = response.getValues(0).getInt32Value();
final List<TrackInfo> trackInfos = getTrackInfo(dsd);
return trackInfos.isEmpty() ? null : trackInfos.get(idx);
}
/**
* Selects a track of current data source.
* Same as {@link #selectTrack(DataSourceDesc, int)} with
* Same as {@link #selectTrack(DataSourceDesc, TrackInfo)} with
* {@code dsd = getCurrentDataSource()}.
*
* @param index the index of the track to be selected. The valid range of the index
* is 0..total number of track - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo()} method.
* @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
* object can be obtained from {@link #getTrackInfo()}.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* This is an asynchronous call.
*
* @see MediaPlayer2#getTrackInfo()
*/
// This is an asynchronous call.
public @NonNull Object selectTrack(int index) {
return selectTrack(getCurrentDataSource(), index);
@NonNull
public Object selectTrack(@NonNull TrackInfo trackInfo) {
return selectTrack(getCurrentDataSource(), trackInfo);
}
/**
@@ -2225,38 +2244,40 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
* in that an audio track can only be selected in the <em>Prepared</em> state.
* </p>
* @param dsd the descriptor of data source of which you want to select track
* @param index the index of the track to be selected. The valid range of the index
* is 0..total number of track - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo(DataSourceDesc)} method.
* @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
* object can be obtained from {@link #getTrackInfo()}.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* This is an asynchronous call.
*
* @see MediaPlayer2#getTrackInfo(DataSourceDesc)
*/
// This is an asynchronous call.
public @NonNull Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
@NonNull
public Object selectTrack(@NonNull DataSourceDesc dsd, @NonNull TrackInfo trackInfo) {
return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
@Override
void process() {
selectOrDeselectTrack(dsd, index, true /* select */);
selectOrDeselectTrack(dsd, trackInfo.mId, true /* select */);
}
});
}
/**
* Deselect a track of current data source.
* Same as {@link #deselectTrack(DataSourceDesc, int)} with
* Same as {@link #deselectTrack(DataSourceDesc, TrackInfo)} with
* {@code dsd = getCurrentDataSource()}.
*
* @param index the index of the track to be deselected. The valid range of the index
* is 0..total number of tracks - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo()} method.
* @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
* object can be obtained from {@link #getTrackInfo()}.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* This is an asynchronous call.
*
* @see MediaPlayer2#getTrackInfo()
*/
// This is an asynchronous call.
public @NonNull Object deselectTrack(int index) {
return deselectTrack(getCurrentDataSource(), index);
@NonNull
public Object deselectTrack(@NonNull TrackInfo trackInfo) {
return deselectTrack(getCurrentDataSource(), trackInfo);
}
/**
@@ -2267,19 +2288,20 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
* selected before, it throws an exception.
* </p>
* @param dsd the descriptor of data source of which you want to deselect track
* @param index the index of the track to be deselected. The valid range of the index
* is 0..total number of tracks - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo} method.
* @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
* object can be obtained from {@link #getTrackInfo()}.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* This is an asynchronous call.
*
* @see MediaPlayer2#getTrackInfo(DataSourceDesc)
*/
// This is an asynchronous call.
public @NonNull Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
@NonNull
public Object deselectTrack(@NonNull DataSourceDesc dsd, @NonNull TrackInfo trackInfo) {
return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
@Override
void process() {
selectOrDeselectTrack(dsd, index, false /* select */);
selectOrDeselectTrack(dsd, trackInfo.mId, false /* select */);
}
});
}
@@ -2640,11 +2662,13 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
return;
}
Iterator<Value> in = playerMsg.getValuesList().iterator();
SubtitleData data = new SubtitleData(
in.next().getInt32Value(), // trackIndex
in.next().getInt64Value(), // startTimeUs
in.next().getInt64Value(), // durationUs
in.next().getBytesValue().toByteArray()); // data
final int trackIndex = in.next().getInt32Value();
TrackInfo trackInfo = getTrackInfo(dsd).get(trackIndex);
final long startTimeUs = in.next().getInt64Value();
final long durationTimeUs = in.next().getInt64Value();
final byte[] subData = in.next().getBytesValue().toByteArray();
SubtitleData data = new SubtitleData(trackInfo,
startTimeUs, durationTimeUs, subData);
sendEvent(new EventNotifier() {
@Override
public void notify(EventCallback callback) {
@@ -2764,6 +2788,74 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
}
}
/**
* Class encapsulating subtitle data, as received through the
* {@link EventCallback#onSubtitleData} interface.
* <p>
* A {@link SubtitleData} object includes:
* <ul>
* <li> track metadadta in a {@link TrackInfo} object</li>
* <li> the start time (in microseconds) of the data</li>
* <li> the duration (in microseconds) of the data</li>
* <li> the actual data.</li>
* </ul>
* The data is stored in a byte-array, and is encoded in one of the supported in-band
* subtitle formats. The subtitle encoding is determined by the MIME type of the
* {@link TrackInfo} of the subtitle track, one of
* {@link MediaFormat#MIMETYPE_TEXT_CEA_608}, {@link MediaFormat#MIMETYPE_TEXT_CEA_708},
* {@link MediaFormat#MIMETYPE_TEXT_VTT}.
*/
public static final class SubtitleData {
private TrackInfo mTrackInfo;
private long mStartTimeUs;
private long mDurationUs;
private byte[] mData;
private SubtitleData(TrackInfo trackInfo, long startTimeUs, long durationUs, byte[] data) {
mTrackInfo = trackInfo;
mStartTimeUs = startTimeUs;
mDurationUs = durationUs;
mData = (data != null ? data : new byte[0]);
}
/**
* @return metadata of track which contains this subtitle data
*/
@NonNull
public TrackInfo getTrackInfo() {
return mTrackInfo;
}
/**
* @return media time at which the subtitle should start to be displayed in microseconds
*/
public long getStartTimeUs() {
return mStartTimeUs;
}
/**
* @return the duration in microsecond during which the subtitle should be displayed
*/
public long getDurationUs() {
return mDurationUs;
}
/**
* Returns the encoded data for the subtitle content.
* Encoding format depends on the subtitle type, refer to
* <a href="https://en.wikipedia.org/wiki/CEA-708">CEA 708</a>,
* <a href="https://en.wikipedia.org/wiki/EIA-608">CEA/EIA 608</a> and
* <a href="https://www.w3.org/TR/webvtt1/">WebVTT</a>, defined by the MIME type
* of the subtitle track.
* @return the encoded subtitle data
*/
@NonNull
public byte[] getData() {
return mData;
}
}
/**
* Interface definition for callbacks to be invoked when the player has the corresponding
* events.