Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c86577a authored by Dongwon Kang's avatar Dongwon Kang Committed by Android (Google) Code Review
Browse files

Merge "MediaPlayer2: use TrackInfo based track APIs"

parents e4f1cb24 49a7d80e
Loading
Loading
Loading
Loading
+14 −7
Original line number Diff line number Diff line
@@ -25547,8 +25547,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();
@@ -25563,8 +25563,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();
@@ -25582,8 +25582,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();
+143 −51
Original line number Diff line number Diff line
@@ -1967,6 +1967,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.
     *
@@ -2014,10 +2025,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.
@@ -2030,11 +2042,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;
        }
@@ -2121,7 +2134,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;
    }
@@ -2129,54 +2142,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()
@@ -2186,26 +2201,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);
    }

    /**
@@ -2230,38 +2249,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);
    }

    /**
@@ -2272,19 +2293,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 */);
            }
        });
    }
@@ -2645,11 +2667,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) {
@@ -2769,6 +2793,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.