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:
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user